Browse Source

Merge branch 'love2d:12.0-development' into 12.0-development

nikeinikei 2 years ago
parent
commit
eec94ccc99
51 changed files with 1331 additions and 519 deletions
  1. 30 5
      CMakeLists.txt
  2. 9 0
      changes.txt
  3. 171 0
      license.txt
  4. 1 1
      platform/unix/deps.m4
  5. 66 20
      platform/xcode/liblove.xcodeproj/project.pbxproj
  6. 1 0
      src/common/Module.h
  7. 1 0
      src/common/config.h
  8. 13 8
      src/modules/audio/openal/Source.cpp
  9. 60 22
      src/modules/event/sdl/Event.cpp
  10. 3 3
      src/modules/graphics/Graphics.cpp
  11. 2 2
      src/modules/graphics/Graphics.h
  12. 8 3
      src/modules/graphics/Mesh.cpp
  13. 2 1
      src/modules/graphics/Mesh.h
  14. 17 17
      src/modules/graphics/TextBatch.cpp
  15. 3 3
      src/modules/graphics/TextBatch.h
  16. 19 19
      src/modules/graphics/vulkan/Graphics.cpp
  17. 13 6
      src/modules/graphics/wrap_Graphics.cpp
  18. 1 1
      src/modules/graphics/wrap_Graphics.h
  19. 6 2
      src/modules/graphics/wrap_Mesh.cpp
  20. 36 36
      src/modules/graphics/wrap_TextBatch.cpp
  21. 3 3
      src/modules/graphics/wrap_TextBatch.h
  22. 1 1
      src/modules/graphics/wrap_Texture.cpp
  23. 8 0
      src/modules/joystick/Joystick.h
  24. 84 6
      src/modules/joystick/sdl/Joystick.cpp
  25. 5 0
      src/modules/joystick/sdl/Joystick.h
  26. 80 0
      src/modules/joystick/wrap_Joystick.cpp
  27. 3 1
      src/modules/love/boot.lua
  28. 8 1
      src/modules/love/callbacks.lua
  29. 10 0
      src/modules/love/love.cpp
  30. 0 28
      src/modules/physics/box2d/DistanceJoint.cpp
  31. 0 20
      src/modules/physics/box2d/DistanceJoint.h
  32. 1 34
      src/modules/physics/box2d/MouseJoint.cpp
  33. 12 2
      src/modules/physics/box2d/Physics.cpp
  34. 26 4
      src/modules/physics/box2d/Physics.h
  35. 0 28
      src/modules/physics/box2d/WeldJoint.cpp
  36. 0 20
      src/modules/physics/box2d/WeldJoint.h
  37. 0 28
      src/modules/physics/box2d/WheelJoint.cpp
  38. 0 20
      src/modules/physics/box2d/WheelJoint.h
  39. 0 34
      src/modules/physics/box2d/wrap_DistanceJoint.cpp
  40. 0 34
      src/modules/physics/box2d/wrap_MouseJoint.cpp
  41. 89 0
      src/modules/physics/box2d/wrap_Physics.cpp
  42. 0 34
      src/modules/physics/box2d/wrap_WeldJoint.cpp
  43. 0 38
      src/modules/physics/box2d/wrap_WheelJoint.cpp
  44. 37 0
      src/modules/sensor/Sensor.cpp
  45. 83 0
      src/modules/sensor/Sensor.h
  46. 174 0
      src/modules/sensor/sdl/Sensor.cpp
  47. 68 0
      src/modules/sensor/sdl/Sensor.h
  48. 120 0
      src/modules/sensor/wrap_Sensor.cpp
  49. 37 0
      src/modules/sensor/wrap_Sensor.h
  50. 20 33
      src/modules/window/sdl/Window.cpp
  51. 0 1
      src/modules/window/sdl/Window.h

+ 30 - 5
CMakeLists.txt

@@ -172,7 +172,7 @@ Please see https://github.com/love2d/megasource
 	find_package(ModPlug REQUIRED)
 	find_package(ModPlug REQUIRED)
 	find_package(OpenAL REQUIRED)
 	find_package(OpenAL REQUIRED)
 	find_package(OpenGL REQUIRED)
 	find_package(OpenGL REQUIRED)
-	find_package(SDL2 REQUIRED)
+	find_package(SDL2 2.0.9 REQUIRED)
 	find_package(Theora REQUIRED)
 	find_package(Theora REQUIRED)
 	find_package(Vorbis REQUIRED)
 	find_package(Vorbis REQUIRED)
 	find_package(ZLIB REQUIRED)
 	find_package(ZLIB REQUIRED)
@@ -540,8 +540,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/SpriteBatch.h
 	src/modules/graphics/SpriteBatch.h
 	src/modules/graphics/StreamBuffer.cpp
 	src/modules/graphics/StreamBuffer.cpp
 	src/modules/graphics/StreamBuffer.h
 	src/modules/graphics/StreamBuffer.h
-	src/modules/graphics/Text.cpp
-	src/modules/graphics/Text.h
+	src/modules/graphics/TextBatch.cpp
+	src/modules/graphics/TextBatch.h
 	src/modules/graphics/Texture.cpp
 	src/modules/graphics/Texture.cpp
 	src/modules/graphics/Texture.h
 	src/modules/graphics/Texture.h
 	src/modules/graphics/vertex.cpp
 	src/modules/graphics/vertex.cpp
@@ -570,8 +570,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/wrap_SpriteBatch.h
 	src/modules/graphics/wrap_SpriteBatch.h
 	src/modules/graphics/wrap_Texture.cpp
 	src/modules/graphics/wrap_Texture.cpp
 	src/modules/graphics/wrap_Texture.h
 	src/modules/graphics/wrap_Texture.h
-	src/modules/graphics/wrap_Text.cpp
-	src/modules/graphics/wrap_Text.h
+	src/modules/graphics/wrap_TextBatch.cpp
+	src/modules/graphics/wrap_TextBatch.h
 	src/modules/graphics/wrap_Video.cpp
 	src/modules/graphics/wrap_Video.cpp
 	src/modules/graphics/wrap_Video.h
 	src/modules/graphics/wrap_Video.h
 )
 )
@@ -907,6 +907,30 @@ set(LOVE_SRC_MODULE_PHYSICS
 source_group("modules\\physics" FILES ${LOVE_SRC_MODULE_PHYSICS_ROOT})
 source_group("modules\\physics" FILES ${LOVE_SRC_MODULE_PHYSICS_ROOT})
 source_group("modules\\physics\\box2d" FILES ${LOVE_SRC_MODULE_PHYSICS_BOX2D})
 source_group("modules\\physics\\box2d" FILES ${LOVE_SRC_MODULE_PHYSICS_BOX2D})
 
 
+#
+# love.sensor
+#
+
+set(LOVE_SRC_MODULE_SENSOR_ROOT
+	src/modules/sensor/Sensor.cpp
+	src/modules/sensor/Sensor.h
+	src/modules/sensor/wrap_Sensor.cpp
+	src/modules/sensor/wrap_Sensor.h
+)
+
+set(LOVE_SRC_MODULE_SENSOR_SDL
+	src/modules/sensor/sdl/Sensor.cpp
+	src/modules/sensor/sdl/Sensor.h
+)
+
+set(LOVE_SRC_MODULE_SENSOR
+	${LOVE_SRC_MODULE_SENSOR_ROOT}
+	${LOVE_SRC_MODULE_SENSOR_SDL}
+)
+
+source_group("modules\\sensor" FILES ${LOVE_SRC_MODULE_SENSOR_ROOT})
+source_group("modules\\sensor\\sdl" FILES ${LOVE_SRC_MODULE_SENSOR_SDL})
+
 #
 #
 # love.sound
 # love.sound
 #
 #
@@ -1881,6 +1905,7 @@ set(LOVE_LIB_SRC
 	${LOVE_SRC_MODULE_MATH}
 	${LOVE_SRC_MODULE_MATH}
 	${LOVE_SRC_MODULE_MOUSE}
 	${LOVE_SRC_MODULE_MOUSE}
 	${LOVE_SRC_MODULE_PHYSICS}
 	${LOVE_SRC_MODULE_PHYSICS}
+	${LOVE_SRC_MODULE_SENSOR}
 	${LOVE_SRC_MODULE_SOUND}
 	${LOVE_SRC_MODULE_SOUND}
 	${LOVE_SRC_MODULE_SYSTEM}
 	${LOVE_SRC_MODULE_SYSTEM}
 	${LOVE_SRC_MODULE_THREAD}
 	${LOVE_SRC_MODULE_THREAD}

+ 9 - 0
changes.txt

@@ -25,6 +25,10 @@ Released: N/A
 * Added love.keyboard.isModifierActive.
 * Added love.keyboard.isModifierActive.
 * Added Joystick:setPlayerIndex and Joystick:getPlayerIndex.
 * Added Joystick:setPlayerIndex and Joystick:getPlayerIndex.
 * Added Joystick:getGamepadType.
 * Added Joystick:getGamepadType.
+* Added Joystick:hasSensor.
+* Added Joystick:isSensorEnabled.
+* Added Joystick:setSensorEnabled.
+* Added Joystick:getSensorData.
 * Added new Gamepad API buttons: "misc1", "paddle1", "paddle2", "paddle3", "paddle4". and "touchpad".
 * Added new Gamepad API buttons: "misc1", "paddle1", "paddle2", "paddle3", "paddle4". and "touchpad".
 * Added World:getFixturesInArea().
 * Added World:getFixturesInArea().
 * Added support for saving .exr image files via ImageData:encode.
 * Added support for saving .exr image files via ImageData:encode.
@@ -61,6 +65,9 @@ Released: N/A
 * Added a variant of love.graphics.setColorMask which accepts a single boolean.
 * Added a variant of love.graphics.setColorMask which accepts a single boolean.
 * Added new 'clampone' wrap mode.
 * Added new 'clampone' wrap mode.
 * Added a variant of Font:getWidth which takes a codepoint number argument.
 * Added a variant of Font:getWidth which takes a codepoint number argument.
+* Added love.sensor module.
+* Added love.sensorupdated callback.
+* Added love.joysticksensorupdated callback.
 
 
 * Changed the default font from Vera size 12 to Noto Sans size 13.
 * Changed the default font from Vera size 12 to Noto Sans size 13.
 * Changed the Texture class and implementation to no longer have separate Canvas and Image subclasses.
 * Changed the Texture class and implementation to no longer have separate Canvas and Image subclasses.
@@ -75,6 +82,7 @@ Released: N/A
 * Changed love.filesystem.exists to no longer be deprecated.
 * Changed love.filesystem.exists to no longer be deprecated.
 * Changed RevoluteJoint:getMotorTorque to take 'dt' as a parameter instead of 'inverse_dt'.
 * Changed RevoluteJoint:getMotorTorque to take 'dt' as a parameter instead of 'inverse_dt'.
 * Changed love.math.perlinNoise and simplexNoise to use higher precision numbers for its internal calculations.
 * Changed love.math.perlinNoise and simplexNoise to use higher precision numbers for its internal calculations.
+* Changed t.accelerometerjoystick startup flag in love.conf to unset by default.
 
 
 * Renamed 'display' field to 'displayindex' in love.window.setMode/updateMode/getMode and love.conf.
 * Renamed 'display' field to 'displayindex' in love.window.setMode/updateMode/getMode and love.conf.
 
 
@@ -86,6 +94,7 @@ Released: N/A
 * Deprecated love.math.noise (replaced by perlinNoise and simplexNoise).
 * Deprecated love.math.noise (replaced by perlinNoise and simplexNoise).
 * Deprecated love.graphics.getImageFormats and love.graphics.getCanvasFormats (replaced by getTextureFormats).
 * Deprecated love.graphics.getImageFormats and love.graphics.getCanvasFormats (replaced by getTextureFormats).
 * Deprecated t.window.highdpi in love.conf and the highdpi flag of love.window.setMode (replaced by t.highdpi in love.conf).
 * Deprecated t.window.highdpi in love.conf and the highdpi flag of love.window.setMode (replaced by t.highdpi in love.conf).
+* Deprecated t.accelerometerjoystick in love.conf (replaced by love.sensor module).
 * Deprecated the variants of Mesh:attachAttribute and SpriteBatch:attachAttribute which accept a Mesh (replaced by variants which accept a Buffer).
 * Deprecated the variants of Mesh:attachAttribute and SpriteBatch:attachAttribute which accept a Mesh (replaced by variants which accept a Buffer).
 * Deprecated Texture:newImageData (replaced by love.graphics.readbackTexture).
 * Deprecated Texture:newImageData (replaced by love.graphics.readbackTexture).
 
 

+ 171 - 0
license.txt

@@ -13,6 +13,11 @@ This distribution contains code from the following projects (full license text b
 	License: MIT/Expat
 	License: MIT/Expat
 	Copyright (c) 2002-2016 Lee Salzman
 	Copyright (c) 2002-2016 Lee Salzman
 
 
+ - FreeType
+	Website: https://freetype.org/
+	License: FreeType License
+	Copyright (c) 2006-2017 David Turner, Robert Wilhelm, and Werner Lemberg.
+
  - GLAD
  - GLAD
 	Website: http://glad.dav1d.de/
 	Website: http://glad.dav1d.de/
 	License: MIT/Expat
 	License: MIT/Expat
@@ -199,6 +204,172 @@ MIT/Expat
 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 	THE SOFTWARE.
 	THE SOFTWARE.
 
 
+FreeType License
+	                    The FreeType Project LICENSE
+	                    ----------------------------
+
+	                            2006-Jan-27
+
+	                    Copyright 1996-2002, 2006 by
+	          David Turner, Robert Wilhelm, and Werner Lemberg
+
+	Introduction
+	============
+
+	  The FreeType  Project is distributed in  several archive packages;
+	  some of them may contain, in addition to the FreeType font engine,
+	  various tools and  contributions which rely on, or  relate to, the
+	  FreeType Project.
+
+	  This  license applies  to all  files found  in such  packages, and
+	  which do not  fall under their own explicit  license.  The license
+	  affects  thus  the  FreeType   font  engine,  the  test  programs,
+	  documentation and makefiles, at the very least.
+
+	  This  license   was  inspired  by  the  BSD,   Artistic,  and  IJG
+	  (Independent JPEG  Group) licenses, which  all encourage inclusion
+	  and  use of  free  software in  commercial  and freeware  products
+	  alike.  As a consequence, its main points are that:
+
+	    o We don't promise that this software works. However, we will be
+	      interested in any kind of bug reports. (`as is' distribution)
+
+	    o You can  use this software for whatever you  want, in parts or
+	      full form, without having to pay us. (`royalty-free' usage)
+
+	    o You may not pretend that  you wrote this software.  If you use
+	      it, or  only parts of it,  in a program,  you must acknowledge
+	      somewhere  in  your  documentation  that  you  have  used  the
+	      FreeType code. (`credits')
+
+	  We  specifically  permit  and  encourage  the  inclusion  of  this
+	  software, with  or without modifications,  in commercial products.
+	  We  disclaim  all warranties  covering  The  FreeType Project  and
+	  assume no liability related to The FreeType Project.
+
+
+	  Finally,  many  people  asked  us  for  a  preferred  form  for  a
+	  credit/disclaimer to use in compliance with this license.  We thus
+	  encourage you to use the following text:
+
+	   """
+	    Portions of this software are copyright © <year> The FreeType
+	    Project (www.freetype.org).  All rights reserved.
+	   """
+
+	  Please replace <year> with the value from the FreeType version you
+	  actually use.
+
+
+	Legal Terms
+	===========
+
+	0. Definitions
+	--------------
+
+	  Throughout this license,  the terms `package', `FreeType Project',
+	  and  `FreeType  archive' refer  to  the  set  of files  originally
+	  distributed  by the  authors  (David Turner,  Robert Wilhelm,  and
+	  Werner Lemberg) as the `FreeType Project', be they named as alpha,
+	  beta or final release.
+
+	  `You' refers to  the licensee, or person using  the project, where
+	  `using' is a generic term including compiling the project's source
+	  code as  well as linking it  to form a  `program' or `executable'.
+	  This  program is  referred to  as  `a program  using the  FreeType
+	  engine'.
+
+	  This  license applies  to all  files distributed  in  the original
+	  FreeType  Project,   including  all  source   code,  binaries  and
+	  documentation,  unless  otherwise  stated   in  the  file  in  its
+	  original, unmodified form as  distributed in the original archive.
+	  If you are  unsure whether or not a particular  file is covered by
+	  this license, you must contact us to verify this.
+
+	  The FreeType  Project is copyright (C) 1996-2000  by David Turner,
+	  Robert Wilhelm, and Werner Lemberg.  All rights reserved except as
+	  specified below.
+
+	1. No Warranty
+	--------------
+
+	  THE FREETYPE PROJECT  IS PROVIDED `AS IS' WITHOUT  WARRANTY OF ANY
+	  KIND, EITHER  EXPRESS OR IMPLIED,  INCLUDING, BUT NOT  LIMITED TO,
+	  WARRANTIES  OF  MERCHANTABILITY   AND  FITNESS  FOR  A  PARTICULAR
+	  PURPOSE.  IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
+	  BE LIABLE  FOR ANY DAMAGES CAUSED  BY THE USE OR  THE INABILITY TO
+	  USE, OF THE FREETYPE PROJECT.
+
+	2. Redistribution
+	-----------------
+
+	  This  license  grants  a  worldwide, royalty-free,  perpetual  and
+	  irrevocable right  and license to use,  execute, perform, compile,
+	  display,  copy,   create  derivative  works   of,  distribute  and
+	  sublicense the  FreeType Project (in  both source and  object code
+	  forms)  and  derivative works  thereof  for  any  purpose; and  to
+	  authorize others  to exercise  some or all  of the  rights granted
+	  herein, subject to the following conditions:
+
+	    o Redistribution of  source code  must retain this  license file
+	      (`FTL.TXT') unaltered; any  additions, deletions or changes to
+	      the original  files must be clearly  indicated in accompanying
+	      documentation.   The  copyright   notices  of  the  unaltered,
+	      original  files must  be  preserved in  all  copies of  source
+	      files.
+
+	    o Redistribution in binary form must provide a  disclaimer  that
+	      states  that  the software is based in part of the work of the
+	      FreeType Team,  in  the  distribution  documentation.  We also
+	      encourage you to put an URL to the FreeType web page  in  your
+	      documentation, though this isn't mandatory.
+
+	  These conditions  apply to any  software derived from or  based on
+	  the FreeType Project,  not just the unmodified files.   If you use
+	  our work, you  must acknowledge us.  However, no  fee need be paid
+	  to us.
+
+	3. Advertising
+	--------------
+
+	  Neither the  FreeType authors and  contributors nor you  shall use
+	  the name of the  other for commercial, advertising, or promotional
+	  purposes without specific prior written permission.
+
+	  We suggest,  but do not require, that  you use one or  more of the
+	  following phrases to refer  to this software in your documentation
+	  or advertising  materials: `FreeType Project',  `FreeType Engine',
+	  `FreeType library', or `FreeType Distribution'.
+
+	  As  you have  not signed  this license,  you are  not  required to
+	  accept  it.   However,  as  the FreeType  Project  is  copyrighted
+	  material, only  this license, or  another one contracted  with the
+	  authors, grants you  the right to use, distribute,  and modify it.
+	  Therefore,  by  using,  distributing,  or modifying  the  FreeType
+	  Project, you indicate that you understand and accept all the terms
+	  of this license.
+
+	4. Contacts
+	-----------
+
+	  There are two mailing lists related to FreeType:
+
+	    o [email protected]
+
+	      Discusses general use and applications of FreeType, as well as
+	      future and  wanted additions to the  library and distribution.
+	      If  you are looking  for support,  start in  this list  if you
+	      haven't found anything to help you in the documentation.
+
+	    o [email protected]
+
+	      Discusses bugs,  as well  as engine internals,  design issues,
+	      specific licenses, porting, etc.
+
+	  Our home page can be found at
+
+	    http://www.freetype.org
+
 3-Clause BSD
 3-Clause BSD
 	All rights reserved.
 	All rights reserved.
 
 

+ 1 - 1
platform/unix/deps.m4

@@ -21,7 +21,7 @@ AC_DEFUN([ACLOVE_DEP_LIBM], [
 
 
 AC_DEFUN([ACLOVE_DEP_SDL2], [
 AC_DEFUN([ACLOVE_DEP_SDL2], [
 	aclove_sdl2_found=no
 	aclove_sdl2_found=no
-	AM_PATH_SDL2([], [aclove_sdl2_found=yes], [])
+	AM_PATH_SDL2([2.0.9], [aclove_sdl2_found=yes], [])
 	AS_VAR_IF([aclove_sdl2_found], [no], [LOVE_MSG_ERROR([SDL 2])], [])])
 	AS_VAR_IF([aclove_sdl2_found], [no], [LOVE_MSG_ERROR([SDL 2])], [])])
 
 
 AC_DEFUN([ACLOVE_DEP_PTHREAD], [
 AC_DEFUN([ACLOVE_DEP_PTHREAD], [

+ 66 - 20
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -1114,6 +1114,15 @@
 		FACA02FB1F5E397E0084B28F /* HashFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA02E61F5E396B0084B28F /* HashFunction.cpp */; };
 		FACA02FB1F5E397E0084B28F /* HashFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA02E61F5E396B0084B28F /* HashFunction.cpp */; };
 		FACA02FC1F5E39810084B28F /* wrap_CompressedData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA02E81F5E396B0084B28F /* wrap_CompressedData.cpp */; };
 		FACA02FC1F5E39810084B28F /* wrap_CompressedData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA02E81F5E396B0084B28F /* wrap_CompressedData.cpp */; };
 		FACA02FD1F5E39840084B28F /* wrap_DataModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA02EA1F5E396B0084B28F /* wrap_DataModule.cpp */; };
 		FACA02FD1F5E39840084B28F /* wrap_DataModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA02EA1F5E396B0084B28F /* wrap_DataModule.cpp */; };
+		FACA06AC293EE5CD001A2557 /* wrap_Sensor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA06A5293EE5CD001A2557 /* wrap_Sensor.cpp */; };
+		FACA06AD293EE5CD001A2557 /* wrap_Sensor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA06A5293EE5CD001A2557 /* wrap_Sensor.cpp */; };
+		FACA06AE293EE5CD001A2557 /* Sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = FACA06A6293EE5CD001A2557 /* Sensor.h */; };
+		FACA06AF293EE5CD001A2557 /* Sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = FACA06A8293EE5CD001A2557 /* Sensor.h */; };
+		FACA06B0293EE5CD001A2557 /* Sensor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA06A9293EE5CD001A2557 /* Sensor.cpp */; };
+		FACA06B1293EE5CD001A2557 /* Sensor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA06A9293EE5CD001A2557 /* Sensor.cpp */; };
+		FACA06B2293EE5CD001A2557 /* Sensor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA06AA293EE5CD001A2557 /* Sensor.cpp */; };
+		FACA06B3293EE5CD001A2557 /* Sensor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FACA06AA293EE5CD001A2557 /* Sensor.cpp */; };
+		FACA06B4293EE5CD001A2557 /* wrap_Sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = FACA06AB293EE5CD001A2557 /* wrap_Sensor.h */; };
 		FACFB751276D7E3B0089F78D /* freetype.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FACFB750276D7E2B0089F78D /* freetype.xcframework */; };
 		FACFB751276D7E3B0089F78D /* freetype.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FACFB750276D7E2B0089F78D /* freetype.xcframework */; };
 		FACFB753276D7F860089F78D /* Lua.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FACFB752276D7F6F0089F78D /* Lua.xcframework */; };
 		FACFB753276D7F860089F78D /* Lua.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FACFB752276D7F6F0089F78D /* Lua.xcframework */; };
 		FAD19A171DFF8CA200D5398A /* ImageDataBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAD19A151DFF8CA200D5398A /* ImageDataBase.cpp */; };
 		FAD19A171DFF8CA200D5398A /* ImageDataBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAD19A151DFF8CA200D5398A /* ImageDataBase.cpp */; };
