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(OpenAL REQUIRED)
 	find_package(OpenGL REQUIRED)
-	find_package(SDL2 REQUIRED)
+	find_package(SDL2 2.0.9 REQUIRED)
 	find_package(Theora REQUIRED)
 	find_package(Vorbis REQUIRED)
 	find_package(ZLIB REQUIRED)
@@ -540,8 +540,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/SpriteBatch.h
 	src/modules/graphics/StreamBuffer.cpp
 	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.h
 	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_Texture.cpp
 	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.h
 )
@@ -907,6 +907,30 @@ set(LOVE_SRC_MODULE_PHYSICS
 source_group("modules\\physics" FILES ${LOVE_SRC_MODULE_PHYSICS_ROOT})
 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
 #
@@ -1881,6 +1905,7 @@ set(LOVE_LIB_SRC
 	${LOVE_SRC_MODULE_MATH}
 	${LOVE_SRC_MODULE_MOUSE}
 	${LOVE_SRC_MODULE_PHYSICS}
+	${LOVE_SRC_MODULE_SENSOR}
 	${LOVE_SRC_MODULE_SOUND}
 	${LOVE_SRC_MODULE_SYSTEM}
 	${LOVE_SRC_MODULE_THREAD}

+ 9 - 0
changes.txt

@@ -25,6 +25,10 @@ Released: N/A
 * Added love.keyboard.isModifierActive.
 * Added Joystick:setPlayerIndex and Joystick:getPlayerIndex.
 * 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 World:getFixturesInArea().
 * 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 new 'clampone' wrap mode.
 * 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 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 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 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.
 
@@ -86,6 +94,7 @@ Released: N/A
 * Deprecated love.math.noise (replaced by perlinNoise and simplexNoise).
 * 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.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 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
 	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
 	Website: http://glad.dav1d.de/
 	License: MIT/Expat
@@ -199,6 +204,172 @@ MIT/Expat
 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 	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
 	All rights reserved.
 

+ 1 - 1
platform/unix/deps.m4

@@ -21,7 +21,7 @@ AC_DEFUN([ACLOVE_DEP_LIBM], [
 
 AC_DEFUN([ACLOVE_DEP_SDL2], [
 	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])], [])])
 
 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 */; };
 		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 */; };
+		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 */; };
 		FACFB753276D7F860089F78D /* Lua.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FACFB752276D7F6F0089F78D /* Lua.xcframework */; };
 		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 */; };
 		FADF53F91E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
 		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 */; };
 		FADF54081E3D78F700012CC0 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF54051E3D78F700012CC0 /* Video.cpp */; };
 		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>"; };
 		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>"; };
+		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>"; };
 		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>"; };
@@ -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; };
 		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>"; };
-		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>"; };
 		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>"; };
@@ -2660,6 +2675,7 @@
 				FA0B7C001A95902C000E1D17 /* math */,
 				FA0B7C0D1A95902C000E1D17 /* mouse */,
 				FA0B7C1B1A95902C000E1D17 /* physics */,
+				FACA06A4293EE5CD001A2557 /* sensor */,
 				FA0B7C7B1A95902C000E1D17 /* sound */,
 				FA0B7C9A1A95902C000E1D17 /* system */,
 				FA0B7CA21A95902C000E1D17 /* thread */,
@@ -2860,8 +2876,8 @@
 				FADF542E1E3DABF600012CC0 /* SpriteBatch.h */,
 				FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */,
 				FA2AF6721DAD62710032B62C /* StreamBuffer.h */,
-				FADF53FB1E3D74F200012CC0 /* Text.cpp */,
-				FADF53FC1E3D74F200012CC0 /* Text.h */,
+				FADF53FB1E3D74F200012CC0 /* TextBatch.cpp */,
+				FADF53FC1E3D74F200012CC0 /* TextBatch.h */,
 				FA0B7BBE1A95902C000E1D17 /* Texture.cpp */,
 				FA0B7BBF1A95902C000E1D17 /* Texture.h */,
 				FA2AF6731DAD64970032B62C /* vertex.cpp */,
@@ -2889,8 +2905,8 @@
 				FA1BA0B61E17043400AA2803 /* wrap_Shader.h */,
 				FADF54321E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp */,
 				FADF54331E3DAE6E00012CC0 /* wrap_SpriteBatch.h */,
-				FADF54001E3D77B500012CC0 /* wrap_Text.cpp */,
-				FADF54011E3D77B500012CC0 /* wrap_Text.h */,
+				FADF54001E3D77B500012CC0 /* wrap_TextBatch.cpp */,
+				FADF54011E3D77B500012CC0 /* wrap_TextBatch.h */,
 				FA620A301AA2F8DB005DB4C2 /* wrap_Texture.cpp */,
 				FA620A311AA2F8DB005DB4C2 /* wrap_Texture.h */,
 				FADF540A1E3D7CDD00012CC0 /* wrap_Video.cpp */,