@@ -1124,12 +1133,12 @@
 		FADF53F81E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
 		FADF53F81E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
 		FADF53F91E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
 		FADF53F91E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
 		FADF53FA1E3C7ACD00012CC0 /* Buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF53F71E3C7ACD00012CC0 /* Buffer.h */; };
 		FADF53FA1E3C7ACD00012CC0 /* Buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF53F71E3C7ACD00012CC0 /* Buffer.h */; };
-		FADF53FD1E3D74F200012CC0 /* Text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53FB1E3D74F200012CC0 /* Text.cpp */; };
-		FADF53FE1E3D74F200012CC0 /* Text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53FB1E3D74F200012CC0 /* Text.cpp */; };
-		FADF53FF1E3D74F200012CC0 /* Text.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF53FC1E3D74F200012CC0 /* Text.h */; };
-		FADF54021E3D77B500012CC0 /* wrap_Text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54001E3D77B500012CC0 /* wrap_Text.cpp */; };
-		FADF54031E3D77B500012CC0 /* wrap_Text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54001E3D77B500012CC0 /* wrap_Text.cpp */; };
-		FADF54041E3D77B500012CC0 /* wrap_Text.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF54011E3D77B500012CC0 /* wrap_Text.h */; };
+		FADF53FD1E3D74F200012CC0 /* TextBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53FB1E3D74F200012CC0 /* TextBatch.cpp */; };
+		FADF53FE1E3D74F200012CC0 /* TextBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53FB1E3D74F200012CC0 /* TextBatch.cpp */; };
+		FADF53FF1E3D74F200012CC0 /* TextBatch.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF53FC1E3D74F200012CC0 /* TextBatch.h */; };
+		FADF54021E3D77B500012CC0 /* wrap_TextBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54001E3D77B500012CC0 /* wrap_TextBatch.cpp */; };
+		FADF54031E3D77B500012CC0 /* wrap_TextBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54001E3D77B500012CC0 /* wrap_TextBatch.cpp */; };
+		FADF54041E3D77B500012CC0 /* wrap_TextBatch.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF54011E3D77B500012CC0 /* wrap_TextBatch.h */; };
 		FADF54071E3D78F700012CC0 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54051E3D78F700012CC0 /* Video.cpp */; };
 		FADF54071E3D78F700012CC0 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54051E3D78F700012CC0 /* Video.cpp */; };
 		FADF54081E3D78F700012CC0 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54051E3D78F700012CC0 /* Video.cpp */; };
 		FADF54081E3D78F700012CC0 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54051E3D78F700012CC0 /* Video.cpp */; };
 		FADF54091E3D78F700012CC0 /* Video.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF54061E3D78F700012CC0 /* Video.h */; };
 		FADF54091E3D78F700012CC0 /* Video.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF54061E3D78F700012CC0 /* Video.h */; };
@@ -2138,6 +2147,12 @@
 		FACA02E91F5E396B0084B28F /* wrap_CompressedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_CompressedData.h; sourceTree = "<group>"; };
 		FACA02E91F5E396B0084B28F /* wrap_CompressedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_CompressedData.h; sourceTree = "<group>"; };
 		FACA02EA1F5E396B0084B28F /* wrap_DataModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_DataModule.cpp; sourceTree = "<group>"; };
 		FACA02EA1F5E396B0084B28F /* wrap_DataModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_DataModule.cpp; sourceTree = "<group>"; };
 		FACA02EB1F5E396B0084B28F /* wrap_DataModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_DataModule.h; sourceTree = "<group>"; };
 		FACA02EB1F5E396B0084B28F /* wrap_DataModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_DataModule.h; sourceTree = "<group>"; };
+		FACA06A5293EE5CD001A2557 /* wrap_Sensor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Sensor.cpp; sourceTree = "<group>"; };
+		FACA06A6293EE5CD001A2557 /* Sensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sensor.h; sourceTree = "<group>"; };
+		FACA06A8293EE5CD001A2557 /* Sensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sensor.h; sourceTree = "<group>"; };
+		FACA06A9293EE5CD001A2557 /* Sensor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sensor.cpp; sourceTree = "<group>"; };
+		FACA06AA293EE5CD001A2557 /* Sensor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sensor.cpp; sourceTree = "<group>"; };
+		FACA06AB293EE5CD001A2557 /* wrap_Sensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Sensor.h; sourceTree = "<group>"; };
 		FACFB750276D7E2B0089F78D /* freetype.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = freetype.xcframework; path = ios/libraries/freetype.xcframework; sourceTree = "<group>"; };
 		FACFB750276D7E2B0089F78D /* freetype.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = freetype.xcframework; path = ios/libraries/freetype.xcframework; sourceTree = "<group>"; };
 		FACFB752276D7F6F0089F78D /* Lua.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Lua.xcframework; path = ios/libraries/Lua.xcframework; sourceTree = "<group>"; };
 		FACFB752276D7F6F0089F78D /* Lua.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Lua.xcframework; path = ios/libraries/Lua.xcframework; sourceTree = "<group>"; };
 		FAD19A151DFF8CA200D5398A /* ImageDataBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageDataBase.cpp; sourceTree = "<group>"; };
 		FAD19A151DFF8CA200D5398A /* ImageDataBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageDataBase.cpp; sourceTree = "<group>"; };
@@ -2146,10 +2161,10 @@
 		FADF4CC52663D0EC004F95C1 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
 		FADF4CC52663D0EC004F95C1 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
 		FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Buffer.cpp; sourceTree = "<group>"; };
 		FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Buffer.cpp; sourceTree = "<group>"; };
 		FADF53F71E3C7ACD00012CC0 /* Buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Buffer.h; sourceTree = "<group>"; };
 		FADF53F71E3C7ACD00012CC0 /* Buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Buffer.h; sourceTree = "<group>"; };
-		FADF53FB1E3D74F200012CC0 /* Text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Text.cpp; sourceTree = "<group>"; };
-		FADF53FC1E3D74F200012CC0 /* Text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Text.h; sourceTree = "<group>"; };
-		FADF54001E3D77B500012CC0 /* wrap_Text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Text.cpp; sourceTree = "<group>"; };
-		FADF54011E3D77B500012CC0 /* wrap_Text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Text.h; sourceTree = "<group>"; };
+		FADF53FB1E3D74F200012CC0 /* TextBatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextBatch.cpp; sourceTree = "<group>"; };
+		FADF53FC1E3D74F200012CC0 /* TextBatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextBatch.h; sourceTree = "<group>"; };
+		FADF54001E3D77B500012CC0 /* wrap_TextBatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_TextBatch.cpp; sourceTree = "<group>"; };
+		FADF54011E3D77B500012CC0 /* wrap_TextBatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_TextBatch.h; sourceTree = "<group>"; };
 		FADF54051E3D78F700012CC0 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Video.cpp; sourceTree = "<group>"; };
 		FADF54051E3D78F700012CC0 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Video.cpp; sourceTree = "<group>"; };
 		FADF54061E3D78F700012CC0 /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = "<group>"; };
 		FADF54061E3D78F700012CC0 /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = "<group>"; };
 		FADF540A1E3D7CDD00012CC0 /* wrap_Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Video.cpp; sourceTree = "<group>"; };
 		FADF540A1E3D7CDD00012CC0 /* wrap_Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Video.cpp; sourceTree = "<group>"; };
@@ -2660,6 +2675,7 @@
 				FA0B7C001A95902C000E1D17 /* math */,
 				FA0B7C001A95902C000E1D17 /* math */,
 				FA0B7C0D1A95902C000E1D17 /* mouse */,
 				FA0B7C0D1A95902C000E1D17 /* mouse */,
 				FA0B7C1B1A95902C000E1D17 /* physics */,
 				FA0B7C1B1A95902C000E1D17 /* physics */,
+				FACA06A4293EE5CD001A2557 /* sensor */,
 				FA0B7C7B1A95902C000E1D17 /* sound */,
 				FA0B7C7B1A95902C000E1D17 /* sound */,
 				FA0B7C9A1A95902C000E1D17 /* system */,
 				FA0B7C9A1A95902C000E1D17 /* system */,
 				FA0B7CA21A95902C000E1D17 /* thread */,
 				FA0B7CA21A95902C000E1D17 /* thread */,
@@ -2860,8 +2876,8 @@
 				FADF542E1E3DABF600012CC0 /* SpriteBatch.h */,
 				FADF542E1E3DABF600012CC0 /* SpriteBatch.h */,
 				FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */,
 				FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */,
 				FA2AF6721DAD62710032B62C /* StreamBuffer.h */,
 				FA2AF6721DAD62710032B62C /* StreamBuffer.h */,
-				FADF53FB1E3D74F200012CC0 /* Text.cpp */,
-				FADF53FC1E3D74F200012CC0 /* Text.h */,
+				FADF53FB1E3D74F200012CC0 /* TextBatch.cpp */,
+				FADF53FC1E3D74F200012CC0 /* TextBatch.h */,
 				FA0B7BBE1A95902C000E1D17 /* Texture.cpp */,
 				FA0B7BBE1A95902C000E1D17 /* Texture.cpp */,
 				FA0B7BBF1A95902C000E1D17 /* Texture.h */,
 				FA0B7BBF1A95902C000E1D17 /* Texture.h */,
 				FA2AF6731DAD64970032B62C /* vertex.cpp */,
 				FA2AF6731DAD64970032B62C /* vertex.cpp */,
@@ -2889,8 +2905,8 @@
 				FA1BA0B61E17043400AA2803 /* wrap_Shader.h */,
 				FA1BA0B61E17043400AA2803 /* wrap_Shader.h */,
 				FADF54321E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp */,
 				FADF54321E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp */,
 				FADF54331E3DAE6E00012CC0 /* wrap_SpriteBatch.h */,
 				FADF54331E3DAE6E00012CC0 /* wrap_SpriteBatch.h */,
-				FADF54001E3D77B500012CC0 /* wrap_Text.cpp */,
-				FADF54011E3D77B500012CC0 /* wrap_Text.h */,
+				FADF54001E3D77B500012CC0 /* wrap_TextBatch.cpp */,
+				FADF54011E3D77B500012CC0 /* wrap_TextBatch.h */,
 				FA620A301AA2F8DB005DB4C2 /* wrap_Texture.cpp */,
 				FA620A301AA2F8DB005DB4C2 /* wrap_Texture.cpp */,
 				FA620A311AA2F8DB005DB4C2 /* wrap_Texture.h */,
 				FA620A311AA2F8DB005DB4C2 /* wrap_Texture.h */,
 				FADF540A1E3D7CDD00012CC0 /* wrap_Video.cpp */,
 				FADF540A1E3D7CDD00012CC0 /* wrap_Video.cpp */,
@@ -3807,6 +3823,27 @@
 			path = data;
 			path = data;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		FACA06A4293EE5CD001A2557 /* sensor */ = {
+			isa = PBXGroup;
+			children = (
+				FACA06A7293EE5CD001A2557 /* sdl */,
+				FACA06AA293EE5CD001A2557 /* Sensor.cpp */,
+				FACA06A6293EE5CD001A2557 /* Sensor.h */,
+				FACA06A5293EE5CD001A2557 /* wrap_Sensor.cpp */,
+				FACA06AB293EE5CD001A2557 /* wrap_Sensor.h */,
+			);
+			path = sensor;
+			sourceTree = "<group>";
+		};
+		FACA06A7293EE5CD001A2557 /* sdl */ = {
+			isa = PBXGroup;
+			children = (
+				FACA06A9293EE5CD001A2557 /* Sensor.cpp */,
+				FACA06A8293EE5CD001A2557 /* Sensor.h */,
+			);
+			path = sdl;
+			sourceTree = "<group>";
+		};
 		FAF13FBF1E20934C00F898D2 /* glslang */ = {
 		FAF13FBF1E20934C00F898D2 /* glslang */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -4010,6 +4047,7 @@
 				FA0B7D4A1A95902C000E1D17 /* Polyline.h in Headers */,
 				FA0B7D4A1A95902C000E1D17 /* Polyline.h in Headers */,
 				FABDA9E72552448300B5C523 /* b2_chain_shape.h in Headers */,
 				FABDA9E72552448300B5C523 /* b2_chain_shape.h in Headers */,
 				FAF1405C1E20934C00F898D2 /* InitializeGlobals.h in Headers */,
 				FAF1405C1E20934C00F898D2 /* InitializeGlobals.h in Headers */,
+				FACA06B4293EE5CD001A2557 /* wrap_Sensor.h in Headers */,
 				FA0B7DB31A95902C000E1D17 /* wrap_Image.h in Headers */,
 				FA0B7DB31A95902C000E1D17 /* wrap_Image.h in Headers */,
 				FAF140591E20934C00F898D2 /* Common.h in Headers */,
 				FAF140591E20934C00F898D2 /* Common.h in Headers */,
 				FA0B7EC31A95902C000E1D17 /* threads.h in Headers */,
 				FA0B7EC31A95902C000E1D17 /* threads.h in Headers */,
@@ -4210,7 +4248,7 @@
 				FABDA9EA2552448300B5C523 /* b2_world_callbacks.h in Headers */,
 				FABDA9EA2552448300B5C523 /* b2_world_callbacks.h in Headers */,
 				FA0B7E7D1A95902C000E1D17 /* wrap_World.h in Headers */,
 				FA0B7E7D1A95902C000E1D17 /* wrap_World.h in Headers */,
 				FA0B7EBD1A95902C000E1D17 /* LuaThread.h in Headers */,
 				FA0B7EBD1A95902C000E1D17 /* LuaThread.h in Headers */,
-				FADF53FF1E3D74F200012CC0 /* Text.h in Headers */,
+				FADF53FF1E3D74F200012CC0 /* TextBatch.h in Headers */,
 				FA0B7DC01A95902C000E1D17 /* JoystickModule.h in Headers */,
 				FA0B7DC01A95902C000E1D17 /* JoystickModule.h in Headers */,
 				FA18CF3923DCF67900263725 /* spirv_msl.hpp in Headers */,
 				FA18CF3923DCF67900263725 /* spirv_msl.hpp in Headers */,
 				FA0B7E871A95902C000E1D17 /* CoreAudioDecoder.h in Headers */,
 				FA0B7E871A95902C000E1D17 /* CoreAudioDecoder.h in Headers */,
@@ -4299,6 +4337,7 @@
 				FA0B7DC31A95902C000E1D17 /* wrap_Joystick.h in Headers */,
 				FA0B7DC31A95902C000E1D17 /* wrap_Joystick.h in Headers */,
 				FA18CF3B23DCF67900263725 /* spirv_cross_util.hpp in Headers */,
 				FA18CF3B23DCF67900263725 /* spirv_cross_util.hpp in Headers */,
 				FAF140631E20934C00F898D2 /* Types.h in Headers */,
 				FAF140631E20934C00F898D2 /* Types.h in Headers */,
+				FACA06AF293EE5CD001A2557 /* Sensor.h in Headers */,
 				FA4F2BE61DE6650600CA37D7 /* wrap_Transform.h in Headers */,
 				FA4F2BE61DE6650600CA37D7 /* wrap_Transform.h in Headers */,
 				FA0B7EE71A95902D000E1D17 /* Window.h in Headers */,
 				FA0B7EE71A95902D000E1D17 /* Window.h in Headers */,
 				FA0B7E651A95902C000E1D17 /* wrap_PolygonShape.h in Headers */,
 				FA0B7E651A95902C000E1D17 /* wrap_PolygonShape.h in Headers */,
@@ -4359,6 +4398,7 @@
 				FA0B7E931A95902C000E1D17 /* ModPlugDecoder.h in Headers */,
 				FA0B7E931A95902C000E1D17 /* ModPlugDecoder.h in Headers */,
 				FA0B7D111A95902C000E1D17 /* BMFontRasterizer.h in Headers */,
 				FA0B7D111A95902C000E1D17 /* BMFontRasterizer.h in Headers */,
 				217DFBE61D9F6D490055D849 /* http.lua.h in Headers */,
 				217DFBE61D9F6D490055D849 /* http.lua.h in Headers */,
+				FACA06AE293EE5CD001A2557 /* Sensor.h in Headers */,
 				FA18CF1D23DCF67900263725 /* sampler.hpp in Headers */,
 				FA18CF1D23DCF67900263725 /* sampler.hpp in Headers */,
 				FA0B7E3E1A95902C000E1D17 /* wrap_Body.h in Headers */,
 				FA0B7E3E1A95902C000E1D17 /* wrap_Body.h in Headers */,
 				FABDA9C92552448300B5C523 /* b2_distance.h in Headers */,
 				FABDA9C92552448300B5C523 /* b2_distance.h in Headers */,
@@ -4443,7 +4483,7 @@
 				FABDA9CF2552448300B5C523 /* b2_mouse_joint.h in Headers */,
 				FABDA9CF2552448300B5C523 /* b2_mouse_joint.h in Headers */,
 				FABDA9E42552448300B5C523 /* b2_collision.h in Headers */,
 				FABDA9E42552448300B5C523 /* b2_collision.h in Headers */,
 				FA41A3CA1C0A1F950084430C /* ASTCHandler.h in Headers */,
 				FA41A3CA1C0A1F950084430C /* ASTCHandler.h in Headers */,
-				FADF54041E3D77B500012CC0 /* wrap_Text.h in Headers */,
+				FADF54041E3D77B500012CC0 /* wrap_TextBatch.h in Headers */,
 				FA0B7ED31A95902C000E1D17 /* wrap_ThreadModule.h in Headers */,
 				FA0B7ED31A95902C000E1D17 /* wrap_ThreadModule.h in Headers */,
 				FAB922C6257D99EF0035DAD6 /* Range.h in Headers */,
 				FAB922C6257D99EF0035DAD6 /* Range.h in Headers */,
 				FAC756F61E4F99B400B91289 /* Effect.h in Headers */,
 				FAC756F61E4F99B400B91289 /* Effect.h in Headers */,
@@ -4613,6 +4653,7 @@
 				FAE64A952071365100BC7981 /* physfs_platform_qnx.c in Sources */,
 				FAE64A952071365100BC7981 /* physfs_platform_qnx.c in Sources */,
 				FABDA9FC2552448300B5C523 /* b2_collide_polygon.cpp in Sources */,
 				FABDA9FC2552448300B5C523 /* b2_collide_polygon.cpp in Sources */,
 				FABDA9D12552448300B5C523 /* b2_draw.cpp in Sources */,
 				FABDA9D12552448300B5C523 /* b2_draw.cpp in Sources */,
+				FACA06B3293EE5CD001A2557 /* Sensor.cpp in Sources */,
 				FA0B7EA41A95902C000E1D17 /* SoundData.cpp in Sources */,
 				FA0B7EA41A95902C000E1D17 /* SoundData.cpp in Sources */,
 				FAF1406A1E20934C00F898D2 /* glslang_tab.cpp in Sources */,
 				FAF1406A1E20934C00F898D2 /* glslang_tab.cpp in Sources */,
 				FA8951A31AA2EDF300EC385A /* wrap_Event.cpp in Sources */,
 				FA8951A31AA2EDF300EC385A /* wrap_Event.cpp in Sources */,
@@ -4644,7 +4685,7 @@
 				FA1BA0A31E16D97500AA2803 /* wrap_Font.cpp in Sources */,
 				FA1BA0A31E16D97500AA2803 /* wrap_Font.cpp in Sources */,
 				FABDA9842552448200B5C523 /* b2_chain_polygon_contact.cpp in Sources */,
 				FABDA9842552448200B5C523 /* b2_chain_polygon_contact.cpp in Sources */,
 				FA0B7E0D1A95902C000E1D17 /* Fixture.cpp in Sources */,
 				FA0B7E0D1A95902C000E1D17 /* Fixture.cpp in Sources */,
-				FADF53FE1E3D74F200012CC0 /* Text.cpp in Sources */,
+				FADF53FE1E3D74F200012CC0 /* TextBatch.cpp in Sources */,
 				FA0B7D191A95902C000E1D17 /* TrueTypeRasterizer.cpp in Sources */,
 				FA0B7D191A95902C000E1D17 /* TrueTypeRasterizer.cpp in Sources */,
 				FAC271E723B5B5B400C200D3 /* renderstate.cpp in Sources */,
 				FAC271E723B5B5B400C200D3 /* renderstate.cpp in Sources */,
 				FA84DE6727791C36002674C6 /* GraphicsReadback.cpp in Sources */,
 				FA84DE6727791C36002674C6 /* GraphicsReadback.cpp in Sources */,
@@ -4664,7 +4705,7 @@
 				FA0B7CE61A95902C000E1D17 /* wrap_Source.cpp in Sources */,
 				FA0B7CE61A95902C000E1D17 /* wrap_Source.cpp in Sources */,
 				FA9D8DDE1DEF842A002CD881 /* Drawable.cpp in Sources */,
 				FA9D8DDE1DEF842A002CD881 /* Drawable.cpp in Sources */,
 				FA0B7CCE1A95902C000E1D17 /* Audio.cpp in Sources */,
 				FA0B7CCE1A95902C000E1D17 /* Audio.cpp in Sources */,
-				FADF54031E3D77B500012CC0 /* wrap_Text.cpp in Sources */,
+				FADF54031E3D77B500012CC0 /* wrap_TextBatch.cpp in Sources */,
 				FA0B7DCB1A95902C000E1D17 /* Keyboard.cpp in Sources */,
 				FA0B7DCB1A95902C000E1D17 /* Keyboard.cpp in Sources */,
 				FA0B7DFB1A95902C000E1D17 /* Body.cpp in Sources */,
 				FA0B7DFB1A95902C000E1D17 /* Body.cpp in Sources */,
 				FA0B7ED21A95902C000E1D17 /* wrap_ThreadModule.cpp in Sources */,
 				FA0B7ED21A95902C000E1D17 /* wrap_ThreadModule.cpp in Sources */,
@@ -4688,6 +4729,7 @@
 				FA0B7AD51A958EA3000E1D17 /* unix.c in Sources */,
 				FA0B7AD51A958EA3000E1D17 /* unix.c in Sources */,
 				FAB17BE71ABFAA9000F9BA27 /* lz4.c in Sources */,
 				FAB17BE71ABFAA9000F9BA27 /* lz4.c in Sources */,
 				FAE64A902071364800BC7981 /* physfs_unicode.c in Sources */,
 				FAE64A902071364800BC7981 /* physfs_unicode.c in Sources */,
+				FACA06B1293EE5CD001A2557 /* Sensor.cpp in Sources */,
 				FA4F2BE71DE6650D00CA37D7 /* Transform.cpp in Sources */,
 				FA4F2BE71DE6650D00CA37D7 /* Transform.cpp in Sources */,
 				FAF140A11E20934C00F898D2 /* RemoveTree.cpp in Sources */,
 				FAF140A11E20934C00F898D2 /* RemoveTree.cpp in Sources */,
 				FABDA9972552448300B5C523 /* b2_distance_joint.cpp in Sources */,
 				FABDA9972552448300B5C523 /* b2_distance_joint.cpp in Sources */,
@@ -4833,6 +4875,7 @@
 				FA4F2BB41DE1E4BD00CA37D7 /* RecordingDevice.cpp in Sources */,
 				FA4F2BB41DE1E4BD00CA37D7 /* RecordingDevice.cpp in Sources */,
 				FA0B7DF51A95902C000E1D17 /* wrap_Mouse.cpp in Sources */,
 				FA0B7DF51A95902C000E1D17 /* wrap_Mouse.cpp in Sources */,
 				FA0B7E861A95902C000E1D17 /* CoreAudioDecoder.cpp in Sources */,
 				FA0B7E861A95902C000E1D17 /* CoreAudioDecoder.cpp in Sources */,
+				FACA06AD293EE5CD001A2557 /* wrap_Sensor.cpp in Sources */,
 				FA0B7E761A95902C000E1D17 /* wrap_WeldJoint.cpp in Sources */,
 				FA0B7E761A95902C000E1D17 /* wrap_WeldJoint.cpp in Sources */,
 				FA0B7D561A95902C000E1D17 /* Buffer.cpp in Sources */,
 				FA0B7D561A95902C000E1D17 /* Buffer.cpp in Sources */,
 				FA1557C51CE90BD900AFF582 /* EXRHandler.cpp in Sources */,
 				FA1557C51CE90BD900AFF582 /* EXRHandler.cpp in Sources */,
@@ -5037,6 +5080,7 @@
 				FAF6C9E623C2DE2900D7B5BC /* SpvBuilder.cpp in Sources */,
 				FAF6C9E623C2DE2900D7B5BC /* SpvBuilder.cpp in Sources */,
 				FABDA9C12552448300B5C523 /* b2_rope.cpp in Sources */,
 				FABDA9C12552448300B5C523 /* b2_rope.cpp in Sources */,
 				FA0B7D1E1A95902C000E1D17 /* ImageRasterizer.cpp in Sources */,
 				FA0B7D1E1A95902C000E1D17 /* ImageRasterizer.cpp in Sources */,
+				FACA06B2293EE5CD001A2557 /* Sensor.cpp in Sources */,
 				FABDA9FB2552448300B5C523 /* b2_collide_polygon.cpp in Sources */,
 				FABDA9FB2552448300B5C523 /* b2_collide_polygon.cpp in Sources */,
 				FABDA9D02552448300B5C523 /* b2_draw.cpp in Sources */,
 				FABDA9D02552448300B5C523 /* b2_draw.cpp in Sources */,
 				FAF140A31E20934C00F898D2 /* Scan.cpp in Sources */,
 				FAF140A31E20934C00F898D2 /* Scan.cpp in Sources */,
@@ -5080,7 +5124,7 @@
 				FAC7CD781FE35E95006A60C7 /* physfs_platform_qnx.c in Sources */,
 				FAC7CD781FE35E95006A60C7 /* physfs_platform_qnx.c in Sources */,
 				FA0B7D3C1A95902C000E1D17 /* Texture.cpp in Sources */,
 				FA0B7D3C1A95902C000E1D17 /* Texture.cpp in Sources */,
 				FABDA9EB2552448300B5C523 /* b2_collide_circle.cpp in Sources */,
 				FABDA9EB2552448300B5C523 /* b2_collide_circle.cpp in Sources */,
-				FADF53FD1E3D74F200012CC0 /* Text.cpp in Sources */,
+				FADF53FD1E3D74F200012CC0 /* TextBatch.cpp in Sources */,
 				FA84DE612778D7F3002674C6 /* SpirvIntrinsics.cpp in Sources */,
 				FA84DE612778D7F3002674C6 /* SpirvIntrinsics.cpp in Sources */,
 				FAFEB29C28F210550025D7D0 /* unixstream.c in Sources */,
 				FAFEB29C28F210550025D7D0 /* unixstream.c in Sources */,
 				FA6A2B741F60B6710074C308 /* ByteData.cpp in Sources */,
 				FA6A2B741F60B6710074C308 /* ByteData.cpp in Sources */,
@@ -5095,7 +5139,7 @@
 				FA9D8DDD1DEF842A002CD881 /* Drawable.cpp in Sources */,
 				FA9D8DDD1DEF842A002CD881 /* Drawable.cpp in Sources */,
 				FA0B7DFA1A95902C000E1D17 /* Body.cpp in Sources */,
 				FA0B7DFA1A95902C000E1D17 /* Body.cpp in Sources */,
 				FAF6C9EB23C2DE2900D7B5BC /* GlslangToSpv.cpp in Sources */,
 				FAF6C9EB23C2DE2900D7B5BC /* GlslangToSpv.cpp in Sources */,
-				FADF54021E3D77B500012CC0 /* wrap_Text.cpp in Sources */,
+				FADF54021E3D77B500012CC0 /* wrap_TextBatch.cpp in Sources */,
 				FA0B7ED11A95902C000E1D17 /* wrap_ThreadModule.cpp in Sources */,
 				FA0B7ED11A95902C000E1D17 /* wrap_ThreadModule.cpp in Sources */,
 				FAC7CD7F1FE35E95006A60C7 /* physfs_archiver_wad.c in Sources */,
 				FAC7CD7F1FE35E95006A60C7 /* physfs_archiver_wad.c in Sources */,
 				FA0B7EDF1A95902D000E1D17 /* wrap_Touch.cpp in Sources */,
 				FA0B7EDF1A95902D000E1D17 /* wrap_Touch.cpp in Sources */,
@@ -5112,6 +5156,7 @@
 				FA0B7AB51A958EA3000E1D17 /* ddsparse.cpp in Sources */,
 				FA0B7AB51A958EA3000E1D17 /* ddsparse.cpp in Sources */,
 				FACA02F41F5E396B0084B28F /* wrap_CompressedData.cpp in Sources */,
 				FACA02F41F5E396B0084B28F /* wrap_CompressedData.cpp in Sources */,
 				FAF140AC1E20934C00F898D2 /* Versions.cpp in Sources */,
 				FAF140AC1E20934C00F898D2 /* Versions.cpp in Sources */,
+				FACA06B0293EE5CD001A2557 /* Sensor.cpp in Sources */,
 				FA0B79461A958E3B000E1D17 /* Vector.cpp in Sources */,
 				FA0B79461A958E3B000E1D17 /* Vector.cpp in Sources */,
 				FAC7CD811FE35E95006A60C7 /* physfs_platform_os2.c in Sources */,
 				FAC7CD811FE35E95006A60C7 /* physfs_platform_os2.c in Sources */,
 				FABDA9962552448300B5C523 /* b2_distance_joint.cpp in Sources */,
 				FABDA9962552448300B5C523 /* b2_distance_joint.cpp in Sources */,
@@ -5257,6 +5302,7 @@
 				FAC7CD891FE35E95006A60C7 /* physfs_archiver_dir.c in Sources */,
 				FAC7CD891FE35E95006A60C7 /* physfs_archiver_dir.c in Sources */,
 				FAF140751E20934C00F898D2 /* IntermTraverse.cpp in Sources */,
 				FAF140751E20934C00F898D2 /* IntermTraverse.cpp in Sources */,
 				FA0B7CE21A95902C000E1D17 /* wrap_Audio.cpp in Sources */,
 				FA0B7CE21A95902C000E1D17 /* wrap_Audio.cpp in Sources */,
+				FACA06AC293EE5CD001A2557 /* wrap_Sensor.cpp in Sources */,
 				FA0B7DF71A95902C000E1D17 /* Body.cpp in Sources */,
 				FA0B7DF71A95902C000E1D17 /* Body.cpp in Sources */,
 				FABDA9ED2552448300B5C523 /* b2_circle_shape.cpp in Sources */,
 				FABDA9ED2552448300B5C523 /* b2_circle_shape.cpp in Sources */,
 				FA0B7DF41A95902C000E1D17 /* wrap_Mouse.cpp in Sources */,
 				FA0B7DF41A95902C000E1D17 /* wrap_Mouse.cpp in Sources */,

+ 1 - 0
src/common/Module.h

@@ -51,6 +51,7 @@ public:
 		M_MATH,
 		M_MATH,
 		M_MOUSE,
 		M_MOUSE,
 		M_PHYSICS,
 		M_PHYSICS,
+		M_SENSOR,
 		M_SOUND,
 		M_SOUND,
 		M_SYSTEM,
 		M_SYSTEM,
 		M_THREAD,
 		M_THREAD,

+ 1 - 0
src/common/config.h

@@ -159,6 +159,7 @@
 #	define LOVE_ENABLE_MATH
 #	define LOVE_ENABLE_MATH
 #	define LOVE_ENABLE_MOUSE
 #	define LOVE_ENABLE_MOUSE
 #	define LOVE_ENABLE_PHYSICS
 #	define LOVE_ENABLE_PHYSICS
+#	define LOVE_ENABLE_SENSOR
 #	define LOVE_ENABLE_SOUND
 #	define LOVE_ENABLE_SOUND
 #	define LOVE_ENABLE_SYSTEM
 #	define LOVE_ENABLE_SYSTEM
 #	define LOVE_ENABLE_THREAD
 #	define LOVE_ENABLE_THREAD

+ 13 - 8
src/modules/audio/openal/Source.cpp

@@ -1160,6 +1160,19 @@ int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
 			decoded = 0;
 			decoded = 0;
 	}
 	}
 
 
+	// This shouldn't run after toLoop is calculated in this streamAtomic call,
+	// otherwise it'll decrease too quickly.
+	// TODO: this code is hard to understand, can it be made more clear?
+	// It's meant to reset offsetSamples once OpenAL starts processing the first
+	// queued buffer after a loop.
+	if (toLoop > 0)
+	{
+		if (--toLoop == 0)
+		{
+			offsetSamples = 0;
+		}
+	}
+
 	if (decoder->isFinished() && isLooping())
 	if (decoder->isFinished() && isLooping())
 	{
 	{
 		int queued, processed;
 		int queued, processed;
@@ -1172,14 +1185,6 @@ int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
 		d->rewind();
 		d->rewind();
 	}
 	}
 
 
-	if (toLoop > 0)
-	{
-		if (--toLoop == 0)
-		{
-			offsetSamples = 0;
-		}
-	}
-
 	return decoded;
 	return decoded;
 }
 }
 
 

+ 60 - 22
src/modules/event/sdl/Event.cpp

@@ -32,6 +32,7 @@
 #include "audio/Audio.h"
 #include "audio/Audio.h"
 #include "common/config.h"
 #include "common/config.h"
 #include "timer/Timer.h"
 #include "timer/Timer.h"
+#include "sensor/sdl/Sensor.h"
 
 
 #include <cmath>
 #include <cmath>
 
 
@@ -178,6 +179,7 @@ Message *Event::convert(const SDL_Event &e)
 	vargs.reserve(4);
 	vargs.reserve(4);
 
 
 	love::filesystem::Filesystem *filesystem = nullptr;
 	love::filesystem::Filesystem *filesystem = nullptr;
+	love::sensor::Sensor *sensorInstance = nullptr;
 
 
 	love::keyboard::Keyboard::Key key = love::keyboard::Keyboard::KEY_UNKNOWN;
 	love::keyboard::Keyboard::Key key = love::keyboard::Keyboard::KEY_UNKNOWN;
 	love::keyboard::Keyboard::Scancode scancode = love::keyboard::Keyboard::SCANCODE_UNKNOWN;
 	love::keyboard::Keyboard::Scancode scancode = love::keyboard::Keyboard::SCANCODE_UNKNOWN;
@@ -191,10 +193,6 @@ Message *Event::convert(const SDL_Event &e)
 	love::touch::Touch::TouchInfo touchinfo;
 	love::touch::Touch::TouchInfo touchinfo;
 #endif
 #endif
 
 
-#ifdef LOVE_LINUX
-	static bool touchNormalizationBug = false;
-#endif
-
 	switch (e.type)
 	switch (e.type)
 	{
 	{
 	case SDL_KEYDOWN:
 	case SDL_KEYDOWN:
@@ -313,22 +311,9 @@ Message *Event::convert(const SDL_Event &e)
 		touchinfo.dy = e.tfinger.dy;
 		touchinfo.dy = e.tfinger.dy;
 		touchinfo.pressure = e.tfinger.pressure;
 		touchinfo.pressure = e.tfinger.pressure;
 
 
-#ifdef LOVE_LINUX
-		// FIXME: hacky workaround for SDL not normalizing touch coordinates in
-		// its X11 backend: https://bugzilla.libsdl.org/show_bug.cgi?id=2307
-		if (touchNormalizationBug || fabs(touchinfo.x) >= 1.5 || fabs(touchinfo.y) >= 1.5 || fabs(touchinfo.dx) >= 1.5 || fabs(touchinfo.dy) >= 1.5)
-		{
-			touchNormalizationBug = true;
-			windowToDPICoords(&touchinfo.x, &touchinfo.y);
-			windowToDPICoords(&touchinfo.dx, &touchinfo.dy);
-		}
-		else
-#endif
-		{
-			// SDL's coords are normalized to [0, 1], but we want screen coords.
-			normalizedToDPICoords(&touchinfo.x, &touchinfo.y);
-			normalizedToDPICoords(&touchinfo.dx, &touchinfo.dy);
-		}
+		// SDL's coords are normalized to [0, 1], but we want screen coords.
+		normalizedToDPICoords(&touchinfo.x, &touchinfo.y);
+		normalizedToDPICoords(&touchinfo.dx, &touchinfo.dy);
 
 
 		// We need to update the love.touch.sdl internal state from here.
 		// We need to update the love.touch.sdl internal state from here.
 		touchmodule = (touch::sdl::Touch *) Module::getInstance("love.touch.sdl");
 		touchmodule = (touch::sdl::Touch *) Module::getInstance("love.touch.sdl");
@@ -366,12 +351,14 @@ Message *Event::convert(const SDL_Event &e)
 	case SDL_CONTROLLERBUTTONDOWN:
 	case SDL_CONTROLLERBUTTONDOWN:
 	case SDL_CONTROLLERBUTTONUP:
 	case SDL_CONTROLLERBUTTONUP:
 	case SDL_CONTROLLERAXISMOTION:
 	case SDL_CONTROLLERAXISMOTION:
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	case SDL_CONTROLLERSENSORUPDATE:
+#endif
 		msg = convertJoystickEvent(e);
 		msg = convertJoystickEvent(e);
 		break;
 		break;
 	case SDL_WINDOWEVENT:
 	case SDL_WINDOWEVENT:
 		msg = convertWindowEvent(e);
 		msg = convertWindowEvent(e);
 		break;
 		break;
-#if SDL_VERSION_ATLEAST(2, 0, 9)
 	case SDL_DISPLAYEVENT:
 	case SDL_DISPLAYEVENT:
 		if (e.display.event == SDL_DISPLAYEVENT_ORIENTATION)
 		if (e.display.event == SDL_DISPLAYEVENT_ORIENTATION)
 		{
 		{
@@ -405,7 +392,6 @@ Message *Event::convert(const SDL_Event &e)
 			msg = new Message("displayrotated", vargs);
 			msg = new Message("displayrotated", vargs);
 		}
 		}
 		break;
 		break;
-#endif
 	case SDL_DROPFILE:
 	case SDL_DROPFILE:
 		filesystem = Module::getInstance<filesystem::Filesystem>(Module::M_FILESYSTEM);
 		filesystem = Module::getInstance<filesystem::Filesystem>(Module::M_FILESYSTEM);
 		if (filesystem != nullptr)
 		if (filesystem != nullptr)
@@ -446,6 +432,37 @@ Message *Event::convert(const SDL_Event &e)
 		msg = new Message("localechanged");
 		msg = new Message("localechanged");
 		break;
 		break;
 #endif
 #endif
+	case SDL_SENSORUPDATE:
+		sensorInstance = Module::getInstance<sensor::Sensor>(M_SENSOR);
+		if (sensorInstance)
+		{
+			std::vector<void*> sensors = sensorInstance->getHandles();
+
+			for (void *s: sensors)
+			{
+				SDL_Sensor *sensor = (SDL_Sensor *) s;
+				SDL_SensorID id = SDL_SensorGetInstanceID(sensor);
+
+				if (e.sensor.which == id)
+				{
+					// Found sensor
+					const char *sensorType;
+					if (!sensor::Sensor::getConstant(sensor::sdl::Sensor::convert(SDL_SensorGetType(sensor)), sensorType))
+						sensorType = "unknown";
+
+					vargs.emplace_back(sensorType, strlen(sensorType));
+					// Both accelerometer and gyroscope only pass up to 3 values.
+					// https://github.com/libsdl-org/SDL/blob/SDL2/include/SDL_sensor.h#L81-L127
+					vargs.emplace_back(e.sensor.data[0]);
+					vargs.emplace_back(e.sensor.data[1]);
+					vargs.emplace_back(e.sensor.data[2]);
+					msg = new Message("sensorupdated", vargs);
+
+					break;
+				}
+			}
+		}
+		break;
 	default:
 	default:
 		break;
 		break;
 	}
 	}