@@ -3807,6 +3823,27 @@
 			path = data;
 			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 */ = {
 			isa = PBXGroup;
 			children = (
@@ -4010,6 +4047,7 @@
 				FA0B7D4A1A95902C000E1D17 /* Polyline.h in Headers */,
 				FABDA9E72552448300B5C523 /* b2_chain_shape.h in Headers */,
 				FAF1405C1E20934C00F898D2 /* InitializeGlobals.h in Headers */,
+				FACA06B4293EE5CD001A2557 /* wrap_Sensor.h in Headers */,
 				FA0B7DB31A95902C000E1D17 /* wrap_Image.h in Headers */,
 				FAF140591E20934C00F898D2 /* Common.h in Headers */,
 				FA0B7EC31A95902C000E1D17 /* threads.h in Headers */,
@@ -4210,7 +4248,7 @@
 				FABDA9EA2552448300B5C523 /* b2_world_callbacks.h in Headers */,
 				FA0B7E7D1A95902C000E1D17 /* wrap_World.h in Headers */,
 				FA0B7EBD1A95902C000E1D17 /* LuaThread.h in Headers */,
-				FADF53FF1E3D74F200012CC0 /* Text.h in Headers */,
+				FADF53FF1E3D74F200012CC0 /* TextBatch.h in Headers */,
 				FA0B7DC01A95902C000E1D17 /* JoystickModule.h in Headers */,
 				FA18CF3923DCF67900263725 /* spirv_msl.hpp in Headers */,
 				FA0B7E871A95902C000E1D17 /* CoreAudioDecoder.h in Headers */,
@@ -4299,6 +4337,7 @@
 				FA0B7DC31A95902C000E1D17 /* wrap_Joystick.h in Headers */,
 				FA18CF3B23DCF67900263725 /* spirv_cross_util.hpp in Headers */,
 				FAF140631E20934C00F898D2 /* Types.h in Headers */,
+				FACA06AF293EE5CD001A2557 /* Sensor.h in Headers */,
 				FA4F2BE61DE6650600CA37D7 /* wrap_Transform.h in Headers */,
 				FA0B7EE71A95902D000E1D17 /* Window.h in Headers */,
 				FA0B7E651A95902C000E1D17 /* wrap_PolygonShape.h in Headers */,
@@ -4359,6 +4398,7 @@
 				FA0B7E931A95902C000E1D17 /* ModPlugDecoder.h in Headers */,
 				FA0B7D111A95902C000E1D17 /* BMFontRasterizer.h in Headers */,
 				217DFBE61D9F6D490055D849 /* http.lua.h in Headers */,
+				FACA06AE293EE5CD001A2557 /* Sensor.h in Headers */,
 				FA18CF1D23DCF67900263725 /* sampler.hpp in Headers */,
 				FA0B7E3E1A95902C000E1D17 /* wrap_Body.h in Headers */,
 				FABDA9C92552448300B5C523 /* b2_distance.h in Headers */,
@@ -4443,7 +4483,7 @@
 				FABDA9CF2552448300B5C523 /* b2_mouse_joint.h in Headers */,
 				FABDA9E42552448300B5C523 /* b2_collision.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 */,
 				FAB922C6257D99EF0035DAD6 /* Range.h in Headers */,
 				FAC756F61E4F99B400B91289 /* Effect.h in Headers */,
@@ -4613,6 +4653,7 @@
 				FAE64A952071365100BC7981 /* physfs_platform_qnx.c in Sources */,
 				FABDA9FC2552448300B5C523 /* b2_collide_polygon.cpp in Sources */,
 				FABDA9D12552448300B5C523 /* b2_draw.cpp in Sources */,
+				FACA06B3293EE5CD001A2557 /* Sensor.cpp in Sources */,
 				FA0B7EA41A95902C000E1D17 /* SoundData.cpp in Sources */,
 				FAF1406A1E20934C00F898D2 /* glslang_tab.cpp in Sources */,
 				FA8951A31AA2EDF300EC385A /* wrap_Event.cpp in Sources */,
@@ -4644,7 +4685,7 @@
 				FA1BA0A31E16D97500AA2803 /* wrap_Font.cpp in Sources */,
 				FABDA9842552448200B5C523 /* b2_chain_polygon_contact.cpp in Sources */,
 				FA0B7E0D1A95902C000E1D17 /* Fixture.cpp in Sources */,
-				FADF53FE1E3D74F200012CC0 /* Text.cpp in Sources */,
+				FADF53FE1E3D74F200012CC0 /* TextBatch.cpp in Sources */,
 				FA0B7D191A95902C000E1D17 /* TrueTypeRasterizer.cpp in Sources */,
 				FAC271E723B5B5B400C200D3 /* renderstate.cpp in Sources */,
 				FA84DE6727791C36002674C6 /* GraphicsReadback.cpp in Sources */,
@@ -4664,7 +4705,7 @@
 				FA0B7CE61A95902C000E1D17 /* wrap_Source.cpp in Sources */,
 				FA9D8DDE1DEF842A002CD881 /* Drawable.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 */,
 				FA0B7DFB1A95902C000E1D17 /* Body.cpp in Sources */,
 				FA0B7ED21A95902C000E1D17 /* wrap_ThreadModule.cpp in Sources */,
@@ -4688,6 +4729,7 @@
 				FA0B7AD51A958EA3000E1D17 /* unix.c in Sources */,
 				FAB17BE71ABFAA9000F9BA27 /* lz4.c in Sources */,
 				FAE64A902071364800BC7981 /* physfs_unicode.c in Sources */,
+				FACA06B1293EE5CD001A2557 /* Sensor.cpp in Sources */,
 				FA4F2BE71DE6650D00CA37D7 /* Transform.cpp in Sources */,
 				FAF140A11E20934C00F898D2 /* RemoveTree.cpp in Sources */,
 				FABDA9972552448300B5C523 /* b2_distance_joint.cpp in Sources */,
@@ -4833,6 +4875,7 @@
 				FA4F2BB41DE1E4BD00CA37D7 /* RecordingDevice.cpp in Sources */,
 				FA0B7DF51A95902C000E1D17 /* wrap_Mouse.cpp in Sources */,
 				FA0B7E861A95902C000E1D17 /* CoreAudioDecoder.cpp in Sources */,
+				FACA06AD293EE5CD001A2557 /* wrap_Sensor.cpp in Sources */,
 				FA0B7E761A95902C000E1D17 /* wrap_WeldJoint.cpp in Sources */,
 				FA0B7D561A95902C000E1D17 /* Buffer.cpp in Sources */,
 				FA1557C51CE90BD900AFF582 /* EXRHandler.cpp in Sources */,
@@ -5037,6 +5080,7 @@
 				FAF6C9E623C2DE2900D7B5BC /* SpvBuilder.cpp in Sources */,
 				FABDA9C12552448300B5C523 /* b2_rope.cpp in Sources */,
 				FA0B7D1E1A95902C000E1D17 /* ImageRasterizer.cpp in Sources */,
+				FACA06B2293EE5CD001A2557 /* Sensor.cpp in Sources */,
 				FABDA9FB2552448300B5C523 /* b2_collide_polygon.cpp in Sources */,
 				FABDA9D02552448300B5C523 /* b2_draw.cpp in Sources */,
 				FAF140A31E20934C00F898D2 /* Scan.cpp in Sources */,
@@ -5080,7 +5124,7 @@
 				FAC7CD781FE35E95006A60C7 /* physfs_platform_qnx.c in Sources */,
 				FA0B7D3C1A95902C000E1D17 /* Texture.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 */,
 				FAFEB29C28F210550025D7D0 /* unixstream.c in Sources */,
 				FA6A2B741F60B6710074C308 /* ByteData.cpp in Sources */,
@@ -5095,7 +5139,7 @@
 				FA9D8DDD1DEF842A002CD881 /* Drawable.cpp in Sources */,
 				FA0B7DFA1A95902C000E1D17 /* Body.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 */,
 				FAC7CD7F1FE35E95006A60C7 /* physfs_archiver_wad.c in Sources */,
 				FA0B7EDF1A95902D000E1D17 /* wrap_Touch.cpp in Sources */,
@@ -5112,6 +5156,7 @@
 				FA0B7AB51A958EA3000E1D17 /* ddsparse.cpp in Sources */,
 				FACA02F41F5E396B0084B28F /* wrap_CompressedData.cpp in Sources */,
 				FAF140AC1E20934C00F898D2 /* Versions.cpp in Sources */,
+				FACA06B0293EE5CD001A2557 /* Sensor.cpp in Sources */,
 				FA0B79461A958E3B000E1D17 /* Vector.cpp in Sources */,
 				FAC7CD811FE35E95006A60C7 /* physfs_platform_os2.c in Sources */,
 				FABDA9962552448300B5C523 /* b2_distance_joint.cpp in Sources */,
@@ -5257,6 +5302,7 @@
 				FAC7CD891FE35E95006A60C7 /* physfs_archiver_dir.c in Sources */,
 				FAF140751E20934C00F898D2 /* IntermTraverse.cpp in Sources */,
 				FA0B7CE21A95902C000E1D17 /* wrap_Audio.cpp in Sources */,
+				FACA06AC293EE5CD001A2557 /* wrap_Sensor.cpp in Sources */,
 				FA0B7DF71A95902C000E1D17 /* Body.cpp in Sources */,
 				FABDA9ED2552448300B5C523 /* b2_circle_shape.cpp in Sources */,
 				FA0B7DF41A95902C000E1D17 /* wrap_Mouse.cpp in Sources */,

+ 1 - 0
src/common/Module.h

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

+ 1 - 0
src/common/config.h

@@ -159,6 +159,7 @@
 #	define LOVE_ENABLE_MATH
 #	define LOVE_ENABLE_MOUSE
 #	define LOVE_ENABLE_PHYSICS
+#	define LOVE_ENABLE_SENSOR
 #	define LOVE_ENABLE_SOUND
 #	define LOVE_ENABLE_SYSTEM
 #	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;
 	}
 
+	// 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())
 	{
 		int queued, processed;
@@ -1172,14 +1185,6 @@ int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
 		d->rewind();
 	}
 
-	if (toLoop > 0)
-	{
-		if (--toLoop == 0)
-		{
-			offsetSamples = 0;
-		}
-	}
-
 	return decoded;
 }
 

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