@@ -564,6 +581,27 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 			msg = new Message("joystickremoved", vargs);
 			msg = new Message("joystickremoved", vargs);
 		}
 		}
 		break;
 		break;
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	case SDL_CONTROLLERSENSORUPDATE:
+		stick = joymodule->getJoystickFromID(e.csensor.which);
+		if (stick)
+		{
+			using Sensor = love::sensor::Sensor;
+
+			const char *sensorName;
+			Sensor::SensorType sensorType = love::sensor::sdl::Sensor::convert((SDL_SensorType) e.csensor.sensor);
+			if (!Sensor::getConstant(sensorType, sensorName))
+				sensorName = "unknown";
+
+			vargs.emplace_back(joysticktype, stick);
+			vargs.emplace_back(sensorName, strlen(sensorName));
+			vargs.emplace_back(e.csensor.data[0]);
+			vargs.emplace_back(e.csensor.data[1]);
+			vargs.emplace_back(e.csensor.data[2]);
+			msg = new Message("joysticksensorupdated", vargs);
+		}
+		break;
+#endif // SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
 	default:
 	default:
 		break;
 		break;
 	}
 	}

+ 3 - 3
src/modules/graphics/Graphics.cpp

@@ -30,7 +30,7 @@
 #include "ParticleSystem.h"
 #include "ParticleSystem.h"
 #include "Font.h"
 #include "Font.h"
 #include "Video.h"
 #include "Video.h"
-#include "Text.h"
+#include "TextBatch.h"
 #include "common/deprecation.h"
 #include "common/deprecation.h"
 #include "common/config.h"
 #include "common/config.h"
 
 
@@ -439,9 +439,9 @@ Mesh *Graphics::newMesh(const std::vector<Mesh::BufferAttribute> &attributes, Pr
 	return new Mesh(attributes, drawmode);
 	return new Mesh(attributes, drawmode);
 }
 }
 
 
-love::graphics::Text *Graphics::newText(graphics::Font *font, const std::vector<Font::ColoredString> &text)
+love::graphics::TextBatch *Graphics::newTextBatch(graphics::Font *font, const std::vector<Font::ColoredString> &text)
 {
 {
-	return new Text(font, text);
+	return new TextBatch(font, text);
 }
 }
 
 
 love::data::ByteData *Graphics::readbackBuffer(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset)
 love::data::ByteData *Graphics::readbackBuffer(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset)

+ 2 - 2
src/modules/graphics/Graphics.h

@@ -58,7 +58,7 @@ namespace graphics
 
 
 class SpriteBatch;
 class SpriteBatch;
 class ParticleSystem;
 class ParticleSystem;
-class Text;
+class TextBatch;
 class Video;
 class Video;
 class Buffer;
 class Buffer;
 
 
@@ -460,7 +460,7 @@ public:
 	Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferDataUsage usage);
 	Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferDataUsage usage);
 	Mesh *newMesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode);
 	Mesh *newMesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode);
 
 
-	Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
+	TextBatch *newTextBatch(Font *font, const std::vector<Font::ColoredString> &text = {});
 
 
 	data::ByteData *readbackBuffer(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
 	data::ByteData *readbackBuffer(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
 	GraphicsReadback *readbackBufferAsync(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
 	GraphicsReadback *readbackBufferAsync(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);

+ 8 - 3
src/modules/graphics/Mesh.cpp

@@ -140,7 +140,7 @@ void Mesh::setupAttachedAttributes()
 		if (getAttachedAttributeIndex(name) != -1)
 		if (getAttachedAttributeIndex(name) != -1)
 			throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
 			throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
 
 
-		attachedAttributes.push_back({name, vertexBuffer, nullptr, (int) i, STEP_PER_VERTEX, true});
+		attachedAttributes.push_back({name, vertexBuffer, nullptr, (int) i, 0, STEP_PER_VERTEX, true});
 	}
 	}
 }
 }
 
 
@@ -208,7 +208,7 @@ bool Mesh::isAttributeEnabled(const std::string &name) const
 	return attachedAttributes[index].enabled;
 	return attachedAttributes[index].enabled;
 }
 }
 
 
-void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, AttributeStep step)
+void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, int startindex, AttributeStep step)
 {
 {
 	if ((buffer->getUsageFlags() & BUFFERUSAGEFLAG_VERTEX) == 0)
 	if ((buffer->getUsageFlags() & BUFFERUSAGEFLAG_VERTEX) == 0)
 		throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute.");
 		throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute.");
@@ -217,6 +217,9 @@ void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh,
 	if (step == STEP_PER_INSTANCE && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING])
 	if (step == STEP_PER_INSTANCE && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING])
 		throw love::Exception("Vertex attribute instancing is not supported on this system.");
 		throw love::Exception("Vertex attribute instancing is not supported on this system.");
 
 
+	if (startindex < 0 || startindex >= (int) buffer->getArrayLength())
+		throw love::Exception("Invalid start array index %d.", startindex + 1);
+
 	BufferAttribute oldattrib = {};
 	BufferAttribute oldattrib = {};
 	BufferAttribute newattrib = {};
 	BufferAttribute newattrib = {};
 
 
@@ -231,6 +234,7 @@ void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh,
 	newattrib.mesh = mesh;
 	newattrib.mesh = mesh;
 	newattrib.enabled = oldattrib.buffer.get() ? oldattrib.enabled : true;
 	newattrib.enabled = oldattrib.buffer.get() ? oldattrib.enabled : true;
 	newattrib.indexInBuffer = buffer->getDataMemberIndex(attachname);
 	newattrib.indexInBuffer = buffer->getDataMemberIndex(attachname);
+	newattrib.startArrayIndex = startindex;
 	newattrib.step = step;
 	newattrib.step = step;
 
 
 	if (newattrib.indexInBuffer < 0)
 	if (newattrib.indexInBuffer < 0)
@@ -578,12 +582,13 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
 
 
 			uint16 offset = (uint16) member.offset;
 			uint16 offset = (uint16) member.offset;
 			uint16 stride = (uint16) buffer->getArrayStride();
 			uint16 stride = (uint16) buffer->getArrayStride();
+			size_t bufferoffset = (size_t) stride * attrib.startArrayIndex;
 
 
 			attributes.set(attributeindex, member.decl.format, offset, activebuffers);
 			attributes.set(attributeindex, member.decl.format, offset, activebuffers);
 			attributes.setBufferLayout(activebuffers, stride, attrib.step);
 			attributes.setBufferLayout(activebuffers, stride, attrib.step);
 
 
 			// TODO: Ideally we want to reuse buffers with the same stride+step.
 			// TODO: Ideally we want to reuse buffers with the same stride+step.
-			buffers.set(activebuffers, buffer, 0);
+			buffers.set(activebuffers, buffer, bufferoffset);
 			activebuffers++;
 			activebuffers++;
 		}
 		}
 	}
 	}

+ 2 - 1
src/modules/graphics/Mesh.h

@@ -57,6 +57,7 @@ public:
 		StrongRef<Buffer> buffer;
 		StrongRef<Buffer> buffer;
 		StrongRef<Mesh> mesh;
 		StrongRef<Mesh> mesh;
 		int indexInBuffer;
 		int indexInBuffer;
+		int startArrayIndex;
 		AttributeStep step;
 		AttributeStep step;
 		bool enabled;
 		bool enabled;
 	};
 	};
@@ -109,7 +110,7 @@ public:
 	 * to make sure this Mesh knows to flush the passed in Mesh's data to its
 	 * to make sure this Mesh knows to flush the passed in Mesh's data to its
 	 * buffer when drawing.
 	 * buffer when drawing.
 	 **/
 	 **/
-	void attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
+	void attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, int startindex = 0, AttributeStep step = STEP_PER_VERTEX);
 	bool detachAttribute(const std::string &name);
 	bool detachAttribute(const std::string &name);
 	const std::vector<BufferAttribute> &getAttachedAttributes() const;
 	const std::vector<BufferAttribute> &getAttachedAttributes() const;
 
 

+ 17 - 17
src/modules/graphics/Text.cpp → src/modules/graphics/TextBatch.cpp

@@ -18,7 +18,7 @@
  * 3. This notice may not be removed or altered from any source distribution.
  * 3. This notice may not be removed or altered from any source distribution.
  **/
  **/
 
 
-#include "Text.h"
+#include "TextBatch.h"
 #include "Graphics.h"
 #include "Graphics.h"
 
 
 #include <algorithm>
 #include <algorithm>
@@ -28,9 +28,9 @@ namespace love
 namespace graphics
 namespace graphics
 {
 {
 
 
-love::Type Text::type("Text", &Drawable::type);
+love::Type TextBatch::type("TextBatch", &Drawable::type);
 
 
-Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
+TextBatch::TextBatch(Font *font, const std::vector<Font::ColoredString> &text)
 	: font(font)
 	: font(font)
 	, vertexAttributes(Font::vertexFormat, 0)
 	, vertexAttributes(Font::vertexFormat, 0)
 	, vertexData(nullptr)
 	, vertexData(nullptr)
@@ -41,13 +41,13 @@ Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
 	set(text);
 	set(text);
 }
 }
 
 
-Text::~Text()
+TextBatch::~TextBatch()
 {
 {
 	if (vertexData != nullptr)
 	if (vertexData != nullptr)
 		free(vertexData);
 		free(vertexData);
 }
 }
 
 
-void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
+void TextBatch::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
 {
 {
 	size_t offset = vertoffset * sizeof(Font::GlyphVertex);
 	size_t offset = vertoffset * sizeof(Font::GlyphVertex);
 	size_t datasize = vertices.size() * sizeof(Font::GlyphVertex);
 	size_t datasize = vertices.size() * sizeof(Font::GlyphVertex);
@@ -90,7 +90,7 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
 	}
 	}
 }
 }
 
 
-void Text::regenerateVertices()
+void TextBatch::regenerateVertices()
 {
 {
 	// If the font's texture cache was invalidated then we need to recreate the
 	// If the font's texture cache was invalidated then we need to recreate the
 	// text's vertices, since glyph texcoords might have changed.
 	// text's vertices, since glyph texcoords might have changed.
@@ -107,7 +107,7 @@ void Text::regenerateVertices()
 	}
 	}
 }
 }
 
 
-void Text::addTextData(const TextData &t)
+void TextBatch::addTextData(const TextData &t)
 {
 {
 	std::vector<Font::GlyphVertex> vertices;
 	std::vector<Font::GlyphVertex> vertices;
 	std::vector<Font::DrawCommand> newcommands;
 	std::vector<Font::DrawCommand> newcommands;
@@ -172,12 +172,12 @@ void Text::addTextData(const TextData &t)
 		regenerateVertices();
 		regenerateVertices();
 }
 }
 
 
-void Text::set(const std::vector<Font::ColoredString> &text)
+void TextBatch::set(const std::vector<Font::ColoredString> &text)
 {
 {
 	return set(text, -1.0f, Font::ALIGN_MAX_ENUM);
 	return set(text, -1.0f, Font::ALIGN_MAX_ENUM);
 }
 }
 
 
-void Text::set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align)
+void TextBatch::set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align)
 {
 {
 	if (text.empty() || (text.size() == 1 && text[0].str.empty()))
 	if (text.empty() || (text.size() == 1 && text[0].str.empty()))
 		return clear();
 		return clear();
@@ -188,12 +188,12 @@ void Text::set(const std::vector<Font::ColoredString> &text, float wrap, Font::A
 	addTextData({codepoints, wrap, align, {}, false, false, Matrix4()});
 	addTextData({codepoints, wrap, align, {}, false, false, Matrix4()});
 }
 }
 
 
-int Text::add(const std::vector<Font::ColoredString> &text, const Matrix4 &m)
+int TextBatch::add(const std::vector<Font::ColoredString> &text, const Matrix4 &m)
 {
 {
 	return addf(text, -1.0f, Font::ALIGN_MAX_ENUM, m);
 	return addf(text, -1.0f, Font::ALIGN_MAX_ENUM, m);
 }
 }
 
 
-int Text::addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m)
+int TextBatch::addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m)
 {
 {
 	Font::ColoredCodepoints codepoints;
 	Font::ColoredCodepoints codepoints;
 	Font::getCodepointsFromString(text, codepoints);
 	Font::getCodepointsFromString(text, codepoints);
@@ -203,7 +203,7 @@ int Text::addf(const std::vector<Font::ColoredString> &text, float wrap, Font::A
 	return (int) textData.size() - 1;
 	return (int) textData.size() - 1;
 }
 }
 
 
-void Text::clear()
+void TextBatch::clear()
 {
 {
 	textData.clear();
 	textData.clear();
 	drawCommands.clear();
 	drawCommands.clear();
@@ -211,7 +211,7 @@ void Text::clear()
 	vertOffset = 0;
 	vertOffset = 0;
 }
 }
 
 
-void Text::setFont(Font *f)
+void TextBatch::setFont(Font *f)
 {
 {
 	font.set(f);
 	font.set(f);
 	
 	
@@ -221,12 +221,12 @@ void Text::setFont(Font *f)
 	regenerateVertices();
 	regenerateVertices();
 }
 }
 
 
-Font *Text::getFont() const
+Font *TextBatch::getFont() const
 {
 {
 	return font.get();
 	return font.get();
 }
 }
 
 
-int Text::getWidth(int index) const
+int TextBatch::getWidth(int index) const
 {
 {
 	if (index < 0)
 	if (index < 0)
 		index = std::max((int) textData.size() - 1, 0);
 		index = std::max((int) textData.size() - 1, 0);
@@ -237,7 +237,7 @@ int Text::getWidth(int index) const
 	return textData[index].textInfo.width;
 	return textData[index].textInfo.width;
 }
 }
 
 
-int Text::getHeight(int index) const
+int TextBatch::getHeight(int index) const
 {
 {
 	if (index < 0)
 	if (index < 0)
 		index = std::max((int) textData.size() - 1, 0);
 		index = std::max((int) textData.size() - 1, 0);
@@ -248,7 +248,7 @@ int Text::getHeight(int index) const
 	return textData[index].textInfo.height;
 	return textData[index].textInfo.height;
 }
 }
 
 
-void Text::draw(Graphics *gfx, const Matrix4 &m)
+void TextBatch::draw(Graphics *gfx, const Matrix4 &m)
 {
 {
 	if (vertexBuffer == nullptr || vertexData == nullptr || drawCommands.empty())
 	if (vertexBuffer == nullptr || vertexData == nullptr || drawCommands.empty())
 		return;
 		return;

+ 3 - 3
src/modules/graphics/Text.h → src/modules/graphics/TextBatch.h

@@ -34,14 +34,14 @@ namespace graphics
 
 
 class Graphics;
 class Graphics;
 
 
-class Text : public Drawable
+class TextBatch : public Drawable
 {
 {
 public:
 public:
 
 
 	static love::Type type;
 	static love::Type type;
 
 
-	Text(Font *font, const std::vector<Font::ColoredString> &text = {});
-	virtual ~Text();
+	TextBatch(Font *font, const std::vector<Font::ColoredString> &text = {});
+	virtual ~TextBatch();
 
 
 	void set(const std::vector<Font::ColoredString> &text);
 	void set(const std::vector<Font::ColoredString> &text);
 	void set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align);
 	void set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align);

+ 19 - 19
src/modules/graphics/vulkan/Graphics.cpp

@@ -28,7 +28,7 @@
 #include "Shader.h"
 #include "Shader.h"
 #include "Vulkan.h"
 #include "Vulkan.h"
 
 
-#include "SDL_vulkan.h"
+#include <SDL_vulkan.h>
 
 
 #include <algorithm>
 #include <algorithm>
 #include <vector>
 #include <vector>
@@ -468,7 +468,8 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
 	this->pixelWidth = pixelwidth;
 	this->pixelWidth = pixelwidth;
 	this->pixelHeight = pixelheight;
 	this->pixelHeight = pixelheight;
 
 
-	resetProjection();
+	if (!isRenderTargetActive())
+		resetProjection();
 }
 }
 
 
 bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa)
 bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa)
@@ -476,6 +477,9 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
 	requestedMsaa = msaa;
 	requestedMsaa = msaa;
 	windowHasStencil = windowhasstencil;
 	windowHasStencil = windowhasstencil;
 
 
+	// Must be called before the swapchain is created.
+	setViewportSize(width, height, pixelwidth, pixelheight);
+
 	cleanUpFunctions.clear();
 	cleanUpFunctions.clear();
 	cleanUpFunctions.resize(MAX_FRAMES_IN_FLIGHT);
 	cleanUpFunctions.resize(MAX_FRAMES_IN_FLIGHT);
 
 
@@ -529,8 +533,6 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
 
 
 	restoreState(states.back());
 	restoreState(states.back());
 
 
-	setViewportSize(width, height, pixelwidth, pixelheight);
-
 	Vulkan::resetShaderSwitches();
 	Vulkan::resetShaderSwitches();
 
 
 	frameCounter = 0;
 	frameCounter = 0;
@@ -1783,16 +1785,20 @@ VkPresentModeKHR Graphics::chooseSwapPresentMode(const std::vector<VkPresentMode
 		else
 		else
 			return VK_PRESENT_MODE_FIFO_KHR;
 			return VK_PRESENT_MODE_FIFO_KHR;
 	case 0:
 	case 0:
-		if (std::find(begin, end, VK_PRESENT_MODE_MAILBOX_KHR) != end)
+		// Mailbox mode might be better than immediate mode for a lot of people.
+		// But on at least some systems it acts as if vsync is enabled
+		// https://github.com/love2d/love/issues/1852
+		// TODO: is that a bug in love's code or the graphics driver / compositor?
+		// Should love expose mailbox mode in an API to users in some manner,
+		// instead of trying to guess what to do?
+		if (std::find(begin, end, VK_PRESENT_MODE_IMMEDIATE_KHR) != end)
+			return VK_PRESENT_MODE_IMMEDIATE_KHR;
+		else if (std::find(begin, end, VK_PRESENT_MODE_MAILBOX_KHR) != end)
 			return VK_PRESENT_MODE_MAILBOX_KHR;
 			return VK_PRESENT_MODE_MAILBOX_KHR;
 		else
 		else
-		{
-			if (std::find(begin, end, VK_PRESENT_MODE_IMMEDIATE_KHR) != end)
-				return VK_PRESENT_MODE_IMMEDIATE_KHR;
-			else
-				return VK_PRESENT_MODE_FIFO_KHR;
-		}
+			return VK_PRESENT_MODE_FIFO_KHR;
 	default:
 	default:
+		// TODO: support for swap interval = 2, etc?
 		return VK_PRESENT_MODE_FIFO_KHR;
 		return VK_PRESENT_MODE_FIFO_KHR;
 	}
 	}
 }
 }
@@ -1814,15 +1820,9 @@ VkExtent2D Graphics::chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabiliti
 		return capabilities.currentExtent;
 		return capabilities.currentExtent;
 	else
 	else
 	{
 	{
-		auto window = Module::getInstance<love::window::Window>(M_WINDOW);
-		const void *handle = window->getHandle();
-
-		int width, height;
-		SDL_Vulkan_GetDrawableSize((SDL_Window*)handle, &width, &height);
-
 		VkExtent2D actualExtent = {
 		VkExtent2D actualExtent = {
-			static_cast<uint32_t>(width),
-			static_cast<uint32_t>(height)
+			static_cast<uint32_t>(pixelWidth),
+			static_cast<uint32_t>(pixelHeight)
 		};
 		};
 
 
 		actualExtent.width = clampuint32_t(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
 		actualExtent.width = clampuint32_t(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);

+ 13 - 6
src/modules/graphics/wrap_Graphics.cpp

@@ -2101,21 +2101,21 @@ int w_newMesh(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_newText(lua_State *L)
+int w_newTextBatch(lua_State *L)
 {
 {
 	luax_checkgraphicscreated(L);
 	luax_checkgraphicscreated(L);
 
 
 	graphics::Font *font = luax_checkfont(L, 1);
 	graphics::Font *font = luax_checkfont(L, 1);
-	Text *t = nullptr;
+	TextBatch *t = nullptr;
 
 
 	if (lua_isnoneornil(L, 2))
 	if (lua_isnoneornil(L, 2))
-		luax_catchexcept(L, [&](){ t = instance()->newText(font); });
+		luax_catchexcept(L, [&](){ t = instance()->newTextBatch(font); });
 	else
 	else
 	{
 	{
 		std::vector<Font::ColoredString> text;
 		std::vector<Font::ColoredString> text;
 		luax_checkcoloredstring(L, 2, text);
 		luax_checkcoloredstring(L, 2, text);
 
 
-		luax_catchexcept(L, [&](){ t = instance()->newText(font, text); });
+		luax_catchexcept(L, [&](){ t = instance()->newTextBatch(font, text); });
 	}
 	}
 
 
 	luax_pushtype(L, t);
 	luax_pushtype(L, t);
@@ -2123,6 +2123,12 @@ int w_newText(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
+int w_newText(lua_State *L)
+{
+	luax_markdeprecated(L, 1, "love.graphics.newText", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newTextBatch");
+	return w_newTextBatch(L);
+}
+
 int w_newVideo(lua_State *L)
 int w_newVideo(lua_State *L)
 {
 {
 	luax_checkgraphicscreated(L);
 	luax_checkgraphicscreated(L);
@@ -3769,7 +3775,7 @@ static const luaL_Reg functions[] =
 	{ "newVertexBuffer", w_newVertexBuffer },
 	{ "newVertexBuffer", w_newVertexBuffer },
 	{ "newIndexBuffer", w_newIndexBuffer },
 	{ "newIndexBuffer", w_newIndexBuffer },
 	{ "newMesh", w_newMesh },
 	{ "newMesh", w_newMesh },
-	{ "newText", w_newText },
+	{ "newTextBatch", w_newTextBatch },
 	{ "_newVideo", w_newVideo },
 	{ "_newVideo", w_newVideo },
 
 
 	{ "readbackBuffer", w_readbackBuffer },
 	{ "readbackBuffer", w_readbackBuffer },
@@ -3895,6 +3901,7 @@ static const luaL_Reg functions[] =
 	{ "newArrayImage", w_newArrayImage },
 	{ "newArrayImage", w_newArrayImage },
 	{ "newVolumeImage", w_newVolumeImage },
 	{ "newVolumeImage", w_newVolumeImage },
 	{ "newCubeImage", w_newCubeImage },
 	{ "newCubeImage", w_newCubeImage },
+	{ "newText", w_newText },
 	{ "getCanvasFormats", w_getCanvasFormats },
 	{ "getCanvasFormats", w_getCanvasFormats },
 	{ "getImageFormats", w_getImageFormats },
 	{ "getImageFormats", w_getImageFormats },
 
 
@@ -3919,7 +3926,7 @@ static const lua_CFunction types[] =
 	luaopen_particlesystem,
 	luaopen_particlesystem,
 	luaopen_shader,
 	luaopen_shader,
 	luaopen_mesh,
 	luaopen_mesh,
-	luaopen_text,
+	luaopen_textbatch,
 	luaopen_video,
 	luaopen_video,
 	0
 	0
 };
 };

+ 1 - 1
src/modules/graphics/wrap_Graphics.h

@@ -29,7 +29,7 @@
 #include "wrap_ParticleSystem.h"
 #include "wrap_ParticleSystem.h"
 #include "wrap_Shader.h"
 #include "wrap_Shader.h"
 #include "wrap_Mesh.h"
 #include "wrap_Mesh.h"
-#include "wrap_Text.h"
+#include "wrap_TextBatch.h"
 #include "wrap_Video.h"
 #include "wrap_Video.h"
 #include "wrap_Buffer.h"
 #include "wrap_Buffer.h"
 #include "wrap_GraphicsReadback.h"
 #include "wrap_GraphicsReadback.h"

+ 6 - 2
src/modules/graphics/wrap_Mesh.cpp

@@ -314,8 +314,9 @@ int w_Mesh_attachAttribute(lua_State *L)
 		return luax_enumerror(L, "vertex attribute step", getConstants(step), stepstr);
 		return luax_enumerror(L, "vertex attribute step", getConstants(step), stepstr);
 
 
 	const char *attachname = luaL_optstring(L, 5, name);
 	const char *attachname = luaL_optstring(L, 5, name);
+	int startindex = (int) luaL_optinteger(L, 6, 1) - 1;
 
 
-	luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, mesh, attachname, step); });
+	luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, mesh, attachname, startindex, step); });
 	return 0;
 	return 0;
 }
 }
 
 
@@ -340,7 +341,7 @@ int w_Mesh_getAttachedAttributes(lua_State *L)
 	{
 	{
 		const auto &attrib = attributes[i];
 		const auto &attrib = attributes[i];
 
 
-		lua_createtable(L, 4, 0);
+		lua_createtable(L, 5, 0);
 
 
 		luax_pushstring(L, attrib.name);
 		luax_pushstring(L, attrib.name);
 		lua_rawseti(L, -1, 1);
 		lua_rawseti(L, -1, 1);
@@ -358,6 +359,9 @@ int w_Mesh_getAttachedAttributes(lua_State *L)
 		luax_pushstring(L, member.decl.name);
 		luax_pushstring(L, member.decl.name);
 		lua_rawseti(L, -1, 4);
 		lua_rawseti(L, -1, 4);
 
 
+		lua_pushinteger(L, attrib.startArrayIndex + 1);
+		lua_rawseti(L, -1, 5);
+
 		lua_rawseti(L, -1, i + 1);
 		lua_rawseti(L, -1, i + 1);
 	}
 	}
 
 

+ 36 - 36
src/modules/graphics/wrap_Text.cpp → src/modules/graphics/wrap_TextBatch.cpp

@@ -18,7 +18,7 @@
  * 3. This notice may not be removed or altered from any source distribution.
  * 3. This notice may not be removed or altered from any source distribution.
  **/
  **/
 
 
-#include "wrap_Text.h"
+#include "wrap_TextBatch.h"
 #include "wrap_Font.h"
 #include "wrap_Font.h"
 #include "math/wrap_Transform.h"
 #include "math/wrap_Transform.h"
 
 