@@ -32,6 +32,7 @@
 #include "audio/Audio.h"
 #include "common/config.h"
 #include "timer/Timer.h"
+#include "sensor/sdl/Sensor.h"
 
 #include <cmath>
 
@@ -178,6 +179,7 @@ Message *Event::convert(const SDL_Event &e)
 	vargs.reserve(4);
 
 	love::filesystem::Filesystem *filesystem = nullptr;
+	love::sensor::Sensor *sensorInstance = nullptr;
 
 	love::keyboard::Keyboard::Key key = love::keyboard::Keyboard::KEY_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;
 #endif
 
-#ifdef LOVE_LINUX
-	static bool touchNormalizationBug = false;
-#endif
-
 	switch (e.type)
 	{
 	case SDL_KEYDOWN:
@@ -313,22 +311,9 @@ Message *Event::convert(const SDL_Event &e)
 		touchinfo.dy = e.tfinger.dy;
 		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.
 		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_CONTROLLERBUTTONUP:
 	case SDL_CONTROLLERAXISMOTION:
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	case SDL_CONTROLLERSENSORUPDATE:
+#endif
 		msg = convertJoystickEvent(e);
 		break;
 	case SDL_WINDOWEVENT:
 		msg = convertWindowEvent(e);
 		break;
-#if SDL_VERSION_ATLEAST(2, 0, 9)
 	case SDL_DISPLAYEVENT:
 		if (e.display.event == SDL_DISPLAYEVENT_ORIENTATION)
 		{
@@ -405,7 +392,6 @@ Message *Event::convert(const SDL_Event &e)
 			msg = new Message("displayrotated", vargs);
 		}
 		break;
-#endif
 	case SDL_DROPFILE:
 		filesystem = Module::getInstance<filesystem::Filesystem>(Module::M_FILESYSTEM);
 		if (filesystem != nullptr)
@@ -446,6 +432,37 @@ Message *Event::convert(const SDL_Event &e)
 		msg = new Message("localechanged");
 		break;
 #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:
 		break;
 	}
@@ -564,6 +581,27 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 			msg = new Message("joystickremoved", vargs);
 		}
 		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:
 		break;
 	}

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

@@ -30,7 +30,7 @@
 #include "ParticleSystem.h"
 #include "Font.h"
 #include "Video.h"
-#include "Text.h"
+#include "TextBatch.h"
 #include "common/deprecation.h"
 #include "common/config.h"
 
@@ -439,9 +439,9 @@ Mesh *Graphics::newMesh(const std::vector<Mesh::BufferAttribute> &attributes, Pr
 	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)

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

@@ -58,7 +58,7 @@ namespace graphics
 
 class SpriteBatch;
 class ParticleSystem;
-class Text;
+class TextBatch;
 class Video;
 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<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);
 	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)
 			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;
 }
 
-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)
 		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])
 		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 newattrib = {};
 
@@ -231,6 +234,7 @@ void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh,
 	newattrib.mesh = mesh;
 	newattrib.enabled = oldattrib.buffer.get() ? oldattrib.enabled : true;
 	newattrib.indexInBuffer = buffer->getDataMemberIndex(attachname);
+	newattrib.startArrayIndex = startindex;
 	newattrib.step = step;
 
 	if (newattrib.indexInBuffer < 0)
@@ -578,12 +582,13 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
 
 			uint16 offset = (uint16) member.offset;
 			uint16 stride = (uint16) buffer->getArrayStride();
+			size_t bufferoffset = (size_t) stride * attrib.startArrayIndex;
 
 			attributes.set(attributeindex, member.decl.format, offset, activebuffers);
 			attributes.setBufferLayout(activebuffers, stride, attrib.step);
 
 			// TODO: Ideally we want to reuse buffers with the same stride+step.
-			buffers.set(activebuffers, buffer, 0);
+			buffers.set(activebuffers, buffer, bufferoffset);
 			activebuffers++;
 		}
 	}

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

@@ -57,6 +57,7 @@ public:
 		StrongRef<Buffer> buffer;
 		StrongRef<Mesh> mesh;
 		int indexInBuffer;
+		int startArrayIndex;
 		AttributeStep step;
 		bool enabled;
 	};
@@ -109,7 +110,7 @@ public:
 	 * to make sure this Mesh knows to flush the passed in Mesh's data to its
 	 * 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);
 	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.
  **/
 
-#include "Text.h"
+#include "TextBatch.h"
 #include "Graphics.h"
 
 #include <algorithm>