@@ -27,14 +27,14 @@ namespace love
 namespace graphics
 namespace graphics
 {
 {
 
 
-Text *luax_checktext(lua_State *L, int idx)
+TextBatch *luax_checktextbatch(lua_State *L, int idx)
 {
 {
-	return luax_checktype<Text>(L, idx);
+	return luax_checktype<TextBatch>(L, idx);
 }
 }
 
 
-int w_Text_set(lua_State *L)
+int w_TextBatch_set(lua_State *L)
 {
 {
-	Text *t = luax_checktext(L, 1);
+	TextBatch *t = luax_checktextbatch(L, 1);
 
 
 	std::vector<Font::ColoredString> newtext;
 	std::vector<Font::ColoredString> newtext;
 	luax_checkcoloredstring(L, 2, newtext);
 	luax_checkcoloredstring(L, 2, newtext);
@@ -43,9 +43,9 @@ int w_Text_set(lua_State *L)
 	return 0;
 	return 0;
 }
 }
 
 
-int w_Text_setf(lua_State *L)
+int w_TextBatch_setf(lua_State *L)
 {
 {
-	Text *t = luax_checktext(L, 1);
+	TextBatch *t = luax_checktextbatch(L, 1);
 
 
 	float wraplimit = (float) luaL_checknumber(L, 3);
 	float wraplimit = (float) luaL_checknumber(L, 3);
 
 
@@ -62,9 +62,9 @@ int w_Text_setf(lua_State *L)
 	return 0;
 	return 0;
 }
 }
 
 
-int w_Text_add(lua_State *L)
+int w_TextBatch_add(lua_State *L)
 {
 {
-	Text *t = luax_checktext(L, 1);
+	TextBatch *t = luax_checktextbatch(L, 1);
 
 
 	int index = 0;
 	int index = 0;
 
 
@@ -96,9 +96,9 @@ int w_Text_add(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_Text_addf(lua_State *L)
+int w_TextBatch_addf(lua_State *L)
 {
 {
-	Text *t = luax_checktext(L, 1);
+	TextBatch *t = luax_checktextbatch(L, 1);
 
 
 	int index = 0;
 	int index = 0;
 
 
@@ -138,72 +138,72 @@ int w_Text_addf(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_Text_clear(lua_State *L)
+int w_TextBatch_clear(lua_State *L)
 {
 {
-	Text *t = luax_checktext(L, 1);
+	TextBatch *t = luax_checktextbatch(L, 1);
 	luax_catchexcept(L, [&](){ t->clear(); });
 	luax_catchexcept(L, [&](){ t->clear(); });
 	return 0;
 	return 0;
 }
 }
 
 
-int w_Text_setFont(lua_State *L)
+int w_TextBatch_setFont(lua_State *L)
 {
 {
-	Text *t = luax_checktext(L, 1);
+	TextBatch *t = luax_checktextbatch(L, 1);
 	Font *f = luax_checktype<Font>(L, 2);
 	Font *f = luax_checktype<Font>(L, 2);
 	luax_catchexcept(L, [&](){ t->setFont(f); });
 	luax_catchexcept(L, [&](){ t->setFont(f); });
 	return 0;
 	return 0;
 }
 }
 
 
-int w_Text_getFont(lua_State *L)
+int w_TextBatch_getFont(lua_State *L)
 {
 {
-	Text *t = luax_checktext(L, 1);
+	TextBatch *t = luax_checktextbatch(L, 1);
 	Font *f = t->getFont();
 	Font *f = t->getFont();
 	luax_pushtype(L, f);
 	luax_pushtype(L, f);
 	return 1;
 	return 1;
 }
 }
 
 
-int w_Text_getWidth(lua_State *L)
+int w_TextBatch_getWidth(lua_State *L)
 {
 {
-	Text *t = luax_checktext(L, 1);
+	TextBatch *t = luax_checktextbatch(L, 1);
 	int index = (int) luaL_optinteger(L, 2, 0) - 1;
 	int index = (int) luaL_optinteger(L, 2, 0) - 1;
 	lua_pushnumber(L, t->getWidth(index));
 	lua_pushnumber(L, t->getWidth(index));
 	return 1;
 	return 1;
 }
 }
 
 
-int w_Text_getHeight(lua_State *L)
+int w_TextBatch_getHeight(lua_State *L)
 {
 {
-	Text *t = luax_checktext(L, 1);
+	TextBatch *t = luax_checktextbatch(L, 1);
 	int index = (int) luaL_optinteger(L, 2, 0) - 1;
 	int index = (int) luaL_optinteger(L, 2, 0) - 1;
 	lua_pushnumber(L, t->getHeight(index));
 	lua_pushnumber(L, t->getHeight(index));
 	return 1;
 	return 1;
 }
 }
 
 
-int w_Text_getDimensions(lua_State *L)
+int w_TextBatch_getDimensions(lua_State *L)
 {
 {
-	Text *t = luax_checktext(L, 1);
+	TextBatch *t = luax_checktextbatch(L, 1);
 	int index = (int) luaL_optinteger(L, 2, 0) - 1;
 	int index = (int) luaL_optinteger(L, 2, 0) - 1;
 	lua_pushnumber(L, t->getWidth(index));
 	lua_pushnumber(L, t->getWidth(index));
 	lua_pushnumber(L, t->getHeight(index));
 	lua_pushnumber(L, t->getHeight(index));
 	return 2;
 	return 2;
 }
 }
 
 
-static const luaL_Reg w_Text_functions[] =
+static const luaL_Reg w_TextBatch_functions[] =
 {
 {
-	{ "set", w_Text_set },
-	{ "setf", w_Text_setf },
-	{ "add", w_Text_add },
-	{ "addf", w_Text_addf },
-	{ "clear", w_Text_clear },
-	{ "setFont", w_Text_setFont },
-	{ "getFont", w_Text_getFont },
-	{ "getWidth", w_Text_getWidth },
-	{ "getHeight", w_Text_getHeight },
-	{ "getDimensions", w_Text_getDimensions },
+	{ "set", w_TextBatch_set },
+	{ "setf", w_TextBatch_setf },
+	{ "add", w_TextBatch_add },
+	{ "addf", w_TextBatch_addf },
+	{ "clear", w_TextBatch_clear },
+	{ "setFont", w_TextBatch_setFont },
+	{ "getFont", w_TextBatch_getFont },
+	{ "getWidth", w_TextBatch_getWidth },
+	{ "getHeight", w_TextBatch_getHeight },
+	{ "getDimensions", w_TextBatch_getDimensions },
 	{ 0, 0 }
 	{ 0, 0 }
 };
 };
 
 
-extern "C" int luaopen_text(lua_State *L)
+extern "C" int luaopen_textbatch(lua_State *L)
 {
 {
-	return luax_register_type(L, &Text::type, w_Text_functions, nullptr);
+	return luax_register_type(L, &TextBatch::type, w_TextBatch_functions, nullptr);
 }
 }
 
 
 } // graphics
 } // graphics

+ 3 - 3
src/modules/graphics/wrap_Text.h → src/modules/graphics/wrap_TextBatch.h

@@ -20,7 +20,7 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "Text.h"
+#include "TextBatch.h"
 #include "common/runtime.h"
 #include "common/runtime.h"
 
 
 namespace love
 namespace love
@@ -28,8 +28,8 @@ namespace love
 namespace graphics
 namespace graphics
 {
 {
 
 
-Text *luax_checktext(lua_State *L, int idx);
-extern "C" int luaopen_text(lua_State *L);
+TextBatch *luax_checktextbatch(lua_State *L, int idx);
+extern "C" int luaopen_textbatch(lua_State *L);
 
 
 } // graphics
 } // graphics
 } // love
 } // love

+ 1 - 1
src/modules/graphics/wrap_Texture.cpp

@@ -242,7 +242,7 @@ int w_Texture_setWrap(lua_State *L)
 		return luax_enumerror(L, "wrap mode", SamplerState::getConstants(s.wrapW), rstr);
 		return luax_enumerror(L, "wrap mode", SamplerState::getConstants(s.wrapW), rstr);
 
 
 	luax_catchexcept(L, [&](){ t->setSamplerState(s); });
 	luax_catchexcept(L, [&](){ t->setSamplerState(s); });
-	return 1;
+	return 0;
 }
 }
 
 
 int w_Texture_getWrap(lua_State *L)
 int w_Texture_getWrap(lua_State *L)

+ 8 - 0
src/modules/joystick/Joystick.h

@@ -24,6 +24,7 @@
 // LOVE
 // LOVE
 #include "common/Object.h"
 #include "common/Object.h"
 #include "common/StringMap.h"
 #include "common/StringMap.h"
+#include "sensor/Sensor.h"
 
 
 // stdlib
 // stdlib
 #include <vector>
 #include <vector>
@@ -38,6 +39,8 @@ class Joystick : public Object
 {
 {
 public:
 public:
 
 
+	using Sensor = love::sensor::Sensor;
+
 	static love::Type type;
 	static love::Type type;
 
 
 	// Joystick hat values.
 	// Joystick hat values.
@@ -198,6 +201,11 @@ public:
 	virtual bool setVibration() = 0;
 	virtual bool setVibration() = 0;
 	virtual void getVibration(float &left, float &right) = 0;
 	virtual void getVibration(float &left, float &right) = 0;
 
 
+	virtual bool hasSensor(Sensor::SensorType type) const = 0;
+	virtual bool isSensorEnabled(Sensor::SensorType type) const = 0;
+	virtual void setSensorEnabled(Sensor::SensorType type, bool enabled) = 0;
+	virtual std::vector<float> getSensorData(Sensor::SensorType type) const = 0;
+
 	STRINGMAP_CLASS_DECLARE(Hat);
 	STRINGMAP_CLASS_DECLARE(Hat);
 	STRINGMAP_CLASS_DECLARE(GamepadType);
 	STRINGMAP_CLASS_DECLARE(GamepadType);
 	STRINGMAP_CLASS_DECLARE(GamepadAxis);
 	STRINGMAP_CLASS_DECLARE(GamepadAxis);

+ 84 - 6
src/modules/joystick/sdl/Joystick.cpp

@@ -22,6 +22,10 @@
 #include "common/config.h"
 #include "common/config.h"
 #include "Joystick.h"
 #include "Joystick.h"
 #include "common/int.h"
 #include "common/int.h"
+#include "sensor/sdl/Sensor.h"
+
+// SDL
+#include <SDL_version.h>
 
 
 // C++
 // C++
 #include <algorithm>
 #include <algorithm>
@@ -409,7 +413,6 @@ int Joystick::getID() const
 
 
 void Joystick::getDeviceInfo(int &vendorID, int &productID, int &productVersion) const
 void Joystick::getDeviceInfo(int &vendorID, int &productID, int &productVersion) const
 {
 {
-#if SDL_VERSION_ATLEAST(2, 0, 6)
 	if (joyhandle != nullptr)
 	if (joyhandle != nullptr)
 	{
 	{
 		vendorID = SDL_JoystickGetVendor(joyhandle);
 		vendorID = SDL_JoystickGetVendor(joyhandle);
@@ -417,7 +420,6 @@ void Joystick::getDeviceInfo(int &vendorID, int &productID, int &productVersion)
 		productVersion = SDL_JoystickGetProductVersion(joyhandle);
 		productVersion = SDL_JoystickGetProductVersion(joyhandle);
 	}
 	}
 	else
 	else
-#endif
 	{
 	{
 		vendorID = 0;
 		vendorID = 0;
 		productID = 0;
 		productID = 0;
@@ -521,10 +523,8 @@ bool Joystick::setVibration(float left, float right, float duration)
 
 
 	bool success = false;
 	bool success = false;
 
 
-#if SDL_VERSION_ATLEAST(2, 0, 9)
 	if (SDL_JoystickRumble(joyhandle, (Uint16)(left * LOVE_UINT16_MAX), (Uint16)(right * LOVE_UINT16_MAX), length) == 0)
 	if (SDL_JoystickRumble(joyhandle, (Uint16)(left * LOVE_UINT16_MAX), (Uint16)(right * LOVE_UINT16_MAX), length) == 0)
 		success = true;
 		success = true;
-#endif
 
 
 	if (!success && !checkCreateHaptic())
 	if (!success && !checkCreateHaptic())
 		return false;
 		return false;
@@ -606,10 +606,8 @@ bool Joystick::setVibration()
 {
 {
 	bool success = false;
 	bool success = false;
 
 
-#if SDL_VERSION_ATLEAST(2, 0, 9)
 	if (!success)
 	if (!success)
 		success = isConnected() && SDL_JoystickRumble(joyhandle, 0, 0, 0) == 0;
 		success = isConnected() && SDL_JoystickRumble(joyhandle, 0, 0, 0) == 0;
-#endif
 
 
 	if (!success && SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
 	if (!success && SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
 		success = (SDL_HapticStopEffect(haptic, vibration.id) == 0);
 		success = (SDL_HapticStopEffect(haptic, vibration.id) == 0);
@@ -643,6 +641,86 @@ void Joystick::getVibration(float &left, float &right)
 	right = vibration.right;
 	right = vibration.right;
 }
 }
 
 
+bool Joystick::hasSensor(Sensor::SensorType type) const
+{
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	using SDLSensor = love::sensor::sdl::Sensor;
+
+	if (!isGamepad())
+		return false;
+
+	return SDL_GameControllerHasSensor(controller, SDLSensor::convert(type)) == SDL_TRUE;
+#else
+	return false;
+#endif
+}
+
+bool Joystick::isSensorEnabled(Sensor::SensorType type) const
+{
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	using SDLSensor = love::sensor::sdl::Sensor;
+
+	if (!isGamepad())
+		return false;
+
+	return SDL_GameControllerIsSensorEnabled(controller, SDLSensor::convert(type)) == SDL_TRUE;
+#else
+	return false;
+#endif
+}
+
+void Joystick::setSensorEnabled(Sensor::SensorType type, bool enabled)
+{
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	using SDLSensor = love::sensor::sdl::Sensor;
+
+	if (!isGamepad())
+		throw love::Exception("Sensor is only supported on gamepad");
+
+	if (SDL_GameControllerSetSensorEnabled(controller, SDLSensor::convert(type), enabled ? SDL_TRUE : SDL_FALSE) != 0)
+	{
+		const char *name = nullptr;
+		SDLSensor::getConstant(type, name);
+
+		throw love::Exception("Could not open \"%s\" SDL gamepad sensor (%s)", name, SDL_GetError());
+	}
+#else
+	throw love::Exception("Compiled version of SDL or LOVE does not support gamepad sensor");
+#endif
+}
+
+std::vector<float> Joystick::getSensorData(Sensor::SensorType type) const
+{
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	using SDLSensor = love::sensor::sdl::Sensor;
+
+	if (!isGamepad())
+		throw love::Exception("Sensor is only supported on gamepad");
+
+	std::vector<float> data(3);
+
+	if (!isSensorEnabled(type))
+	{
+		const char *name = nullptr;
+		SDLSensor::getConstant(type, name);
+
+		throw love::Exception("\"%s\" gamepad sensor is not enabled", name);
+	}
+
+	if (SDL_GameControllerGetSensorData(controller, SDLSensor::convert(type), data.data(), (int) data.size()) != 0)
+	{
+		const char *name = nullptr;
+		SDLSensor::getConstant(type, name);
+
+		throw love::Exception("Could not get \"%s\" SDL gamepad sensor data (%s)", name, SDL_GetError());
+	}
+
+	return data;
+#else
+	throw love::Exception("Compiled version of SDL or LOVE does not support gamepad sensor");
+#endif
+}
+
 bool Joystick::getConstant(Uint8 in, Joystick::Hat &out)
 bool Joystick::getConstant(Uint8 in, Joystick::Hat &out)
 {
 {
 	return hats.find(in, out);
 	return hats.find(in, out);

+ 5 - 0
src/modules/joystick/sdl/Joystick.h

@@ -88,6 +88,11 @@ public:
 	bool setVibration() override;
 	bool setVibration() override;
 	void getVibration(float &left, float &right) override;
 	void getVibration(float &left, float &right) override;
 
 
+	bool hasSensor(Sensor::SensorType type) const override;
+	bool isSensorEnabled(Sensor::SensorType type) const override;
+	void setSensorEnabled(Sensor::SensorType type, bool enabled) override;
+	std::vector<float> getSensorData(Sensor::SensorType type) const override;
+
 	static bool getConstant(Hat in, Uint8 &out);
 	static bool getConstant(Hat in, Uint8 &out);
 	static bool getConstant(Uint8 in, Hat &out);
 	static bool getConstant(Uint8 in, Hat &out);
 
 

+ 80 - 0
src/modules/joystick/wrap_Joystick.cpp

@@ -21,6 +21,7 @@
 // LOVE
 // LOVE
 #include "wrap_Joystick.h"
 #include "wrap_Joystick.h"
 #include "wrap_JoystickModule.h"
 #include "wrap_JoystickModule.h"
+#include "sensor/Sensor.h"
 
 
 #include <vector>
 #include <vector>
 
 
@@ -367,6 +368,78 @@ int w_Joystick_getVibration(lua_State *L)
 	return 2;
 	return 2;
 }
 }
 
 
+#ifdef LOVE_ENABLE_SENSOR
+
+int w_Joystick_hasSensor(lua_State *L)
+{
+	using namespace love::sensor;
+
+	Joystick *j = luax_checkjoystick(L, 1);
+	const char *sensorType = luaL_checkstring(L, 2);
+
+	Sensor::SensorType type;
+	if (!Sensor::getConstant(sensorType, type))
+		luax_enumerror(L, "sensor type", Sensor::getConstants(type), sensorType);
+
+	luax_pushboolean(L, j->hasSensor(type));
+	return 1;
+}
+
+int w_Joystick_isSensorEnabled(lua_State *L)
+{
+	using namespace love::sensor;
+
+	Joystick *j = luax_checkjoystick(L, 1);
+	const char *sensorType = luaL_checkstring(L, 2);
+
+	Sensor::SensorType type;
+	if (!Sensor::getConstant(sensorType, type))
+		luax_enumerror(L, "sensor type", Sensor::getConstants(type), sensorType);
+
+	luax_pushboolean(L, j->isSensorEnabled(type));
+	return 1;
+}
+
+int w_Joystick_setSensorEnabled(lua_State *L)
+{
+	using namespace love::sensor;
+
+	Joystick *j = luax_checkjoystick(L, 1);
+	const char *sensorType = luaL_checkstring(L, 2);
+
+	Sensor::SensorType type;
+	if (!Sensor::getConstant(sensorType, type))
+		luax_enumerror(L, "sensor type", Sensor::getConstants(type), sensorType);
+
+	bool enabled = luax_checkboolean(L, 3);
+
+	luax_catchexcept(L, [&]() { j->setSensorEnabled(type, enabled); });
+	return 0;
+}
+
+int w_Joystick_getSensorData(lua_State *L)
+{
+	using namespace love::sensor;
+
+	Joystick *j = luax_checkjoystick(L, 1);
+	const char *sensorType = luaL_checkstring(L, 2);
+
+	Sensor::SensorType type;
+	if (!Sensor::getConstant(sensorType, type))
+		luax_enumerror(L, "sensor type", Sensor::getConstants(type), sensorType);
+
+	std::vector<float> data;
+
+	luax_catchexcept(L, [&]() { data = j->getSensorData(type); });
+
+	for (float f: data)
+		lua_pushnumber(L, f);
+
+	return (int) data.size();
+}
+
+#endif // LOVE_ENABLE_SENSOR
+
 // List of functions to wrap.
 // List of functions to wrap.
 static const luaL_Reg w_Joystick_functions[] =
 static const luaL_Reg w_Joystick_functions[] =
 {
 {
@@ -396,6 +469,13 @@ static const luaL_Reg w_Joystick_functions[] =
 	{ "setVibration", w_Joystick_setVibration },
 	{ "setVibration", w_Joystick_setVibration },
 	{ "getVibration", w_Joystick_getVibration },
 	{ "getVibration", w_Joystick_getVibration },
 
 
+#ifdef LOVE_ENABLE_SENSOR
+	{ "hasSensor", w_Joystick_hasSensor },
+	{ "isSensorEnabled", w_Joystick_isSensorEnabled },
+	{ "setSensorEnabled", w_Joystick_setSensorEnabled },
+	{ "getSensorData", w_Joystick_setSensorEnabled },
+#endif
+
 	// From wrap_JoystickModule.
 	// From wrap_JoystickModule.
 	{ "getConnectedIndex", w_getIndex },
 	{ "getConnectedIndex", w_getIndex },
 
 

+ 3 - 1
src/modules/love/boot.lua

@@ -189,6 +189,7 @@ function love.init()
 			audio = true,
 			audio = true,
 			math = true,
 			math = true,
 			physics = true,
 			physics = true,
+			sensor = true,
 			sound = true,
 			sound = true,
 			system = true,
 			system = true,
 			font = true,
 			font = true,
@@ -204,7 +205,7 @@ function love.init()
 		identity = false,
 		identity = false,
 		appendidentity = false,
 		appendidentity = false,
 		externalstorage = false, -- Only relevant for Android.
 		externalstorage = false, -- Only relevant for Android.
-		accelerometerjoystick = true, -- Only relevant for Android / iOS.
+		accelerometerjoystick = nil, -- Only relevant for Android / iOS, deprecated.
 		gammacorrect = false,
 		gammacorrect = false,
 		highdpi = false,
 		highdpi = false,
 		renderers = nil,
 		renderers = nil,
@@ -311,6 +312,7 @@ function love.init()
 		"touch",
 		"touch",
 		"sound",
 		"sound",
 		"system",
 		"system",
+		"sensor",
 		"audio",
 		"audio",
 		"image",
 		"image",
 		"video",
 		"video",

+ 8 - 1
src/modules/love/callbacks.lua

@@ -88,6 +88,9 @@ function love.createhandlers()
 		joystickremoved = function (j)
 		joystickremoved = function (j)
 			if love.joystickremoved then return love.joystickremoved(j) end
 			if love.joystickremoved then return love.joystickremoved(j) end
 		end,
 		end,
+		joysticksensorupdated = function (j, sensorType, x, y, z)
+			if love.joysticksensorupdated then return love.joysticksensorupdated(j, sensorType, x, y, z) end
+		end,
 		focus = function (f)
 		focus = function (f)
 			if love.focus then return love.focus(f) end
 			if love.focus then return love.focus(f) end
 		end,
 		end,
@@ -134,6 +137,9 @@ function love.createhandlers()
 				love.audio.setPlaybackDevice()
 				love.audio.setPlaybackDevice()
 			end
 			end
 		end,
 		end,
+		sensorupdated = function (sensorType, x, y, z)
+			if love.sensorupdated then return love.sensorupdated(sensorType, x, y, z) end
+		end,
 	}, {
 	}, {
 		__index = function(self, name)
 		__index = function(self, name)
 			error("Unknown event: " .. name)
 			error("Unknown event: " .. name)
@@ -232,7 +238,8 @@ function love.errhand(msg)
 	if love.audio then love.audio.stop() end
 	if love.audio then love.audio.stop() end
 
 
 	love.graphics.reset()
 	love.graphics.reset()
-	local font = love.graphics.setNewFont(14)
+	local font = love.graphics.newFont(14)
+	love.graphics.setFont(font)
 
 
 	love.graphics.setColor(1, 1, 1)
 	love.graphics.setColor(1, 1, 1)
 
 

+ 10 - 0
src/modules/love/love.cpp

@@ -152,6 +152,9 @@ extern "C"
 #if defined(LOVE_ENABLE_PHYSICS)
 #if defined(LOVE_ENABLE_PHYSICS)
 	extern int luaopen_love_physics(lua_State*);
 	extern int luaopen_love_physics(lua_State*);
 #endif
 #endif
+#if defined(LOVE_ENABLE_SENSOR)
+	extern int luaopen_love_sensor(lua_State*);
+#endif
 #if defined(LOVE_ENABLE_SOUND)
 #if defined(LOVE_ENABLE_SOUND)
 	extern int luaopen_love_sound(lua_State*);
 	extern int luaopen_love_sound(lua_State*);
 #endif
 #endif
@@ -221,6 +224,9 @@ static const luaL_Reg modules[] = {
 #if defined(LOVE_ENABLE_PHYSICS)
 #if defined(LOVE_ENABLE_PHYSICS)
 	{ "love.physics", luaopen_love_physics },
 	{ "love.physics", luaopen_love_physics },
 #endif
 #endif
+#if defined(LOVE_ENABLE_SENSOR)
+	{ "love.sensor", luaopen_love_sensor },
+#endif
 #if defined(LOVE_ENABLE_SOUND)
 #if defined(LOVE_ENABLE_SOUND)
 	{ "love.sound", luaopen_love_sound },
 	{ "love.sound", luaopen_love_sound },
 #endif
 #endif
@@ -778,6 +784,10 @@ int w__setAccelerometerAsJoystick(lua_State *L)
 {
 {
 	bool enable = (bool) lua_toboolean(L, 1);
 	bool enable = (bool) lua_toboolean(L, 1);
 	SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, enable ? "1" : "0");
 	SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, enable ? "1" : "0");
+
+	if (enable)
+		love::luax_markdeprecated(L, 1, "accelerometerjoystick", love::API_FIELD, love::DEPRECATED_REPLACED, "love.sensor module");
+
 	return 0;
 	return 0;
 }
 }
 #endif // LOVE_LEGENDARY_ACCELEROMETER_AS_JOYSTICK_HACK
 #endif // LOVE_LEGENDARY_ACCELEROMETER_AS_JOYSTICK_HACK

+ 0 - 28
src/modules/physics/box2d/DistanceJoint.cpp

@@ -56,34 +56,6 @@ float DistanceJoint::getLength() const
 	return Physics::scaleUp(joint->GetLength());
 	return Physics::scaleUp(joint->GetLength());
 }
 }
 
 
-void DistanceJoint::setFrequency(float hz)
-{
-	float stiffness, damping;
-	b2LinearStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
-	joint->SetStiffness(stiffness);
-}
-
-float DistanceJoint::getFrequency() const
-{
-	float frequency, ratio;
-	Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
-	return frequency;
-}
-
-void DistanceJoint::setDampingRatio(float ratio)
-{
-	float stiffness, damping;
-	b2LinearStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
-	joint->SetDamping(damping);
-}
-
-float DistanceJoint::getDampingRatio() const
-{
-	float frequency, ratio;
-	Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
-	return ratio;
-}
-
 void DistanceJoint::setStiffness(float k)
 void DistanceJoint::setStiffness(float k)
 {
 {
 	joint->SetStiffness(k);
 	joint->SetStiffness(k);

+ 0 - 20
src/modules/physics/box2d/DistanceJoint.h

@@ -56,26 +56,6 @@ public:
 	 **/
 	 **/
 	float getLength() const;
 	float getLength() const;
 
 
-	/**
-	 * Sets the response speed. Independent of mass
-	 **/
-	void setFrequency(float hz);
-
-	/**
-	 * Gets the response speed. Independent of mass
-	 **/
-	float getFrequency() const;
-
-	/**
-	 * Set the spring damping ratio. Independent of mass
-	 **/
-	void setDampingRatio(float ratio);
-
-	/**
-	 * Get the spring damping ratio. Independent of mass
-	 **/
-	float getDampingRatio() const;
-
 	/**
 	/**
 	 * Sets the response speed. Dependent of mass
 	 * Sets the response speed. Dependent of mass
 	 **/
 	 **/

+ 1 - 34
src/modules/physics/box2d/MouseJoint.cpp

@@ -49,6 +49,7 @@ MouseJoint::MouseJoint(Body *body1, float x, float y)
 	def.bodyB = body1->body;
 	def.bodyB = body1->body;
 	def.maxForce = 1000.0f * body1->body->GetMass();
 	def.maxForce = 1000.0f * body1->body->GetMass();
 	def.target = Physics::scaleDown(b2Vec2(x,y));
 	def.target = Physics::scaleDown(b2Vec2(x,y));
+	Physics::computeLinearStiffness(def.stiffness, def.damping, 5.0, 0.7, def.bodyA, def.bodyB);
 	joint = (b2MouseJoint *)createJoint(&def);
 	joint = (b2MouseJoint *)createJoint(&def);
 }
 }
 
 
@@ -78,40 +79,6 @@ float MouseJoint::getMaxForce() const
 	return Physics::scaleUp(joint->GetMaxForce());
 	return Physics::scaleUp(joint->GetMaxForce());
 }
 }
 
 
-void MouseJoint::setFrequency(float hz)
-{
-	// This is kind of a crappy check. The Stiffness is used in an internal
-	// box2d calculation whose result must be > FLT_EPSILON, but other variables
-	// go into that calculation...
-	if (hz <= FLT_EPSILON * 2)
-		throw love::Exception("MouseJoint Stiffness must be a positive number.");
-
-	float stiffness, damping;
-	b2LinearStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
-	joint->SetStiffness(stiffness);
-}
-
-float MouseJoint::getFrequency() const
-{
-	float frequency, ratio;
-	Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
-	return frequency;
-}
-
-void MouseJoint::setDampingRatio(float ratio)
-{
-	float stiffness, damping;
-	b2LinearStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
-	joint->SetDamping(damping);
-}
-
-float MouseJoint::getDampingRatio() const
-{
-	float frequency, ratio;
-	Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
-	return ratio;
-}
-
 void MouseJoint::setStiffness(float k)
 void MouseJoint::setStiffness(float k)
 {
 {
 	// This is kind of a crappy check. The Stiffness is used in an internal
 	// This is kind of a crappy check. The Stiffness is used in an internal

+ 12 - 2
src/modules/physics/box2d/Physics.cpp

@@ -405,7 +405,12 @@ b2AABB Physics::scaleUp(const b2AABB &aabb)
 	return t;
 	return t;
 }
 }
 
 
-void Physics::b2LinearFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB)
+void Physics::computeLinearStiffness(float &stiffness, float &damping, float frequency, float dampingRatio, b2Body *bodyA, b2Body *bodyB)
+{
+	b2LinearStiffness(stiffness, damping, frequency, dampingRatio, bodyA, bodyB);
+}
+
+void Physics::computeLinearFrequency(float &frequency, float &ratio, float stiffness, float damping, b2Body *bodyA, b2Body *bodyB)
 {
 {
 	float massA = bodyA->GetMass();
 	float massA = bodyA->GetMass();
 	float massB = bodyB->GetMass();
 	float massB = bodyB->GetMass();
@@ -435,7 +440,12 @@ void Physics::b2LinearFrequency(float& frequency, float& ratio, float stiffness,
 	ratio = damping / (mass * 2.0f * omega);
 	ratio = damping / (mass * 2.0f * omega);
 }
 }
 
 
-void Physics::b2AngularFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB)
+void Physics::computeAngularStiffness(float &stiffness, float &damping, float frequency, float dampingRatio, b2Body *bodyA, b2Body *bodyB)
+{
+	b2AngularStiffness(stiffness, damping, frequency, dampingRatio, bodyA, bodyB);
+}
+
+void Physics::computeAngularFrequency(float &frequency, float &ratio, float stiffness, float damping, b2Body *bodyA, b2Body *bodyB)
 {
 {
 	float IA = bodyA->GetInertia();
 	float IA = bodyA->GetInertia();
 	float IB = bodyB->GetInertia();
 	float IB = bodyB->GetInertia();

+ 26 - 4
src/modules/physics/box2d/Physics.h

@@ -360,7 +360,18 @@ public:
 	static b2AABB scaleUp(const b2AABB& aabb);
 	static b2AABB scaleUp(const b2AABB& aabb);
 
 
 	/**
 	/**
-	 * Calculates linear frequency and damping radio from stiffness and damping
+	 * Calculates the stiffness and damping, given the linear frequency, and damping ratio.
+	 * @param stiffness The output stiffness
+	 * @param damping The output damping
+	 * @param frequency The joint linear frequency
+	 * @param dampingRatio The joint damping ratio
+	 * @param bodyA The bodyA of the joint
+	 * @param bodyB The bodyB of the joint
+	 **/
+	static void computeLinearStiffness(float& stiffness, float& damping, float frequency, float dampingRatio, b2Body* bodyA, b2Body* bodyB);
+
+	/**
+	 * Calculates linear frequency and damping ratio from stiffness and damping
 	 * @param frequency The output frequency
 	 * @param frequency The output frequency
 	 * @param ratio The output damping ratio
 	 * @param ratio The output damping ratio
 	 * @param stiffness The joint stiffness
 	 * @param stiffness The joint stiffness
@@ -368,10 +379,21 @@ public:
 	 * @param bodyA The bodyA of the joint
 	 * @param bodyA The bodyA of the joint
 	 * @param bodyB The bodyB of the joint
 	 * @param bodyB The bodyB of the joint
 	 **/
 	 **/
-	static void b2LinearFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB);
+	static void computeLinearFrequency(float &frequency, float &ratio, float stiffness, float damping, b2Body *bodyA, b2Body *bodyB);
+
+	/**
+	 * Calculates the stiffness and damping, given the angular frequency, and damping ratio.
+	 * @param stiffness The output stiffness
+	 * @param damping The output damping
+	 * @param frequency The joint angular frequency
+	 * @param dampingRatio The joint damping ratio
+	 * @param bodyA The bodyA of the joint
+	 * @param bodyB The bodyB of the joint
+	 **/
+	static void computeAngularStiffness(float &stiffness, float &damping, float frequency, float dampingRatio, b2Body *bodyA, b2Body *bodyB);
 
 
 	/**
 	/**
-	 * Calculates angular frequency and damping radio from stiffness and damping
+	 * Calculates angular frequency and damping ratio from stiffness and damping
 	 * @param frequency The output frequency
 	 * @param frequency The output frequency
 	 * @param ratio The output damping ratio
 	 * @param ratio The output damping ratio
 	 * @param stiffness The joint stiffness
 	 * @param stiffness The joint stiffness
@@ -379,7 +401,7 @@ public:
 	 * @param bodyA The bodyA of the joint
 	 * @param bodyA The bodyA of the joint
 	 * @param bodyB The bodyB of the joint
 	 * @param bodyB The bodyB of the joint
 	 **/
 	 **/
-	static void b2AngularFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB);
+	static void computeAngularFrequency(float &frequency, float &ratio, float stiffness, float damping, b2Body *bodyA, b2Body *bodyB);
 
 
 private:
 private:
 
 

+ 0 - 28
src/modules/physics/box2d/WeldJoint.cpp

@@ -66,34 +66,6 @@ void WeldJoint::init(b2WeldJointDef &def, Body *body1, Body *body2, float xA, fl
 	def.collideConnected = collideConnected;
 	def.collideConnected = collideConnected;
 }
 }
 
 
-void WeldJoint::setFrequency(float hz)
-{
-	float stiffness, damping;
-	b2LinearStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
-	joint->SetStiffness(stiffness);
-}
-
-float WeldJoint::getFrequency() const
-{
-	float frequency, ratio;
-	Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
-	return frequency;
-}
-
-void WeldJoint::setDampingRatio(float ratio)
-{
-	float stiffness, damping;
-	b2LinearStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
-	joint->SetDamping(damping);
-}
-
-float WeldJoint::getDampingRatio() const
-{
-	float frequency, ratio;
-	Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
-	return ratio;
-}
-
 void WeldJoint::setStiffness(float k)
 void WeldJoint::setStiffness(float k)
 {
 {
 	joint->SetStiffness(k);
 	joint->SetStiffness(k);

+ 0 - 20
src/modules/physics/box2d/WeldJoint.h

@@ -49,26 +49,6 @@ public:
 
 
 	virtual ~WeldJoint();
 	virtual ~WeldJoint();
 
 
-	/**
-	 * Sets the response speed. Independent of mass
-	 **/
-	void setFrequency(float hz);
-
-	/**
-	 * Gets the response speed. Independent of mass
-	 **/
-	float getFrequency() const;
-
-	/**
-	 * Set the spring damping ratio. Independent of mass
-	 **/
-	void setDampingRatio(float ratio);
-
-	/**
-	 * Get the spring damping ratio. Independent of mass
-	 **/
-	float getDampingRatio() const;
-
 	/**
 	/**
 	 * Sets the response speed. Dependent of mass
 	 * Sets the response speed. Dependent of mass
 	 **/
 	 **/

+ 0 - 28
src/modules/physics/box2d/WheelJoint.cpp

@@ -95,34 +95,6 @@ float WheelJoint::getMotorTorque(float inv_dt) const
 	return Physics::scaleUp(Physics::scaleUp(joint->GetMotorTorque(inv_dt)));
 	return Physics::scaleUp(Physics::scaleUp(joint->GetMotorTorque(inv_dt)));
 }
 }
 
 
-void WheelJoint::setFrequency(float hz)
-{
-	float stiffness, damping;
-	b2AngularStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
-	joint->SetStiffness(stiffness);
-}
-
-float WheelJoint::getFrequency() const
-{
-	float frequency, ratio;
-	Physics::b2AngularFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
-	return frequency;
-}
-
-void WheelJoint::setDampingRatio(float ratio)
-{
-	float stiffness, damping;
-	b2AngularStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
-	joint->SetDamping(damping);
-}
-
-float WheelJoint::getDampingRatio() const
-{
-	float frequency, ratio;
-	Physics::b2AngularFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
-	return ratio;
-}
-
 void WheelJoint::setStiffness(float k)
 void WheelJoint::setStiffness(float k)
 {
 {
 	joint->SetStiffness(k);
 	joint->SetStiffness(k);

+ 0 - 20
src/modules/physics/box2d/WheelJoint.h

@@ -95,26 +95,6 @@ public:
 	 **/
 	 **/
 	float getMotorTorque(float inv_dt) const;
 	float getMotorTorque(float inv_dt) const;
 
 
-	/**
-	 * Sets the response speed. Independent of mass
-	 **/
-	void setFrequency(float hz);
-
-	/**
-	 * Gets the response speed. Independent of mass
-	 **/
-	float getFrequency() const;
-
-	/**
-	 * Set the spring damping ratio. Independent of mass
-	 **/
-	void setDampingRatio(float ratio);
-
-	/**
-	 * Get the spring damping ratio. Independent of mass
-	 **/
-	float getDampingRatio() const;
-
 	/**
 	/**
 	 * Sets the response speed. Dependent of mass
 	 * Sets the response speed. Dependent of mass
 	 **/
 	 **/

+ 0 - 34
src/modules/physics/box2d/wrap_DistanceJoint.cpp

@@ -50,36 +50,6 @@ int w_DistanceJoint_getLength(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_DistanceJoint_setFrequency(lua_State *L)
-{
-	DistanceJoint *t = luax_checkdistancejoint(L, 1);
-	float arg1 = (float)luaL_checknumber(L, 2);
-	t->setFrequency(arg1);
-	return 0;
-}
-
-int w_DistanceJoint_getFrequency(lua_State *L)
-{
-	DistanceJoint *t = luax_checkdistancejoint(L, 1);
-	lua_pushnumber(L, t->getFrequency());
-	return 1;
-}
-
-int w_DistanceJoint_setDampingRatio(lua_State *L)
-{
-	DistanceJoint *t = luax_checkdistancejoint(L, 1);
-	float arg1 = (float)luaL_checknumber(L, 2);
-	t->setDampingRatio(arg1);
-	return 0;
-}
-
-int w_DistanceJoint_getDampingRatio(lua_State *L)
-{
-	DistanceJoint *t = luax_checkdistancejoint(L, 1);
-	lua_pushnumber(L, t->getDampingRatio());
-	return 1;
-}
-
 int w_DistanceJoint_setStiffness(lua_State *L)
 int w_DistanceJoint_setStiffness(lua_State *L)
 {
 {
 	DistanceJoint *t = luax_checkdistancejoint(L, 1);
 	DistanceJoint *t = luax_checkdistancejoint(L, 1);
@@ -114,10 +84,6 @@ static const luaL_Reg w_DistanceJoint_functions[] =
 {
 {
 	{ "setLength", w_DistanceJoint_setLength },
 	{ "setLength", w_DistanceJoint_setLength },
 	{ "getLength", w_DistanceJoint_getLength },
 	{ "getLength", w_DistanceJoint_getLength },
-	{ "setFrequency", w_DistanceJoint_setFrequency },
-	{ "getFrequency", w_DistanceJoint_getFrequency },
-	{ "setDampingRatio", w_DistanceJoint_setDampingRatio },
-	{ "getDampingRatio", w_DistanceJoint_getDampingRatio },
 	{ "setStiffness", w_DistanceJoint_setStiffness },
 	{ "setStiffness", w_DistanceJoint_setStiffness },
 	{ "getStiffness", w_DistanceJoint_getStiffness },
 	{ "getStiffness", w_DistanceJoint_getStiffness },
 	{ "setDamping", w_DistanceJoint_setDamping },
 	{ "setDamping", w_DistanceJoint_setDamping },

+ 0 - 34
src/modules/physics/box2d/wrap_MouseJoint.cpp

@@ -66,36 +66,6 @@ int w_MouseJoint_getMaxForce(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_MouseJoint_setFrequency(lua_State *L)
-{
-	MouseJoint *t = luax_checkmousejoint(L, 1);
-	float arg1 = (float)luaL_checknumber(L, 2);
-	luax_catchexcept(L, [&]() { t->setFrequency(arg1); });
-	return 0;
-}
-
-int w_MouseJoint_getFrequency(lua_State *L)
-{
-	MouseJoint *t = luax_checkmousejoint(L, 1);
-	lua_pushnumber(L, t->getFrequency());
-	return 1;
-}
-
-int w_MouseJoint_setDampingRatio(lua_State *L)
-{
-	MouseJoint *t = luax_checkmousejoint(L, 1);
-	float arg1 = (float)luaL_checknumber(L, 2);
-	t->setDampingRatio(arg1);
-	return 0;
-}
-
-int w_MouseJoint_getDampingRatio(lua_State *L)
-{
-	MouseJoint *t = luax_checkmousejoint(L, 1);
-	lua_pushnumber(L, t->getDampingRatio());
-	return 1;
-}
-
 int w_MouseJoint_setStiffness(lua_State *L)
 int w_MouseJoint_setStiffness(lua_State *L)
 {
 {
 	MouseJoint *t = luax_checkmousejoint(L, 1);
 	MouseJoint *t = luax_checkmousejoint(L, 1);
@@ -132,10 +102,6 @@ static const luaL_Reg w_MouseJoint_functions[] =
 	{ "getTarget", w_MouseJoint_getTarget },
 	{ "getTarget", w_MouseJoint_getTarget },
 	{ "setMaxForce", w_MouseJoint_setMaxForce },
 	{ "setMaxForce", w_MouseJoint_setMaxForce },
 	{ "getMaxForce", w_MouseJoint_getMaxForce },
 	{ "getMaxForce", w_MouseJoint_getMaxForce },
-	{ "setFrequency", w_MouseJoint_setFrequency },
-	{ "getFrequency", w_MouseJoint_getFrequency },
-	{ "setDampingRatio", w_MouseJoint_setDampingRatio },
-	{ "getDampingRatio", w_MouseJoint_getDampingRatio },
 	{ "setStiffness", w_MouseJoint_setStiffness },
 	{ "setStiffness", w_MouseJoint_setStiffness },
 	{ "getStiffness", w_MouseJoint_getStiffness },
 	{ "getStiffness", w_MouseJoint_getStiffness },
 	{ "setDamping", w_MouseJoint_setDamping },
 	{ "setDamping", w_MouseJoint_setDamping },

+ 89 - 0
src/modules/physics/box2d/wrap_Physics.cpp

@@ -477,12 +477,97 @@ int w_setMeter(lua_State *L)
 	return 0;
 	return 0;
 
 
 }
 }
+
 int w_getMeter(lua_State *L)
 int w_getMeter(lua_State *L)
 {
 {
 	lua_pushinteger(L, Physics::getMeter());
 	lua_pushinteger(L, Physics::getMeter());
 	return 1;
 	return 1;
 }
 }
 
 
+int w_computeLinearStiffness(lua_State *L)
+{
+	float frequency = (float)luaL_checknumber(L, 1);
+	float dampingRatio = (float)luaL_checknumber(L, 2);
+	Body *body1 = luax_checkbody(L, 3);
+	b2Body *other = 0;
+
+	if (lua_isnoneornil(L, 4))
+		other = body1->getWorld()->getGroundBody();
+	else
+		other = luax_checkbody(L, 4)->body;
+
+	float stiffness, damping;
+	Physics::computeLinearStiffness(stiffness, damping, frequency, dampingRatio, body1->body, other);
+
+	lua_pushnumber(L, stiffness);
+	lua_pushnumber(L, damping);
+
+	return 2;
+}
+
+int w_computeLinearFrequency(lua_State *L)
+{
+	float stiffness = (float)luaL_checknumber(L, 1);
+	float damping = (float)luaL_checknumber(L, 2);
+	Body *body1 = luax_checkbody(L, 3);
+	b2Body *other = 0;
+
+	if (lua_isnoneornil(L, 4))
+		other = body1->getWorld()->getGroundBody();
+	else
+		other = luax_checkbody(L, 4)->body;
+
+	float frequency, dampingRatio;
+	Physics::computeLinearFrequency(frequency, dampingRatio, stiffness, damping, body1->body, other);
+
+	lua_pushnumber(L, frequency);
+	lua_pushnumber(L, dampingRatio);
+
+	return 2;
+}
+
+int w_computeAngularStiffness(lua_State *L)
+{
+	float frequency = (float)luaL_checknumber(L, 1);
+	float dampingRatio = (float)luaL_checknumber(L, 2);
+	Body *body1 = luax_checkbody(L, 3);
+	b2Body *other = 0;
+
+	if (lua_isnoneornil(L, 4))
+		other = body1->getWorld()->getGroundBody();
+	else
+		other = luax_checkbody(L, 4)->body;
+
+	float stiffness, damping;
+	Physics::computeAngularStiffness(stiffness, damping, frequency, dampingRatio, body1->body, other);
+
+	lua_pushnumber(L, stiffness);
+	lua_pushnumber(L, damping);
+
+	return 2;
+}
+
+int w_computeAngularFrequency(lua_State *L)
+{
+	float stiffness = (float)luaL_checknumber(L, 1);
+	float damping = (float)luaL_checknumber(L, 2);
+	Body *body1 = luax_checkbody(L, 3);
+	b2Body *other = 0;
+
+	if (lua_isnoneornil(L, 4))
+		other = body1->getWorld()->getGroundBody();
+	else
+		other = luax_checkbody(L, 4)->body;
+
+	float frequency, dampingRatio;
+	Physics::computeAngularFrequency(frequency, dampingRatio, stiffness, damping, body1->body, other);
+
+	lua_pushnumber(L, frequency);
+	lua_pushnumber(L, dampingRatio);
+
+	return 2;
+}
+
 // List of functions to wrap.
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 static const luaL_Reg functions[] =
 {
 {
@@ -508,6 +593,10 @@ static const luaL_Reg functions[] =
 	{ "getDistance", w_getDistance },
 	{ "getDistance", w_getDistance },
 	{ "getMeter", w_getMeter },
 	{ "getMeter", w_getMeter },
 	{ "setMeter", w_setMeter },
 	{ "setMeter", w_setMeter },
+	{ "computeLinearStiffness", w_computeLinearStiffness },
+	{ "computeLinearFrequency", w_computeLinearFrequency },
+	{ "computeAngularStiffness", w_computeAngularStiffness },
+	{ "computeAngularFrequency", w_computeAngularFrequency },
 	{ 0, 0 },
 	{ 0, 0 },
 };
 };
 
 

+ 0 - 34
src/modules/physics/box2d/wrap_WeldJoint.cpp

@@ -35,36 +35,6 @@ WeldJoint *luax_checkweldjoint(lua_State *L, int idx)
 	return j;
 	return j;
 }
 }
 
 
-int w_WeldJoint_setFrequency(lua_State *L)
-{
-	WeldJoint *t = luax_checkweldjoint(L, 1);
-	float arg1 = (float)luaL_checknumber(L, 2);
-	t->setFrequency(arg1);
-	return 0;
-}
-
-int w_WeldJoint_getFrequency(lua_State *L)
-{
-	WeldJoint *t = luax_checkweldjoint(L, 1);
-	lua_pushnumber(L, t->getFrequency());
-	return 1;
-}
-
-int w_WeldJoint_setDampingRatio(lua_State *L)
-{
-	WeldJoint *t = luax_checkweldjoint(L, 1);
-	float arg1 = (float)luaL_checknumber(L, 2);
-	t->setDampingRatio(arg1);
-	return 0;
-}
-
-int w_WeldJoint_getDampingRatio(lua_State *L)
-{
-	WeldJoint *t = luax_checkweldjoint(L, 1);
-	lua_pushnumber(L, t->getDampingRatio());
-	return 1;
-}
-
 int w_WeldJoint_setStiffness(lua_State *L)
 int w_WeldJoint_setStiffness(lua_State *L)
 {
 {
 	WeldJoint *t = luax_checkweldjoint(L, 1);
 	WeldJoint *t = luax_checkweldjoint(L, 1);
@@ -104,10 +74,6 @@ int w_WeldJoint_getReferenceAngle(lua_State *L)
 
 
 static const luaL_Reg w_WeldJoint_functions[] =
 static const luaL_Reg w_WeldJoint_functions[] =
 {
 {
-	{ "setFrequency", w_WeldJoint_setFrequency },
-	{ "getFrequency", w_WeldJoint_getFrequency },
-	{ "setDampingRatio", w_WeldJoint_setDampingRatio },
-	{ "getDampingRatio", w_WeldJoint_getDampingRatio },
 	{ "setStiffness", w_WeldJoint_setStiffness },
 	{ "setStiffness", w_WeldJoint_setStiffness },
 	{ "getStiffness", w_WeldJoint_getStiffness },
 	{ "getStiffness", w_WeldJoint_getStiffness },
 	{ "setDamping", w_WeldJoint_setDamping },
 	{ "setDamping", w_WeldJoint_setDamping },

+ 0 - 38
src/modules/physics/box2d/wrap_WheelJoint.cpp

@@ -102,36 +102,6 @@ int w_WheelJoint_getMotorTorque(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_WheelJoint_setFrequency(lua_State *L)
-{
-	WheelJoint *t = luax_checkwheeljoint(L, 1);
-	float arg1 = (float)luaL_checknumber(L, 2);
-	t->setFrequency(arg1);
-	return 0;
-}
-
-int w_WheelJoint_getFrequency(lua_State *L)
-{
-	WheelJoint *t = luax_checkwheeljoint(L, 1);
-	lua_pushnumber(L, t->getFrequency());
-	return 1;
-}
-
-int w_WheelJoint_setDampingRatio(lua_State *L)
-{
-	WheelJoint *t = luax_checkwheeljoint(L, 1);
-	float arg1 = (float)luaL_checknumber(L, 2);
-	t->setDampingRatio(arg1);
-	return 0;
-}
-
-int w_WheelJoint_getDampingRatio(lua_State *L)
-{
-	WheelJoint *t = luax_checkwheeljoint(L, 1);
-	lua_pushnumber(L, t->getDampingRatio());
-	return 1;
-}
-
 int w_WheelJoint_setStiffness(lua_State *L)
 int w_WheelJoint_setStiffness(lua_State *L)
 {
 {
 	WheelJoint *t = luax_checkwheeljoint(L, 1);
 	WheelJoint *t = luax_checkwheeljoint(L, 1);
@@ -180,18 +150,10 @@ static const luaL_Reg w_WheelJoint_functions[] =
 	{ "setMaxMotorTorque", w_WheelJoint_setMaxMotorTorque },
 	{ "setMaxMotorTorque", w_WheelJoint_setMaxMotorTorque },
 	{ "getMaxMotorTorque", w_WheelJoint_getMaxMotorTorque },
 	{ "getMaxMotorTorque", w_WheelJoint_getMaxMotorTorque },
 	{ "getMotorTorque", w_WheelJoint_getMotorTorque },
 	{ "getMotorTorque", w_WheelJoint_getMotorTorque },
-	{ "setSpringFrequency", w_WheelJoint_setFrequency },
-	{ "getSpringFrequency", w_WheelJoint_getFrequency },
-	{ "setSpringDampingRatio", w_WheelJoint_setDampingRatio },
-	{ "getSpringDampingRatio", w_WheelJoint_getDampingRatio },
 	{ "setSpringStiffness", w_WheelJoint_setStiffness },
 	{ "setSpringStiffness", w_WheelJoint_setStiffness },
 	{ "getSpringStiffness", w_WheelJoint_getStiffness },
 	{ "getSpringStiffness", w_WheelJoint_getStiffness },
 	{ "setSpringDamping", w_WheelJoint_setDamping },
 	{ "setSpringDamping", w_WheelJoint_setDamping },
 	{ "getSpringDamping", w_WheelJoint_getDamping },
 	{ "getSpringDamping", w_WheelJoint_getDamping },
-	{ "setFrequency", w_WheelJoint_setFrequency },
-	{ "getFrequency", w_WheelJoint_getFrequency },
-	{ "setDampingRatio", w_WheelJoint_setDampingRatio },
-	{ "getDampingRatio", w_WheelJoint_getDampingRatio },
 	{ "setStiffness", w_WheelJoint_setStiffness },
 	{ "setStiffness", w_WheelJoint_setStiffness },
 	{ "getStiffness", w_WheelJoint_getStiffness },
 	{ "getStiffness", w_WheelJoint_getStiffness },
 	{ "setDamping", w_WheelJoint_setDamping },
 	{ "setDamping", w_WheelJoint_setDamping },

+ 37 - 0
src/modules/sensor/Sensor.cpp

@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+// LOVE
+#include "Sensor.h"
+
+namespace love
+{
+namespace sensor
+{
+
+STRINGMAP_CLASS_BEGIN(Sensor, Sensor::SensorType, Sensor::SENSOR_MAX_ENUM, sensorType)
+{
+	{ "accelerometer", Sensor::SENSOR_ACCELEROMETER },
+	{ "gyroscope",     Sensor::SENSOR_GYROSCOPE     },
+}
+STRINGMAP_CLASS_END(Sensor, Sensor::SensorType, Sensor::SENSOR_MAX_ENUM, sensorType)
+
+} // sensor
+} // love

+ 83 - 0
src/modules/sensor/Sensor.h

@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_SENSOR_H
+#define LOVE_SENSOR_H
+
+ // LOVE
+#include "common/Module.h"
+#include "common/StringMap.h"
+
+namespace love
+{
+namespace sensor
+{
+
+class Sensor: public Module
+{
+public:
+
+	enum SensorType
+	{
+		SENSOR_ACCELEROMETER,
+		SENSOR_GYROSCOPE,
+		SENSOR_MAX_ENUM
+	};
+
+	virtual ~Sensor() {}
+
+	// Implements Module.
+	ModuleType getModuleType() const override { return M_SENSOR; }
+
+	/**
+	 * Check the availability of the sensor.
+	 **/
+	virtual bool hasSensor(SensorType type) = 0;
+
+	/**
+	 * Check if the sensor is enabled.
+	 **/
+	virtual bool isEnabled(SensorType type) = 0;
+
+	/**
+	 * Enable or disable a sensor.
+	 **/
+	virtual void setEnabled(SensorType type, bool enabled) = 0;
+
+	/**
+	 * Get data from sensor.
+	 **/
+	virtual std::vector<float> getData(SensorType type) = 0;
+
+	/**
+	 * Get backend-dependent handle of sensor.
+	 **/
+	virtual std::vector<void *> getHandles() = 0;
+
+	virtual const char *getSensorName(SensorType type) = 0;
+
+	STRINGMAP_CLASS_DECLARE(SensorType);
+
+}; // Sensor
+
+} // sensor
+} // love
+
+#endif

+ 174 - 0
src/modules/sensor/sdl/Sensor.cpp

@@ -0,0 +1,174 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+// LOVE
+#include "Sensor.h"
+
+// SDL
+#include <SDL.h>
+#include <SDL_sensor.h>
+
+namespace love
+{
+namespace sensor
+{
+namespace sdl
+{
+
+Sensor::Sensor()
+: sensors()
+{
+	if (SDL_InitSubSystem(SDL_INIT_SENSOR) < 0)
+		throw love::Exception("Could not initialize SDL sensor subsystem (%s)", SDL_GetError());
+}
+
+Sensor::~Sensor()
+{
+	SDL_QuitSubSystem(SDL_INIT_SENSOR);
+}
+
+const char *Sensor::getName() const
+{
+	return "love.sensor.sdl";
+}
+
+bool Sensor::hasSensor(SensorType type)
+{
+	for (int i = 0; i < SDL_NumSensors(); i++)
+	{
+		if (convert(SDL_SensorGetDeviceType(i)) == type)
+			return true;
+	}
+
+	return false;
+}
+
+bool Sensor::isEnabled(SensorType type)
+{
+	return sensors[type]; // nullptr is default
+}
+
+void Sensor::setEnabled(SensorType type, bool enable)
+{
+	if (sensors[type] && !enable)
+	{
+		SDL_SensorClose(sensors[type]);
+		sensors[type] = nullptr;
+	}
+	else if (sensors[type] == nullptr && enable)
+	{
+		for (int i = 0; i < SDL_NumSensors(); i++)
+		{
+			if (convert(SDL_SensorGetDeviceType(i)) == type)
+			{
+				SDL_Sensor *sensorHandle = SDL_SensorOpen(i);
+
+				if (sensorHandle == nullptr)
+				{
+					const char *name = nullptr;
+					getConstant(type, name);
+
+					throw love::Exception("Could not open \"%s\" SDL sensor (%s)", name, SDL_GetError());
+				}
+
+				sensors[type] = sensorHandle;
+			}
+		}
+	}
+}
+
+std::vector<float> Sensor::getData(SensorType type)
+{
+	if (sensors[type] == nullptr)
+	{
+		const char *name = nullptr;
+		getConstant(type, name);
+
+		throw love::Exception("\"%s\" sensor is not enabled", name);
+	}
+
+	std::vector<float> values(3);
+
+	if (SDL_SensorGetData(sensors[type], values.data(), (int) values.size()) != 0)
+	{
+		const char *name = nullptr;
+		getConstant(type, name);
+
+		throw love::Exception("Could not get \"%s\" SDL sensor data (%s)", name, SDL_GetError());
+	}
+
+	return values;
+}
+
+std::vector<void*> Sensor::getHandles()
+{
+	std::vector<void*> nativeSensor;
+
+	for (const std::pair<SensorType, SDL_Sensor*> &data: sensors)
+	{
+		if (data.second)
+			nativeSensor.push_back(data.second);
+	}
+
+	return nativeSensor;
+}
+
+const char *Sensor::getSensorName(SensorType type)
+{
+	if (sensors[type] == nullptr)
+	{
+		const char *name = nullptr;
+		getConstant(type, name);
+
+		throw love::Exception("\"%s\" sensor is not enabled", name);
+	}
+
+	return SDL_SensorGetName(sensors[type]);
+}
+
+Sensor::SensorType Sensor::convert(SDL_SensorType type)
+{
+	switch (type)
+	{
+		case SDL_SENSOR_ACCEL:
+			return SENSOR_ACCELEROMETER;
+		case SDL_SENSOR_GYRO:
+			return SENSOR_GYROSCOPE;
+		default:
+			return SENSOR_MAX_ENUM;
+	}
+}
+
+SDL_SensorType Sensor::convert(Sensor::SensorType type)
+{
+	switch (type)
+	{
+		case SENSOR_ACCELEROMETER:
+			return SDL_SENSOR_ACCEL;
+		case SENSOR_GYROSCOPE:
+			return SDL_SENSOR_GYRO;
+		default:
+			return SDL_SENSOR_UNKNOWN;
+	}
+}
+
+}
+}
+}

+ 68 - 0
src/modules/sensor/sdl/Sensor.h

@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_SENSOR_SDL_SENSOR_H
+#define LOVE_SENSOR_SDL_SENSOR_H
+
+// LOVE
+#include "sensor/Sensor.h"
+
+// SDL
+#include <SDL_sensor.h>
+
+// std
+#include <map>
+
+namespace love
+{
+namespace sensor
+{
+namespace sdl
+{
+
+class Sensor : public love::sensor::Sensor
+{
+public:
+	Sensor();
+	~Sensor() override;
+
+	// Implements Module.
+	const char *getName() const override;
+
+	bool hasSensor(SensorType type) override;
+	bool isEnabled(SensorType type) override;
+	void setEnabled(SensorType type, bool enable) override;
+	std::vector<float> getData(SensorType type) override;
+	std::vector<void*> getHandles() override;
+	const char *getSensorName(SensorType type) override;
+
+	static SensorType convert(SDL_SensorType type);
+	static SDL_SensorType convert(SensorType type);
+
+private:
+	std::map<SensorType, SDL_Sensor*> sensors;
+
+}; // Sensor
+
+} // sdl
+} // sensor
+} // love
+
+#endif

+ 120 - 0
src/modules/sensor/wrap_Sensor.cpp

@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+ // LOVE
+#include "wrap_Sensor.h"
+#include "sdl/Sensor.h"
+
+namespace love
+{
+namespace sensor
+{
+
+#define instance() (Module::getInstance<Sensor>(Module::M_SENSOR))
+
+inline Sensor::SensorType luax_checksensortype(lua_State *L, int i)
+{
+	const char *sensorType = luaL_checkstring(L, i);
+	Sensor::SensorType type = Sensor::SENSOR_MAX_ENUM;
+
+	if (!Sensor::getConstant(sensorType, type))
+		luax_enumerror(L, "sensor mode", Sensor::getConstants(type), sensorType);
+
+	return type;
+}
+
+static int w_hasSensor(lua_State *L)
+{
+	Sensor::SensorType type = luax_checksensortype(L, 1);
+
+	lua_pushboolean(L, instance()->hasSensor(type));
+	return 1;
+}
+
+static int w_isEnabled(lua_State *L)
+{
+	Sensor::SensorType type = luax_checksensortype(L, 1);
+
+	lua_pushboolean(L, instance()->isEnabled(type));
+	return 1;
+}
+
+static int w_setEnabled(lua_State *L)
+{
+	Sensor::SensorType type = luax_checksensortype(L, 1);
+	bool enabled = luax_checkboolean(L, 2);
+
+	luax_catchexcept(L, [&](){ instance()->setEnabled(type, enabled); });
+	return 0;
+}
+
+static int w_getData(lua_State *L)
+{
+	Sensor::SensorType type = luax_checksensortype(L, 1);
+
+	std::vector<float> data;
+	luax_catchexcept(L, [&](){ data = instance()->getData(type); });
+
+	for (float f: data)
+		lua_pushnumber(L, f);
+
+	return (int) data.size();
+}
+
+static int w_getName(lua_State *L)
+{
+	Sensor::SensorType type = luax_checksensortype(L, 1);
+	const char *name = nullptr;
+
+	luax_catchexcept(L, [&](){ name = instance()->getSensorName(type); });
+	lua_pushstring(L, name);
+	return 1;
+}
+
+static const luaL_Reg functions[] =
+{
+	{ "hasSensor", w_hasSensor },
+	{ "isEnabled", w_isEnabled },
+	{ "setEnabled", w_setEnabled },
+	{ "getData", w_getData },
+	{ "getName", w_getName },
+	{ nullptr, nullptr }
+};
+
+extern "C" int luaopen_love_sensor(lua_State * L)
+{
+	Sensor *instance = instance();
+	if (instance == nullptr)
+		luax_catchexcept(L, [&]() { instance = new love::sensor::sdl::Sensor(); });
+	else
+		instance->retain();
+
+	WrappedModule w;
+	w.module = instance;
+	w.name = "sensor";
+	w.type = &Module::type;
+	w.functions = functions;
+	w.types = nullptr;
+
+	return luax_register_module(L, w);
+}
+
+} // sensor
+} // love

+ 37 - 0
src/modules/sensor/wrap_Sensor.h

@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_WRAP_SENSOR_H
+#define LOVE_WRAP_SENSOR_H
+
+ // LOVE
+#include "common/runtime.h"
+
+namespace love
+{
+namespace sensor
+{
+
+extern "C" LOVE_EXPORT int luaopen_love_sensor(lua_State *L);
+
+} // sensor
+} // love
+
+#endif

+ 20 - 33
src/modules/window/sdl/Window.cpp

@@ -46,6 +46,10 @@
 // SDL
 // SDL
 #include <SDL_syswm.h>
 #include <SDL_syswm.h>
 
 
+#ifdef LOVE_GRAPHICS_VULKAN
+#include <SDL_vulkan.h>
+#endif
+
 #if defined(LOVE_WINDOWS)
 #if defined(LOVE_WINDOWS)
 #include <windows.h>
 #include <windows.h>
 #include <dwmapi.h>
 #include <dwmapi.h>
@@ -91,7 +95,6 @@ Window::Window()
 	, metalView(nullptr)
 	, metalView(nullptr)
 #endif
 #endif
 	, displayedWindowError(false)
 	, displayedWindowError(false)
-	, hasSDL203orEarlier(false)
 	, contextAttribs()
 	, contextAttribs()
 {
 {
 	if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
 	if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
@@ -99,10 +102,6 @@ Window::Window()
 
 
 	// Make sure the screensaver doesn't activate by default.
 	// Make sure the screensaver doesn't activate by default.
 	setDisplaySleepEnabled(false);
 	setDisplaySleepEnabled(false);
-
-	SDL_version version = {};
-	SDL_GetVersion(&version);
-	hasSDL203orEarlier = (version.major == 2 && version.minor == 0 && version.patch <= 3);
 }
 }
 
 
 Window::~Window()
 Window::~Window()
@@ -140,16 +139,6 @@ void Window::setGLFramebufferAttributes(bool sRGB)
 
 
 	SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0);
 	SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0);
 
 
-	const char *driver = SDL_GetCurrentVideoDriver();
-	if (driver && strstr(driver, "x11") == driver)
-	{
-		// Always disable the sRGB flag when GLX is used with older SDL versions,
-		// because of this bug: https://bugzilla.libsdl.org/show_bug.cgi?id=2897
-		// In practice GLX will always give an sRGB-capable framebuffer anyway.
-		if (hasSDL203orEarlier)
-			SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 0);
-	}
-
 #if defined(LOVE_WINDOWS)
 #if defined(LOVE_WINDOWS)
 	// Avoid the Microsoft OpenGL 1.1 software renderer on Windows. Apparently
 	// Avoid the Microsoft OpenGL 1.1 software renderer on Windows. Apparently
 	// older Intel drivers like to use it as a fallback when requesting some
 	// older Intel drivers like to use it as a fallback when requesting some
@@ -250,14 +239,6 @@ std::vector<Window::ContextAttribs> Window::getContextAttribsList() const
 		if (curdriver && strstr(curdriver, glesdriver) == curdriver)
 		if (curdriver && strstr(curdriver, glesdriver) == curdriver)
 		{
 		{
 			preferGLES = true;
 			preferGLES = true;
-
-			// Prior to SDL 2.0.4, backends that use OpenGL ES didn't properly
-			// ask for a sRGB framebuffer when requested by SDL_GL_SetAttribute.
-			// This doesn't account for windowing backends that sometimes use
-			// EGL, e.g. the X11 and windows SDL backends.
-			if (hasSDL203orEarlier)
-				graphics::setGammaCorrect(false);
-
 			break;
 			break;
 		}
 		}
 	}
 	}
@@ -588,12 +569,8 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 		if (!f.fullscreen)
 		if (!f.fullscreen)
 			SDL_SetWindowSize(window, width, height);
 			SDL_SetWindowSize(window, width, height);
 
 
-		// On linux systems 2.0.5+ might not be available...
-		// TODO: require at least 2.0.5?
-#if SDL_VERSION_ATLEAST(2, 0, 5)
 		if (this->settings.resizable != f.resizable)
 		if (this->settings.resizable != f.resizable)
 			SDL_SetWindowResizable(window, f.resizable ? SDL_TRUE : SDL_FALSE);
 			SDL_SetWindowResizable(window, f.resizable ? SDL_TRUE : SDL_FALSE);
-#endif
 
 
 		if (this->settings.borderless != f.borderless)
 		if (this->settings.borderless != f.borderless)
 			SDL_SetWindowBordered(window, f.borderless ? SDL_FALSE : SDL_TRUE);
 			SDL_SetWindowBordered(window, f.borderless ? SDL_FALSE : SDL_TRUE);
@@ -688,11 +665,16 @@ bool Window::onSizeChanged(int width, int height)
 	windowWidth = width;
 	windowWidth = width;
 	windowHeight = height;
 	windowHeight = height;
 
 
+	// TODO: Use SDL_GetWindowSizeInPixels here when supported.
 	if (glcontext != nullptr)
 	if (glcontext != nullptr)
 		SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 		SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 #ifdef LOVE_GRAPHICS_METAL
 #ifdef LOVE_GRAPHICS_METAL
 	else if (metalView != nullptr)
 	else if (metalView != nullptr)
 		SDL_Metal_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 		SDL_Metal_GetDrawableSize(window, &pixelWidth, &pixelHeight);
+#endif
+#ifdef LOVE_GRAPHICS_VULKAN
+	else if (windowRenderer == graphics::RENDERER_VULKAN)
+		SDL_Vulkan_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 #endif
 #endif
 	else
 	else
 	{
 	{
@@ -720,12 +702,17 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi
 	pixelWidth = windowWidth;
 	pixelWidth = windowWidth;
 	pixelHeight = windowHeight;
 	pixelHeight = windowHeight;
 
 
+	// TODO: Use SDL_GetWindowSizeInPixels here when supported.
 	if ((wflags & SDL_WINDOW_OPENGL) != 0)
 	if ((wflags & SDL_WINDOW_OPENGL) != 0)
 		SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 		SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 #ifdef LOVE_GRAPHICS_METAL
 #ifdef LOVE_GRAPHICS_METAL
 	else if ((wflags & SDL_WINDOW_METAL) != 0)
 	else if ((wflags & SDL_WINDOW_METAL) != 0)
 		SDL_Metal_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 		SDL_Metal_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 #endif
 #endif
+#ifdef LOVE_GRAPHICS_VULKAN
+	else if ((wflags & SDL_WINDOW_VULKAN) != 0)
+		SDL_Vulkan_GetDrawableSize(window, &pixelWidth, &pixelHeight);
+#endif
 
 
 	if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
 	if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
 	{
 	{
@@ -909,9 +896,6 @@ const char *Window::getDisplayName(int displayindex) const
 
 
 Window::DisplayOrientation Window::getDisplayOrientation(int displayindex) const
 Window::DisplayOrientation Window::getDisplayOrientation(int displayindex) const
 {
 {
-	// TODO: We can expose this everywhere, we just need to watch out for the
-	// SDL binary being older than the headers on Linux.
-#if SDL_VERSION_ATLEAST(2, 0, 9) && (defined(LOVE_ANDROID) || !defined(LOVE_LINUX))
 	switch (SDL_GetDisplayOrientation(displayindex))
 	switch (SDL_GetDisplayOrientation(displayindex))
 	{
 	{
 		case SDL_ORIENTATION_UNKNOWN: return ORIENTATION_UNKNOWN;
 		case SDL_ORIENTATION_UNKNOWN: return ORIENTATION_UNKNOWN;
@@ -920,9 +904,6 @@ Window::DisplayOrientation Window::getDisplayOrientation(int displayindex) const
 		case SDL_ORIENTATION_PORTRAIT: return ORIENTATION_PORTRAIT;
 		case SDL_ORIENTATION_PORTRAIT: return ORIENTATION_PORTRAIT;
 		case SDL_ORIENTATION_PORTRAIT_FLIPPED: return ORIENTATION_PORTRAIT_FLIPPED;
 		case SDL_ORIENTATION_PORTRAIT_FLIPPED: return ORIENTATION_PORTRAIT_FLIPPED;
 	}
 	}
-#else
-	LOVE_UNUSED(displayindex);
-#endif
 
 
 	return ORIENTATION_UNKNOWN;
 	return ORIENTATION_UNKNOWN;
 }
 }
@@ -1119,6 +1100,7 @@ void Window::setVSync(int vsync)
 	}
 	}
 
 
 #ifdef LOVE_GRAPHICS_VULKAN
 #ifdef LOVE_GRAPHICS_VULKAN
+	// TODO: this doesn't update the swap-chain, but it should.
 	love::graphics::vulkan::Vulkan::setVsync(vsync);
 	love::graphics::vulkan::Vulkan::setVsync(vsync);
 #endif
 #endif
 
 
@@ -1148,6 +1130,11 @@ int Window::getVSync() const
 	}
 	}
 #endif
 #endif
 
 
+#ifdef LOVE_GRAPHICS_VULKAN
+	if (windowRenderer == love::graphics::RENDERER_VULKAN)
+		return love::graphics::vulkan::Vulkan::getVsync();
+#endif
+
 	return 0;
 	return 0;
 }
 }
 
 

+ 0 - 1
src/modules/window/sdl/Window.h

@@ -175,7 +175,6 @@ private:
 	graphics::Renderer windowRenderer = graphics::RENDERER_NONE;
 	graphics::Renderer windowRenderer = graphics::RENDERER_NONE;
 
 
 	bool displayedWindowError;
 	bool displayedWindowError;
-	bool hasSDL203orEarlier;
 	ContextAttribs contextAttribs;
 	ContextAttribs contextAttribs;
 
 
 	StrongRef<graphics::Graphics> graphics;
 	StrongRef<graphics::Graphics> graphics;