@@ -28,9 +28,9 @@ namespace love
 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)
 	, vertexAttributes(Font::vertexFormat, 0)
 	, vertexData(nullptr)
@@ -41,13 +41,13 @@ Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
 	set(text);
 }
 
-Text::~Text()
+TextBatch::~TextBatch()
 {
 	if (vertexData != nullptr)
 		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 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
 	// 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::DrawCommand> newcommands;
@@ -172,12 +172,12 @@ void Text::addTextData(const TextData &t)
 		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);
 }
 
-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()))
 		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()});
 }
 
-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);
 }
 
-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::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;
 }
 
-void Text::clear()
+void TextBatch::clear()
 {
 	textData.clear();
 	drawCommands.clear();
@@ -211,7 +211,7 @@ void Text::clear()
 	vertOffset = 0;
 }
 
-void Text::setFont(Font *f)
+void TextBatch::setFont(Font *f)
 {
 	font.set(f);
 	
@@ -221,12 +221,12 @@ void Text::setFont(Font *f)
 	regenerateVertices();
 }
 
-Font *Text::getFont() const
+Font *TextBatch::getFont() const
 {
 	return font.get();
 }
 
-int Text::getWidth(int index) const
+int TextBatch::getWidth(int index) const
 {
 	if (index < 0)
 		index = std::max((int) textData.size() - 1, 0);
@@ -237,7 +237,7 @@ int Text::getWidth(int index) const
 	return textData[index].textInfo.width;
 }
 
-int Text::getHeight(int index) const
+int TextBatch::getHeight(int index) const
 {
 	if (index < 0)
 		index = std::max((int) textData.size() - 1, 0);
@@ -248,7 +248,7 @@ int Text::getHeight(int index) const
 	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())
 		return;

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

@@ -34,14 +34,14 @@ namespace graphics
 
 class Graphics;
 
-class Text : public Drawable
+class TextBatch : public Drawable
 {
 public:
 
 	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, float wrap, Font::AlignMode align);

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

@@ -28,7 +28,7 @@
 #include "Shader.h"
 #include "Vulkan.h"
 
-#include "SDL_vulkan.h"
+#include <SDL_vulkan.h>
 
 #include <algorithm>
 #include <vector>
@@ -468,7 +468,8 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
 	this->pixelWidth = pixelwidth;
 	this->pixelHeight = pixelheight;
 
-	resetProjection();
+	if (!isRenderTargetActive())
+		resetProjection();
 }
 
 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;
 	windowHasStencil = windowhasstencil;
 
+	// Must be called before the swapchain is created.
+	setViewportSize(width, height, pixelwidth, pixelheight);
+
 	cleanUpFunctions.clear();
 	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());
 
-	setViewportSize(width, height, pixelwidth, pixelheight);
-
 	Vulkan::resetShaderSwitches();
 
 	frameCounter = 0;
@@ -1783,16 +1785,20 @@ VkPresentModeKHR Graphics::chooseSwapPresentMode(const std::vector<VkPresentMode
 		else
 			return VK_PRESENT_MODE_FIFO_KHR;
 	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;
 		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:
+		// TODO: support for swap interval = 2, etc?
 		return VK_PRESENT_MODE_FIFO_KHR;
 	}
 }
@@ -1814,15 +1820,9 @@ VkExtent2D Graphics::chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabiliti
 		return capabilities.currentExtent;
 	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 = {
-			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);

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

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

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

@@ -29,7 +29,7 @@
 #include "wrap_ParticleSystem.h"
 #include "wrap_Shader.h"
 #include "wrap_Mesh.h"
-#include "wrap_Text.h"
+#include "wrap_TextBatch.h"
 #include "wrap_Video.h"
 #include "wrap_Buffer.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);
 
 	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;
 }
 
@@ -340,7 +341,7 @@ int w_Mesh_getAttachedAttributes(lua_State *L)
 	{
 		const auto &attrib = attributes[i];
 
-		lua_createtable(L, 4, 0);
+		lua_createtable(L, 5, 0);
 
 		luax_pushstring(L, attrib.name);
 		lua_rawseti(L, -1, 1);
@@ -358,6 +359,9 @@ int w_Mesh_getAttachedAttributes(lua_State *L)
 		luax_pushstring(L, member.decl.name);
 		lua_rawseti(L, -1, 4);
 
+		lua_pushinteger(L, attrib.startArrayIndex + 1);
+		lua_rawseti(L, -1, 5);
+
 		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.
  **/
 
-#include "wrap_Text.h"
+#include "wrap_TextBatch.h"
 #include "wrap_Font.h"
 #include "math/wrap_Transform.h"
 
@@ -27,14 +27,14 @@ namespace love
 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;
 	luax_checkcoloredstring(L, 2, newtext);
@@ -43,9 +43,9 @@ int w_Text_set(lua_State *L)
 	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);
 
@@ -62,9 +62,9 @@ int w_Text_setf(lua_State *L)
 	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;
 
@@ -96,9 +96,9 @@ int w_Text_add(lua_State *L)
 	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;
 
@@ -138,72 +138,72 @@ int w_Text_addf(lua_State *L)
 	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(); });
 	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);
 	luax_catchexcept(L, [&](){ t->setFont(f); });
 	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();
 	luax_pushtype(L, f);
 	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;
 	lua_pushnumber(L, t->getWidth(index));
 	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;
 	lua_pushnumber(L, t->getHeight(index));
 	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;
 	lua_pushnumber(L, t->getWidth(index));
 	lua_pushnumber(L, t->getHeight(index));
 	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 }
 };
 
-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

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

@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include "Text.h"
+#include "TextBatch.h"
 #include "common/runtime.h"
 
 namespace love
@@ -28,8 +28,8 @@ namespace love
 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
 } // 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);
 
 	luax_catchexcept(L, [&](){ t->setSamplerState(s); });
-	return 1;
+	return 0;
 }
 
 int w_Texture_getWrap(lua_State *L)

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

@@ -24,6 +24,7 @@
 // LOVE
 #include "common/Object.h"
 #include "common/StringMap.h"
+#include "sensor/Sensor.h"
 
 // stdlib
 #include <vector>
@@ -38,6 +39,8 @@ class Joystick : public Object
 {
 public:
 
+	using Sensor = love::sensor::Sensor;
+
 	static love::Type type;
 
 	// Joystick hat values.
@@ -198,6 +201,11 @@ public:
 	virtual bool setVibration() = 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(GamepadType);
 	STRINGMAP_CLASS_DECLARE(GamepadAxis);

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

@@ -22,6 +22,10 @@
 #include "common/config.h"
 #include "Joystick.h"
 #include "common/int.h"
+#include "sensor/sdl/Sensor.h"
+
+// SDL
+#include <SDL_version.h>
 
 // C++
 #include <algorithm>
@@ -409,7 +413,6 @@ int Joystick::getID() const
 
 void Joystick::getDeviceInfo(int &vendorID, int &productID, int &productVersion) const
 {
-#if SDL_VERSION_ATLEAST(2, 0, 6)
 	if (joyhandle != nullptr)
 	{
 		vendorID = SDL_JoystickGetVendor(joyhandle);
@@ -417,7 +420,6 @@ void Joystick::getDeviceInfo(int &vendorID, int &productID, int &productVersion)
 		productVersion = SDL_JoystickGetProductVersion(joyhandle);
 	}
 	else
-#endif
 	{
 		vendorID = 0;
 		productID = 0;
@@ -521,10 +523,8 @@ bool Joystick::setVibration(float left, float right, float duration)
 
 	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)
 		success = true;
-#endif
 
 	if (!success && !checkCreateHaptic())
 		return false;
@@ -606,10 +606,8 @@ bool Joystick::setVibration()
 {
 	bool success = false;
 
-#if SDL_VERSION_ATLEAST(2, 0, 9)
 	if (!success)
 		success = isConnected() && SDL_JoystickRumble(joyhandle, 0, 0, 0) == 0;
-#endif
 
 	if (!success && SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
 		success = (SDL_HapticStopEffect(haptic, vibration.id) == 0);
@@ -643,6 +641,86 @@ void Joystick::getVibration(float &left, float &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)
 {
 	return hats.find(in, out);

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

@@ -88,6 +88,11 @@ public:
 	bool setVibration() 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(Uint8 in, Hat &out);
 

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

@@ -21,6 +21,7 @@
 // LOVE
 #include "wrap_Joystick.h"
 #include "wrap_JoystickModule.h"
+#include "sensor/Sensor.h"
 
 #include <vector>
 
@@ -367,6 +368,78 @@ int w_Joystick_getVibration(lua_State *L)
 	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.
 static const luaL_Reg w_Joystick_functions[] =
 {
@@ -396,6 +469,13 @@ static const luaL_Reg w_Joystick_functions[] =
 	{ "setVibration", w_Joystick_setVibration },
 	{ "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.
 	{ "getConnectedIndex", w_getIndex },
 

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

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

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

@@ -88,6 +88,9 @@ function love.createhandlers()
 		joystickremoved = function (j)
 			if love.joystickremoved then return love.joystickremoved(j) 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)
 			if love.focus then return love.focus(f) end
 		end,
@@ -134,6 +137,9 @@ function love.createhandlers()
 				love.audio.setPlaybackDevice()
 			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)
 			error("Unknown event: " .. name)
@@ -232,7 +238,8 @@ function love.errhand(msg)
 	if love.audio then love.audio.stop() end
 
 	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)
 

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

@@ -152,6 +152,9 @@ extern "C"
 #if defined(LOVE_ENABLE_PHYSICS)
 	extern int luaopen_love_physics(lua_State*);
 #endif
+#if defined(LOVE_ENABLE_SENSOR)
+	extern int luaopen_love_sensor(lua_State*);
+#endif
 #if defined(LOVE_ENABLE_SOUND)
 	extern int luaopen_love_sound(lua_State*);
 #endif
@@ -221,6 +224,9 @@ static const luaL_Reg modules[] = {
 #if defined(LOVE_ENABLE_PHYSICS)
 	{ "love.physics", luaopen_love_physics },
 #endif
+#if defined(LOVE_ENABLE_SENSOR)
+	{ "love.sensor", luaopen_love_sensor },
+#endif
 #if defined(LOVE_ENABLE_SOUND)
 	{ "love.sound", luaopen_love_sound },
 #endif
@@ -778,6 +784,10 @@ int w__setAccelerometerAsJoystick(lua_State *L)
 {
 	bool enable = (bool) lua_toboolean(L, 1);
 	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;
 }
 #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());
 }
 
-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)
 {
 	joint->SetStiffness(k);

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

@@ -56,26 +56,6 @@ public:
 	 **/
 	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
 	 **/

+ 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.maxForce = 1000.0f * body1->body->GetMass();
 	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);
 }
 
@@ -78,40 +79,6 @@ float MouseJoint::getMaxForce() const
 	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)
 {
 	// 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;
 }
 
-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 massB = bodyB->GetMass();
@@ -435,7 +440,12 @@ void Physics::b2LinearFrequency(float& frequency, float& ratio, float stiffness,
 	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 IB = bodyB->GetInertia();

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

@@ -360,7 +360,18 @@ public:
 	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 ratio The output damping ratio
 	 * @param stiffness The joint stiffness
@@ -368,10 +379,21 @@ public:
 	 * @param bodyA The bodyA 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 ratio The output damping ratio
 	 * @param stiffness The joint stiffness
@@ -379,7 +401,7 @@ public:
 	 * @param bodyA The bodyA 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:
 

+ 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;
 }
 
-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)
 {
 	joint->SetStiffness(k);

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

@@ -49,26 +49,6 @@ public:
 
 	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
 	 **/

+ 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)));
 }
 
-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)
 {
 	joint->SetStiffness(k);

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

@@ -95,26 +95,6 @@ public:
 	 **/
 	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
 	 **/

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

@@ -50,36 +50,6 @@ int w_DistanceJoint_getLength(lua_State *L)
 	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)
 {
 	DistanceJoint *t = luax_checkdistancejoint(L, 1);
@@ -114,10 +84,6 @@ static const luaL_Reg w_DistanceJoint_functions[] =
 {
 	{ "setLength", w_DistanceJoint_setLength },
 	{ "getLength", w_DistanceJoint_getLength },
-	{ "setFrequency", w_DistanceJoint_setFrequency },
-	{ "getFrequency", w_DistanceJoint_getFrequency },
-	{ "setDampingRatio", w_DistanceJoint_setDampingRatio },
-	{ "getDampingRatio", w_DistanceJoint_getDampingRatio },
 	{ "setStiffness", w_DistanceJoint_setStiffness },
 	{ "getStiffness", w_DistanceJoint_getStiffness },
 	{ "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;
 }
 
-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)
 {
 	MouseJoint *t = luax_checkmousejoint(L, 1);
@@ -132,10 +102,6 @@ static const luaL_Reg w_MouseJoint_functions[] =
 	{ "getTarget", w_MouseJoint_getTarget },
 	{ "setMaxForce", w_MouseJoint_setMaxForce },
 	{ "getMaxForce", w_MouseJoint_getMaxForce },
-	{ "setFrequency", w_MouseJoint_setFrequency },
-	{ "getFrequency", w_MouseJoint_getFrequency },
-	{ "setDampingRatio", w_MouseJoint_setDampingRatio },
-	{ "getDampingRatio", w_MouseJoint_getDampingRatio },
 	{ "setStiffness", w_MouseJoint_setStiffness },
 	{ "getStiffness", w_MouseJoint_getStiffness },
 	{ "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;
 
 }
+
 int w_getMeter(lua_State *L)
 {
 	lua_pushinteger(L, Physics::getMeter());
 	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.
 static const luaL_Reg functions[] =
 {
@@ -508,6 +593,10 @@ static const luaL_Reg functions[] =
 	{ "getDistance", w_getDistance },
 	{ "getMeter", w_getMeter },
 	{ "setMeter", w_setMeter },
+	{ "computeLinearStiffness", w_computeLinearStiffness },
+	{ "computeLinearFrequency", w_computeLinearFrequency },
+	{ "computeAngularStiffness", w_computeAngularStiffness },
+	{ "computeAngularFrequency", w_computeAngularFrequency },
 	{ 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;
 }
 
-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)
 {
 	WeldJoint *t = luax_checkweldjoint(L, 1);
@@ -104,10 +74,6 @@ int w_WeldJoint_getReferenceAngle(lua_State *L)
 
 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 },
 	{ "getStiffness", w_WeldJoint_getStiffness },
 	{ "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;
 }
 
-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)
 {
 	WheelJoint *t = luax_checkwheeljoint(L, 1);
@@ -180,18 +150,10 @@ static const luaL_Reg w_WheelJoint_functions[] =
 	{ "setMaxMotorTorque", w_WheelJoint_setMaxMotorTorque },
 	{ "getMaxMotorTorque", w_WheelJoint_getMaxMotorTorque },
 	{ "getMotorTorque", w_WheelJoint_getMotorTorque },
-	{ "setSpringFrequency", w_WheelJoint_setFrequency },
-	{ "getSpringFrequency", w_WheelJoint_getFrequency },
-	{ "setSpringDampingRatio", w_WheelJoint_setDampingRatio },
-	{ "getSpringDampingRatio", w_WheelJoint_getDampingRatio },
 	{ "setSpringStiffness", w_WheelJoint_setStiffness },
 	{ "getSpringStiffness", w_WheelJoint_getStiffness },
 	{ "setSpringDamping", w_WheelJoint_setDamping },
 	{ "getSpringDamping", w_WheelJoint_getDamping },
-	{ "setFrequency", w_WheelJoint_setFrequency },
-	{ "getFrequency", w_WheelJoint_getFrequency },
-	{ "setDampingRatio", w_WheelJoint_setDampingRatio },
-	{ "getDampingRatio", w_WheelJoint_getDampingRatio },
 	{ "setStiffness", w_WheelJoint_setStiffness },
 	{ "getStiffness", w_WheelJoint_getStiffness },
 	{ "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
 #include <SDL_syswm.h>
 
+#ifdef LOVE_GRAPHICS_VULKAN
+#include <SDL_vulkan.h>
+#endif
+
 #if defined(LOVE_WINDOWS)
 #include <windows.h>
 #include <dwmapi.h>
@@ -91,7 +95,6 @@ Window::Window()
 	, metalView(nullptr)
 #endif
 	, displayedWindowError(false)
-	, hasSDL203orEarlier(false)
 	, contextAttribs()
 {
 	if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
@@ -99,10 +102,6 @@ Window::Window()
 
 	// Make sure the screensaver doesn't activate by default.
 	setDisplaySleepEnabled(false);
-
-	SDL_version version = {};
-	SDL_GetVersion(&version);
-	hasSDL203orEarlier = (version.major == 2 && version.minor == 0 && version.patch <= 3);
 }
 
 Window::~Window()
@@ -140,16 +139,6 @@ void Window::setGLFramebufferAttributes(bool sRGB)
 
 	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)
 	// Avoid the Microsoft OpenGL 1.1 software renderer on Windows. Apparently
 	// 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)
 		{
 			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;
 		}
 	}
@@ -588,12 +569,8 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 		if (!f.fullscreen)
 			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)
 			SDL_SetWindowResizable(window, f.resizable ? SDL_TRUE : SDL_FALSE);
-#endif
 
 		if (this->settings.borderless != f.borderless)
 			SDL_SetWindowBordered(window, f.borderless ? SDL_FALSE : SDL_TRUE);
@@ -688,11 +665,16 @@ bool Window::onSizeChanged(int width, int height)
 	windowWidth = width;
 	windowHeight = height;
 
+	// TODO: Use SDL_GetWindowSizeInPixels here when supported.
 	if (glcontext != nullptr)
 		SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 #ifdef LOVE_GRAPHICS_METAL
 	else if (metalView != nullptr)
 		SDL_Metal_GetDrawableSize(window, &pixelWidth, &pixelHeight);
+#endif
+#ifdef LOVE_GRAPHICS_VULKAN
+	else if (windowRenderer == graphics::RENDERER_VULKAN)
+		SDL_Vulkan_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 #endif
 	else
 	{
@@ -720,12 +702,17 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi
 	pixelWidth = windowWidth;
 	pixelHeight = windowHeight;
 
+	// TODO: Use SDL_GetWindowSizeInPixels here when supported.
 	if ((wflags & SDL_WINDOW_OPENGL) != 0)
 		SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 #ifdef LOVE_GRAPHICS_METAL
 	else if ((wflags & SDL_WINDOW_METAL) != 0)
 		SDL_Metal_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 #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)
 	{
@@ -909,9 +896,6 @@ const char *Window::getDisplayName(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))
 	{
 		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_FLIPPED: return ORIENTATION_PORTRAIT_FLIPPED;
 	}
-#else
-	LOVE_UNUSED(displayindex);
-#endif
 
 	return ORIENTATION_UNKNOWN;
 }
@@ -1119,6 +1100,7 @@ void Window::setVSync(int vsync)
 	}
 
 #ifdef LOVE_GRAPHICS_VULKAN
+	// TODO: this doesn't update the swap-chain, but it should.
 	love::graphics::vulkan::Vulkan::setVsync(vsync);
 #endif
 
@@ -1148,6 +1130,11 @@ int Window::getVSync() const
 	}
 #endif
 
+#ifdef LOVE_GRAPHICS_VULKAN
+	if (windowRenderer == love::graphics::RENDERER_VULKAN)
+		return love::graphics::vulkan::Vulkan::getVsync();
+#endif
+
 	return 0;
 }
 

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

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