@lampdotdev/lamp 0.0.0-stage → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +357 -2
  3. package/Resources/Devices/roku.json +410 -0
  4. package/Resources/Devices/xbox-one.json +367 -0
  5. package/Resources/GameKit/NOTICE.md +63 -0
  6. package/Resources/GameKit/apple/LampPage.swift +1646 -0
  7. package/Resources/GameKit/apple/LampPlayer.swift +46 -0
  8. package/Resources/GameKit/apple/LampWebApp.swift +63 -0
  9. package/Resources/GameKit/apple/LampWebGame.swift +87 -0
  10. package/Resources/GameKit/apple/Native.swift +12 -0
  11. package/Resources/GameKit/build.sh +55 -0
  12. package/Resources/GameKit/lamp_game.h +116 -0
  13. package/Resources/GameKit/lamp_host.h +19 -0
  14. package/Resources/GameKit/lamp_kit.cpp +61 -0
  15. package/Resources/GameKit/lamp_sound.cpp +455 -0
  16. package/Resources/GameKit/lamp_xbox.cpp +374 -0
  17. package/Resources/GameKit/libraries/HandmadeMath.h +3940 -0
  18. package/Resources/GameKit/libraries/cgltf.h +7240 -0
  19. package/Resources/GameKit/libraries/dr_mp3.h +5430 -0
  20. package/Resources/GameKit/libraries/dr_wav.h +9209 -0
  21. package/Resources/GameKit/libraries/sokol_debugtext.h +4754 -0
  22. package/Resources/GameKit/libraries/sokol_gfx.h +29558 -0
  23. package/Resources/GameKit/libraries/sokol_gl.h +4538 -0
  24. package/Resources/GameKit/libraries/sokol_log.h +335 -0
  25. package/Resources/GameKit/libraries/sokol_shape.h +1531 -0
  26. package/Resources/GameKit/libraries/stb_image.h +7988 -0
  27. package/Resources/GameKit/libraries/stb_vorbis.c +5584 -0
  28. package/Resources/GameKit/scaffold/Game/game.cpp +142 -0
  29. package/Resources/GameKit/scaffold/Game/scene.glsl +49 -0
  30. package/Resources/GameKit/scaffold-web/Web/game.js +31 -0
  31. package/Resources/GameKit/scaffold-web/Web/index.html +13 -0
  32. package/Resources/GameKit/scaffold-web/Web/making.js +33 -0
  33. package/Resources/GameKit/scaffold-web/Web/world.js +33 -0
  34. package/Resources/GameKit/web/lamp-game.js +3036 -0
  35. package/Resources/GameKit/web/lamp-phaser.js +128 -0
  36. package/Resources/WebHost/App.exe +0 -0
  37. package/Resources/WebHost/Microsoft.Web.WebView2.Core.dll +0 -0
  38. package/Resources/WebHost/WebView2-LICENSE.txt +27 -0
  39. package/Resources/WebHost/lamp_controllers.h +1506 -0
  40. package/Resources/WebHost/lamp_web_host.cpp +1173 -0
  41. package/Resources/WebLibraries/earlier.txt +318 -0
  42. package/Resources/WebLibraries/phaser/LICENSE.md +20 -0
  43. package/Resources/WebLibraries/phaser/NOTICE.md +2 -0
  44. package/Resources/WebLibraries/phaser/phaser.esm.min.js +1 -0
  45. package/Resources/WebLibraries/qrcode/LICENSE +21 -0
  46. package/Resources/WebLibraries/qrcode/NOTICE.md +4 -0
  47. package/Resources/WebLibraries/qrcode/qrcode.js +2297 -0
  48. package/Resources/WebLibraries/rapier/LICENSE +201 -0
  49. package/Resources/WebLibraries/rapier/NOTICE.md +3 -0
  50. package/Resources/WebLibraries/rapier/rapier.js +2 -0
  51. package/Resources/WebLibraries/three/LICENSE +21 -0
  52. package/Resources/WebLibraries/three/NOTICE.md +4 -0
  53. package/Resources/WebLibraries/three/addons/animation/AnimationClipCreator.js +168 -0
  54. package/Resources/WebLibraries/three/addons/animation/CCDIKSolver.js +607 -0
  55. package/Resources/WebLibraries/three/addons/controls/ArcballControls.js +3543 -0
  56. package/Resources/WebLibraries/three/addons/controls/DragControls.js +452 -0
  57. package/Resources/WebLibraries/three/addons/controls/FirstPersonControls.js +512 -0
  58. package/Resources/WebLibraries/three/addons/controls/FlyControls.js +384 -0
  59. package/Resources/WebLibraries/three/addons/controls/MapControls.js +116 -0
  60. package/Resources/WebLibraries/three/addons/controls/OrbitControls.js +1985 -0
  61. package/Resources/WebLibraries/three/addons/controls/PointerLockControls.js +264 -0
  62. package/Resources/WebLibraries/three/addons/controls/TrackballControls.js +1106 -0
  63. package/Resources/WebLibraries/three/addons/controls/TransformControls.js +2003 -0
  64. package/Resources/WebLibraries/three/addons/curves/CurveExtras.js +694 -0
  65. package/Resources/WebLibraries/three/addons/curves/NURBSCurve.js +169 -0
  66. package/Resources/WebLibraries/three/addons/curves/NURBSSurface.js +98 -0
  67. package/Resources/WebLibraries/three/addons/curves/NURBSUtils.js +532 -0
  68. package/Resources/WebLibraries/three/addons/curves/NURBSVolume.js +82 -0
  69. package/Resources/WebLibraries/three/addons/effects/AnaglyphEffect.js +274 -0
  70. package/Resources/WebLibraries/three/addons/effects/AsciiEffect.js +323 -0
  71. package/Resources/WebLibraries/three/addons/effects/OutlineEffect.js +489 -0
  72. package/Resources/WebLibraries/three/addons/effects/ParallaxBarrierEffect.js +155 -0
  73. package/Resources/WebLibraries/three/addons/effects/StereoEffect.js +91 -0
  74. package/Resources/WebLibraries/three/addons/environments/ColorEnvironment.js +59 -0
  75. package/Resources/WebLibraries/three/addons/environments/DebugEnvironment.js +102 -0
  76. package/Resources/WebLibraries/three/addons/environments/RoomEnvironment.js +185 -0
  77. package/Resources/WebLibraries/three/addons/geometries/BoxLineGeometry.js +92 -0
  78. package/Resources/WebLibraries/three/addons/geometries/ConvexGeometry.js +72 -0
  79. package/Resources/WebLibraries/three/addons/geometries/DecalGeometry.js +420 -0
  80. package/Resources/WebLibraries/three/addons/geometries/LoftGeometry.js +355 -0
  81. package/Resources/WebLibraries/three/addons/geometries/ParametricFunctions.js +100 -0
  82. package/Resources/WebLibraries/three/addons/geometries/ParametricGeometry.js +172 -0
  83. package/Resources/WebLibraries/three/addons/geometries/RoundedBoxGeometry.js +216 -0
  84. package/Resources/WebLibraries/three/addons/geometries/TeapotGeometry.js +689 -0
  85. package/Resources/WebLibraries/three/addons/geometries/TextGeometry.js +102 -0
  86. package/Resources/WebLibraries/three/addons/helpers/AnimationPathHelper.js +302 -0
  87. package/Resources/WebLibraries/three/addons/helpers/LightProbeGridHelper.js +166 -0
  88. package/Resources/WebLibraries/three/addons/helpers/LightProbeGridHelperWebGL.js +221 -0
  89. package/Resources/WebLibraries/three/addons/helpers/LightProbeHelper.js +166 -0
  90. package/Resources/WebLibraries/three/addons/helpers/LightProbeHelperGPU.js +102 -0
  91. package/Resources/WebLibraries/three/addons/helpers/OctreeHelper.js +109 -0
  92. package/Resources/WebLibraries/three/addons/helpers/PositionalAudioHelper.js +169 -0
  93. package/Resources/WebLibraries/three/addons/helpers/RapierHelper.js +59 -0
  94. package/Resources/WebLibraries/three/addons/helpers/RectAreaLightHelper.js +118 -0
  95. package/Resources/WebLibraries/three/addons/helpers/TextureHelper.js +265 -0
  96. package/Resources/WebLibraries/three/addons/helpers/TextureHelperGPU.js +214 -0
  97. package/Resources/WebLibraries/three/addons/helpers/VertexNormalsHelper.js +155 -0
  98. package/Resources/WebLibraries/three/addons/helpers/VertexTangentsHelper.js +133 -0
  99. package/Resources/WebLibraries/three/addons/helpers/ViewHelper.js +529 -0
  100. package/Resources/WebLibraries/three/addons/interactive/HTMLMesh.js +617 -0
  101. package/Resources/WebLibraries/three/addons/interactive/InteractiveGroup.js +224 -0
  102. package/Resources/WebLibraries/three/addons/interactive/SelectionBox.js +324 -0
  103. package/Resources/WebLibraries/three/addons/interactive/SelectionHelper.js +150 -0
  104. package/Resources/WebLibraries/three/addons/libs/fflate.module.js +2699 -0
  105. package/Resources/WebLibraries/three/addons/libs/lil-gui.module.min.js +8 -0
  106. package/Resources/WebLibraries/three/addons/libs/meshopt_decoder.module.js +196 -0
  107. package/Resources/WebLibraries/three/addons/libs/potpack.module.js +125 -0
  108. package/Resources/WebLibraries/three/addons/libs/stats.module.js +167 -0
  109. package/Resources/WebLibraries/three/addons/libs/tween.module.js +876 -0
  110. package/Resources/WebLibraries/three/addons/lighting/ClusteredLighting.js +153 -0
  111. package/Resources/WebLibraries/three/addons/lighting/DynamicLighting.js +82 -0
  112. package/Resources/WebLibraries/three/addons/lighting/LightMapper.js +295 -0
  113. package/Resources/WebLibraries/three/addons/lighting/LightProbeGrid.js +923 -0
  114. package/Resources/WebLibraries/three/addons/lighting/LightProbeGridUtils.js +107 -0
  115. package/Resources/WebLibraries/three/addons/lighting/LightProbeGridWebGL.js +846 -0
  116. package/Resources/WebLibraries/three/addons/lighting/vxgi/VXGIConeTracer.js +243 -0
  117. package/Resources/WebLibraries/three/addons/lighting/vxgi/VXGIIrradiance.js +43 -0
  118. package/Resources/WebLibraries/three/addons/lighting/vxgi/VXGINode.js +617 -0
  119. package/Resources/WebLibraries/three/addons/lighting/vxgi/VXGISceneCollector.js +793 -0
  120. package/Resources/WebLibraries/three/addons/lighting/vxgi/VXGIVolume.js +1440 -0
  121. package/Resources/WebLibraries/three/addons/lights/LightProbeGenerator.js +337 -0
  122. package/Resources/WebLibraries/three/addons/lights/RectAreaLightTexturesLib.js +127 -0
  123. package/Resources/WebLibraries/three/addons/lights/RectAreaLightUniformsLib.js +40 -0
  124. package/Resources/WebLibraries/three/addons/lights/SunLight.js +100 -0
  125. package/Resources/WebLibraries/three/addons/lights/SunLightNode.js +56 -0
  126. package/Resources/WebLibraries/three/addons/lights/SunLightShadow.js +319 -0
  127. package/Resources/WebLibraries/three/addons/lights/SunShadowNode.js +312 -0
  128. package/Resources/WebLibraries/three/addons/lines/Line2.js +56 -0
  129. package/Resources/WebLibraries/three/addons/lines/LineGeometry.js +157 -0
  130. package/Resources/WebLibraries/three/addons/lines/LineMaterial.js +742 -0
  131. package/Resources/WebLibraries/three/addons/lines/LineSegments2.js +434 -0
  132. package/Resources/WebLibraries/three/addons/lines/LineSegmentsGeometry.js +298 -0
  133. package/Resources/WebLibraries/three/addons/lines/Wireframe.js +108 -0
  134. package/Resources/WebLibraries/three/addons/lines/WireframeGeometry2.js +49 -0
  135. package/Resources/WebLibraries/three/addons/lines/webgpu/Line2.js +46 -0
  136. package/Resources/WebLibraries/three/addons/lines/webgpu/LineSegments2.js +419 -0
  137. package/Resources/WebLibraries/three/addons/lines/webgpu/Wireframe.js +86 -0
  138. package/Resources/WebLibraries/three/addons/loaders/EXRLoader.js +3411 -0
  139. package/Resources/WebLibraries/three/addons/loaders/FBXLoader.js +4660 -0
  140. package/Resources/WebLibraries/three/addons/loaders/FontLoader.js +262 -0
  141. package/Resources/WebLibraries/three/addons/loaders/GLTFLoader.js +5006 -0
  142. package/Resources/WebLibraries/three/addons/loaders/HDRLoader.js +463 -0
  143. package/Resources/WebLibraries/three/addons/loaders/MTLLoader.js +601 -0
  144. package/Resources/WebLibraries/three/addons/loaders/OBJLoader.js +961 -0
  145. package/Resources/WebLibraries/three/addons/loaders/PLYLoader.js +992 -0
  146. package/Resources/WebLibraries/three/addons/loaders/RGBELoader.js +18 -0
  147. package/Resources/WebLibraries/three/addons/loaders/STLLoader.js +423 -0
  148. package/Resources/WebLibraries/three/addons/loaders/SVGLoader.js +3297 -0
  149. package/Resources/WebLibraries/three/addons/loaders/TGALoader.js +539 -0
  150. package/Resources/WebLibraries/three/addons/loaders/UltraHDRLoader.js +757 -0
  151. package/Resources/WebLibraries/three/addons/math/Capsule.js +159 -0
  152. package/Resources/WebLibraries/three/addons/math/ColorConverter.js +62 -0
  153. package/Resources/WebLibraries/three/addons/math/ColorSpaces.js +147 -0
  154. package/Resources/WebLibraries/three/addons/math/ConvexHull.js +1695 -0
  155. package/Resources/WebLibraries/three/addons/math/ImprovedNoise.js +88 -0
  156. package/Resources/WebLibraries/three/addons/math/Lut.js +317 -0
  157. package/Resources/WebLibraries/three/addons/math/MeshSurfaceSampler.js +315 -0
  158. package/Resources/WebLibraries/three/addons/math/OBB.js +535 -0
  159. package/Resources/WebLibraries/three/addons/math/Octree.js +825 -0
  160. package/Resources/WebLibraries/three/addons/math/SimplexNoise.js +470 -0
  161. package/Resources/WebLibraries/three/addons/misc/ConvexObjectBreaker.js +539 -0
  162. package/Resources/WebLibraries/three/addons/misc/GPUComputationRenderer.js +508 -0
  163. package/Resources/WebLibraries/three/addons/misc/Gyroscope.js +78 -0
  164. package/Resources/WebLibraries/three/addons/misc/MorphAnimMesh.js +119 -0
  165. package/Resources/WebLibraries/three/addons/misc/MorphBlendMesh.js +425 -0
  166. package/Resources/WebLibraries/three/addons/misc/ProgressiveLightMap.js +370 -0
  167. package/Resources/WebLibraries/three/addons/misc/ProgressiveLightMapGPU.js +322 -0
  168. package/Resources/WebLibraries/three/addons/misc/RollerCoaster.js +661 -0
  169. package/Resources/WebLibraries/three/addons/misc/Sculptor.js +1679 -0
  170. package/Resources/WebLibraries/three/addons/misc/SculptorMesh.js +1504 -0
  171. package/Resources/WebLibraries/three/addons/misc/SculptorTools.js +1180 -0
  172. package/Resources/WebLibraries/three/addons/misc/SculptorUtils.js +345 -0
  173. package/Resources/WebLibraries/three/addons/misc/TileCreasedNormalsPlugin.js +270 -0
  174. package/Resources/WebLibraries/three/addons/misc/TubePainter.js +611 -0
  175. package/Resources/WebLibraries/three/addons/misc/Volume.js +520 -0
  176. package/Resources/WebLibraries/three/addons/misc/VolumeSlice.js +275 -0
  177. package/Resources/WebLibraries/three/addons/modifiers/CurveModifier.js +374 -0
  178. package/Resources/WebLibraries/three/addons/modifiers/CurveModifierGPU.js +256 -0
  179. package/Resources/WebLibraries/three/addons/modifiers/EdgeSplitModifier.js +299 -0
  180. package/Resources/WebLibraries/three/addons/modifiers/TessellateModifier.js +340 -0
  181. package/Resources/WebLibraries/three/addons/objects/GroundedSkybox.js +69 -0
  182. package/Resources/WebLibraries/three/addons/objects/Lensflare.js +489 -0
  183. package/Resources/WebLibraries/three/addons/objects/LensflareMesh.js +376 -0
  184. package/Resources/WebLibraries/three/addons/objects/MarchingCubes.js +1261 -0
  185. package/Resources/WebLibraries/three/addons/objects/Reflector.js +390 -0
  186. package/Resources/WebLibraries/three/addons/objects/ReflectorForSSRPass.js +393 -0
  187. package/Resources/WebLibraries/three/addons/objects/Refractor.js +389 -0
  188. package/Resources/WebLibraries/three/addons/objects/ShadowMesh.js +130 -0
  189. package/Resources/WebLibraries/three/addons/objects/Sky.js +342 -0
  190. package/Resources/WebLibraries/three/addons/objects/SkyMesh.js +403 -0
  191. package/Resources/WebLibraries/three/addons/objects/Water.js +374 -0
  192. package/Resources/WebLibraries/three/addons/objects/Water2.js +403 -0
  193. package/Resources/WebLibraries/three/addons/objects/Water2Mesh.js +199 -0
  194. package/Resources/WebLibraries/three/addons/objects/WaterMesh.js +194 -0
  195. package/Resources/WebLibraries/three/addons/postprocessing/AfterimagePass.js +187 -0
  196. package/Resources/WebLibraries/three/addons/postprocessing/BloomPass.js +274 -0
  197. package/Resources/WebLibraries/three/addons/postprocessing/BokehPass.js +218 -0
  198. package/Resources/WebLibraries/three/addons/postprocessing/ClearPass.js +97 -0
  199. package/Resources/WebLibraries/three/addons/postprocessing/CubeTexturePass.js +146 -0
  200. package/Resources/WebLibraries/three/addons/postprocessing/DotScreenPass.js +114 -0
  201. package/Resources/WebLibraries/three/addons/postprocessing/EffectComposer.js +365 -0
  202. package/Resources/WebLibraries/three/addons/postprocessing/FXAAPass.js +40 -0
  203. package/Resources/WebLibraries/three/addons/postprocessing/FilmPass.js +113 -0
  204. package/Resources/WebLibraries/three/addons/postprocessing/GTAOPass.js +735 -0
  205. package/Resources/WebLibraries/three/addons/postprocessing/GlitchPass.js +177 -0
  206. package/Resources/WebLibraries/three/addons/postprocessing/HalftonePass.js +134 -0
  207. package/Resources/WebLibraries/three/addons/postprocessing/LUTPass.js +138 -0
  208. package/Resources/WebLibraries/three/addons/postprocessing/MaskPass.js +195 -0
  209. package/Resources/WebLibraries/three/addons/postprocessing/OutlinePass.js +776 -0
  210. package/Resources/WebLibraries/three/addons/postprocessing/OutputPass.js +148 -0
  211. package/Resources/WebLibraries/three/addons/postprocessing/Pass.js +191 -0
  212. package/Resources/WebLibraries/three/addons/postprocessing/RenderPass.js +193 -0
  213. package/Resources/WebLibraries/three/addons/postprocessing/RenderPixelatedPass.js +314 -0
  214. package/Resources/WebLibraries/three/addons/postprocessing/RenderTransitionPass.js +267 -0
  215. package/Resources/WebLibraries/three/addons/postprocessing/SAOPass.js +407 -0
  216. package/Resources/WebLibraries/three/addons/postprocessing/SMAAPass.js +230 -0
  217. package/Resources/WebLibraries/three/addons/postprocessing/SSAARenderPass.js +313 -0
  218. package/Resources/WebLibraries/three/addons/postprocessing/SSAOPass.js +527 -0
  219. package/Resources/WebLibraries/three/addons/postprocessing/SSRPass.js +858 -0
  220. package/Resources/WebLibraries/three/addons/postprocessing/SavePass.js +132 -0
  221. package/Resources/WebLibraries/three/addons/postprocessing/ShaderPass.js +135 -0
  222. package/Resources/WebLibraries/three/addons/postprocessing/TAARenderPass.js +243 -0
  223. package/Resources/WebLibraries/three/addons/postprocessing/TexturePass.js +131 -0
  224. package/Resources/WebLibraries/three/addons/postprocessing/UnrealBloomPass.js +541 -0
  225. package/Resources/WebLibraries/three/addons/shaders/ACESFilmicToneMappingShader.js +96 -0
  226. package/Resources/WebLibraries/three/addons/shaders/AfterimageShader.js +63 -0
  227. package/Resources/WebLibraries/three/addons/shaders/BasicShader.js +36 -0
  228. package/Resources/WebLibraries/three/addons/shaders/BleachBypassShader.js +68 -0
  229. package/Resources/WebLibraries/three/addons/shaders/BlendShader.js +56 -0
  230. package/Resources/WebLibraries/three/addons/shaders/BokehShader.js +151 -0
  231. package/Resources/WebLibraries/three/addons/shaders/BokehShader2.js +404 -0
  232. package/Resources/WebLibraries/three/addons/shaders/BrightnessContrastShader.js +62 -0
  233. package/Resources/WebLibraries/three/addons/shaders/ColorCorrectionShader.js +59 -0
  234. package/Resources/WebLibraries/three/addons/shaders/ColorifyShader.js +57 -0
  235. package/Resources/WebLibraries/three/addons/shaders/ConvolutionShader.js +74 -0
  236. package/Resources/WebLibraries/three/addons/shaders/CopyShader.js +52 -0
  237. package/Resources/WebLibraries/three/addons/shaders/DOFMipMapShader.js +63 -0
  238. package/Resources/WebLibraries/three/addons/shaders/DepthLimitedBlurShader.js +183 -0
  239. package/Resources/WebLibraries/three/addons/shaders/DigitalGlitch.js +104 -0
  240. package/Resources/WebLibraries/three/addons/shaders/DotScreenShader.js +75 -0
  241. package/Resources/WebLibraries/three/addons/shaders/ExposureShader.js +51 -0
  242. package/Resources/WebLibraries/three/addons/shaders/FXAAShader.js +298 -0
  243. package/Resources/WebLibraries/three/addons/shaders/FilmShader.js +73 -0
  244. package/Resources/WebLibraries/three/addons/shaders/FocusShader.js +94 -0
  245. package/Resources/WebLibraries/three/addons/shaders/FreiChenShader.js +103 -0
  246. package/Resources/WebLibraries/three/addons/shaders/GTAOShader.js +437 -0
  247. package/Resources/WebLibraries/three/addons/shaders/GammaCorrectionShader.js +52 -0
  248. package/Resources/WebLibraries/three/addons/shaders/HalftoneShader.js +332 -0
  249. package/Resources/WebLibraries/three/addons/shaders/HorizontalBlurShader.js +68 -0
  250. package/Resources/WebLibraries/three/addons/shaders/HorizontalTiltShiftShader.js +70 -0
  251. package/Resources/WebLibraries/three/addons/shaders/HueSaturationShader.js +74 -0
  252. package/Resources/WebLibraries/three/addons/shaders/KaleidoShader.js +65 -0
  253. package/Resources/WebLibraries/three/addons/shaders/LuminosityHighPassShader.js +68 -0
  254. package/Resources/WebLibraries/three/addons/shaders/LuminosityShader.js +54 -0
  255. package/Resources/WebLibraries/three/addons/shaders/MirrorShader.js +62 -0
  256. package/Resources/WebLibraries/three/addons/shaders/NormalMapShader.js +60 -0
  257. package/Resources/WebLibraries/three/addons/shaders/OutputShader.js +103 -0
  258. package/Resources/WebLibraries/three/addons/shaders/PoissonDenoiseShader.js +230 -0
  259. package/Resources/WebLibraries/three/addons/shaders/RGBShiftShader.js +61 -0
  260. package/Resources/WebLibraries/three/addons/shaders/SAOShader.js +201 -0
  261. package/Resources/WebLibraries/three/addons/shaders/SMAAShader.js +489 -0
  262. package/Resources/WebLibraries/three/addons/shaders/SSAOShader.js +331 -0
  263. package/Resources/WebLibraries/three/addons/shaders/SSRShader.js +396 -0
  264. package/Resources/WebLibraries/three/addons/shaders/SepiaShader.js +57 -0
  265. package/Resources/WebLibraries/three/addons/shaders/SobelOperatorShader.js +98 -0
  266. package/Resources/WebLibraries/three/addons/shaders/SubsurfaceScatteringShader.js +95 -0
  267. package/Resources/WebLibraries/three/addons/shaders/TechnicolorShader.js +51 -0
  268. package/Resources/WebLibraries/three/addons/shaders/ToonShader.js +349 -0
  269. package/Resources/WebLibraries/three/addons/shaders/TriangleBlurShader.js +79 -0
  270. package/Resources/WebLibraries/three/addons/shaders/UnpackDepthRGBAShader.js +60 -0
  271. package/Resources/WebLibraries/three/addons/shaders/VelocityShader.js +137 -0
  272. package/Resources/WebLibraries/three/addons/shaders/VerticalBlurShader.js +66 -0
  273. package/Resources/WebLibraries/three/addons/shaders/VerticalTiltShiftShader.js +70 -0
  274. package/Resources/WebLibraries/three/addons/shaders/VignetteShader.js +56 -0
  275. package/Resources/WebLibraries/three/addons/shaders/VolumeShader.js +285 -0
  276. package/Resources/WebLibraries/three/addons/shaders/WaterRefractionShader.js +106 -0
  277. package/Resources/WebLibraries/three/addons/textures/FlakesTexture.js +54 -0
  278. package/Resources/WebLibraries/three/addons/tsl/WebGLNodesHandler.js +607 -0
  279. package/Resources/WebLibraries/three/addons/tsl/display/AfterImageNode.js +256 -0
  280. package/Resources/WebLibraries/three/addons/tsl/display/AnaglyphPassNode.js +550 -0
  281. package/Resources/WebLibraries/three/addons/tsl/display/BilateralBlurNode.js +377 -0
  282. package/Resources/WebLibraries/three/addons/tsl/display/BleachBypass.js +33 -0
  283. package/Resources/WebLibraries/three/addons/tsl/display/BloomNode.js +599 -0
  284. package/Resources/WebLibraries/three/addons/tsl/display/CRT.js +150 -0
  285. package/Resources/WebLibraries/three/addons/tsl/display/ChromaticAberrationNode.js +163 -0
  286. package/Resources/WebLibraries/three/addons/tsl/display/DenoiseNode.js +354 -0
  287. package/Resources/WebLibraries/three/addons/tsl/display/DepthOfFieldNode.js +572 -0
  288. package/Resources/WebLibraries/three/addons/tsl/display/DotScreenNode.js +104 -0
  289. package/Resources/WebLibraries/three/addons/tsl/display/FSR1Node.js +481 -0
  290. package/Resources/WebLibraries/three/addons/tsl/display/FXAANode.js +357 -0
  291. package/Resources/WebLibraries/three/addons/tsl/display/FilmNode.js +101 -0
  292. package/Resources/WebLibraries/three/addons/tsl/display/GTAONode.js +727 -0
  293. package/Resources/WebLibraries/three/addons/tsl/display/GaussianBlurNode.js +412 -0
  294. package/Resources/WebLibraries/three/addons/tsl/display/GodraysNode.js +604 -0
  295. package/Resources/WebLibraries/three/addons/tsl/display/ImportanceSampledEnvironment.js +564 -0
  296. package/Resources/WebLibraries/three/addons/tsl/display/LensflareNode.js +282 -0
  297. package/Resources/WebLibraries/three/addons/tsl/display/Lut3DNode.js +109 -0
  298. package/Resources/WebLibraries/three/addons/tsl/display/MotionBlur.js +33 -0
  299. package/Resources/WebLibraries/three/addons/tsl/display/OITPassNode.js +341 -0
  300. package/Resources/WebLibraries/three/addons/tsl/display/OutlineNode.js +916 -0
  301. package/Resources/WebLibraries/three/addons/tsl/display/ParallaxBarrierPassNode.js +90 -0
  302. package/Resources/WebLibraries/three/addons/tsl/display/PixelationPassNode.js +227 -0
  303. package/Resources/WebLibraries/three/addons/tsl/display/RGBShiftNode.js +96 -0
  304. package/Resources/WebLibraries/three/addons/tsl/display/RecurrentDenoiseNode.js +812 -0
  305. package/Resources/WebLibraries/three/addons/tsl/display/RetroPassNode.js +263 -0
  306. package/Resources/WebLibraries/three/addons/tsl/display/SMAANode.js +729 -0
  307. package/Resources/WebLibraries/three/addons/tsl/display/SSAAPassNode.js +344 -0
  308. package/Resources/WebLibraries/three/addons/tsl/display/SSAONode.js +422 -0
  309. package/Resources/WebLibraries/three/addons/tsl/display/SSGINode.js +698 -0
  310. package/Resources/WebLibraries/three/addons/tsl/display/SSRNode.js +1430 -0
  311. package/Resources/WebLibraries/three/addons/tsl/display/SSSNode.js +479 -0
  312. package/Resources/WebLibraries/three/addons/tsl/display/Sepia.js +24 -0
  313. package/Resources/WebLibraries/three/addons/tsl/display/Shape.js +29 -0
  314. package/Resources/WebLibraries/three/addons/tsl/display/SharpenNode.js +284 -0
  315. package/Resources/WebLibraries/three/addons/tsl/display/SobelOperatorNode.js +168 -0
  316. package/Resources/WebLibraries/three/addons/tsl/display/StereoCompositePassNode.js +190 -0
  317. package/Resources/WebLibraries/three/addons/tsl/display/StereoPassNode.js +117 -0
  318. package/Resources/WebLibraries/three/addons/tsl/display/TAAUNode.js +700 -0
  319. package/Resources/WebLibraries/three/addons/tsl/display/TRAANode.js +609 -0
  320. package/Resources/WebLibraries/three/addons/tsl/display/TemporalReprojectNode.js +1024 -0
  321. package/Resources/WebLibraries/three/addons/tsl/display/TransitionNode.js +141 -0
  322. package/Resources/WebLibraries/three/addons/tsl/display/boxBlur.js +65 -0
  323. package/Resources/WebLibraries/three/addons/tsl/display/depthAwareBlend.js +80 -0
  324. package/Resources/WebLibraries/three/addons/tsl/display/depthAwareBlur.js +63 -0
  325. package/Resources/WebLibraries/three/addons/tsl/display/hashBlur.js +55 -0
  326. package/Resources/WebLibraries/three/addons/tsl/display/lensDirt.js +30 -0
  327. package/Resources/WebLibraries/three/addons/tsl/display/radialBlur.js +68 -0
  328. package/Resources/WebLibraries/three/addons/tsl/lighting/ClusteredLightsNode.js +994 -0
  329. package/Resources/WebLibraries/three/addons/tsl/lighting/DynamicLightsNode.js +201 -0
  330. package/Resources/WebLibraries/three/addons/tsl/lighting/LightProbeGridNode.js +183 -0
  331. package/Resources/WebLibraries/three/addons/tsl/lighting/data/AmbientLightDataNode.js +67 -0
  332. package/Resources/WebLibraries/three/addons/tsl/lighting/data/DirectionalLightDataNode.js +117 -0
  333. package/Resources/WebLibraries/three/addons/tsl/lighting/data/HemisphereLightDataNode.js +105 -0
  334. package/Resources/WebLibraries/three/addons/tsl/lighting/data/PointLightDataNode.js +140 -0
  335. package/Resources/WebLibraries/three/addons/tsl/lighting/data/SpotLightDataNode.js +167 -0
  336. package/Resources/WebLibraries/three/addons/tsl/math/Bayer.js +84 -0
  337. package/Resources/WebLibraries/three/addons/tsl/math/curlNoise.js +107 -0
  338. package/Resources/WebLibraries/three/addons/tsl/math/voronoiNoise.js +112 -0
  339. package/Resources/WebLibraries/three/addons/tsl/shadows/TileShadowNode.js +542 -0
  340. package/Resources/WebLibraries/three/addons/tsl/shadows/TileShadowNodeHelper.js +212 -0
  341. package/Resources/WebLibraries/three/addons/tsl/utils/GroundedSkybox.js +62 -0
  342. package/Resources/WebLibraries/three/addons/tsl/utils/RNoise.js +51 -0
  343. package/Resources/WebLibraries/three/addons/tsl/utils/Raymarching.js +70 -0
  344. package/Resources/WebLibraries/three/addons/tsl/utils/SoftParticles.js +53 -0
  345. package/Resources/WebLibraries/three/addons/tsl/utils/SpecularHelpers.js +302 -0
  346. package/Resources/WebLibraries/three/addons/tsl/utils/TAAUtils.js +184 -0
  347. package/Resources/WebLibraries/three/addons/utils/BufferGeometryUtils.js +1523 -0
  348. package/Resources/WebLibraries/three/addons/utils/CameraUtils.js +82 -0
  349. package/Resources/WebLibraries/three/addons/utils/ColorUtils.js +76 -0
  350. package/Resources/WebLibraries/three/addons/utils/GaussianSplatUtils.js +229 -0
  351. package/Resources/WebLibraries/three/addons/utils/GeometryCompressionUtils.js +547 -0
  352. package/Resources/WebLibraries/three/addons/utils/GeometryUtils.js +226 -0
  353. package/Resources/WebLibraries/three/addons/utils/LDrawUtils.js +211 -0
  354. package/Resources/WebLibraries/three/addons/utils/SceneOptimizer.js +459 -0
  355. package/Resources/WebLibraries/three/addons/utils/SceneUtils.js +363 -0
  356. package/Resources/WebLibraries/three/addons/utils/ShadowMapViewer.js +244 -0
  357. package/Resources/WebLibraries/three/addons/utils/ShadowMapViewerGPU.js +233 -0
  358. package/Resources/WebLibraries/three/addons/utils/SkeletonUtils.js +496 -0
  359. package/Resources/WebLibraries/three/addons/utils/SortUtils.js +175 -0
  360. package/Resources/WebLibraries/three/addons/utils/UVsDebug.js +173 -0
  361. package/Resources/WebLibraries/three/addons/utils/WebGLTextureUtils.js +115 -0
  362. package/Resources/WebLibraries/three/addons/utils/WebGPUTextureUtils.js +81 -0
  363. package/Resources/WebLibraries/three/addons/utils/WorkerPool.js +167 -0
  364. package/Resources/WebLibraries/three/addons/utils/unwrapUVs.js +865 -0
  365. package/Resources/WebLibraries/three/three.core.js +61162 -0
  366. package/Resources/WebLibraries/three/three.module.js +19537 -0
  367. package/Resources/WebLibraries/three/three.tsl.js +41903 -0
  368. package/Resources/WebLibraries/three/three.webgpu.js +53714 -0
  369. package/Resources/skills/2d-game/SKILL.md +305 -0
  370. package/Resources/skills/3d-game/SKILL.md +274 -0
  371. package/Resources/skills/accounts/SKILL.md +41 -0
  372. package/Resources/skills/android-design/SKILL.md +159 -0
  373. package/Resources/skills/app-ai/SKILL.md +154 -0
  374. package/Resources/skills/app-intents/SKILL.md +31 -0
  375. package/Resources/skills/app-kinds/SKILL.md +122 -0
  376. package/Resources/skills/apple-watch/SKILL.md +43 -0
  377. package/Resources/skills/assets/SKILL.md +183 -0
  378. package/Resources/skills/building-blocks/SKILL.md +97 -0
  379. package/Resources/skills/device-control/SKILL.md +56 -0
  380. package/Resources/skills/device-integrations/SKILL.md +278 -0
  381. package/Resources/skills/finder-files/SKILL.md +19 -0
  382. package/Resources/skills/game/SKILL.md +414 -0
  383. package/Resources/skills/game-3d/SKILL.md +12 -0
  384. package/Resources/skills/game-art/SKILL.md +11 -0
  385. package/Resources/skills/game-feel/SKILL.md +13 -0
  386. package/Resources/skills/game-levels/SKILL.md +12 -0
  387. package/Resources/skills/game-live/SKILL.md +12 -0
  388. package/Resources/skills/game-physics/SKILL.md +13 -0
  389. package/Resources/skills/game-playtest/SKILL.md +26 -0
  390. package/Resources/skills/game-ui/SKILL.md +278 -0
  391. package/Resources/skills/github-projects/SKILL.md +59 -0
  392. package/Resources/skills/icloud-sync/SKILL.md +30 -0
  393. package/Resources/skills/iphone-hardware/SKILL.md +57 -0
  394. package/Resources/skills/iphone-widgets/SKILL.md +53 -0
  395. package/Resources/skills/lg-developer-mode/SKILL.md +152 -0
  396. package/Resources/skills/mac-widgets/SKILL.md +60 -0
  397. package/Resources/skills/maps/SKILL.md +8 -0
  398. package/Resources/skills/menu-bar/SKILL.md +36 -0
  399. package/Resources/skills/more-apps/SKILL.md +53 -0
  400. package/Resources/skills/phone-controllers/SKILL.md +79 -0
  401. package/Resources/skills/phone-pages/SKILL.md +173 -0
  402. package/Resources/skills/roku-developer-mode/SKILL.md +106 -0
  403. package/Resources/skills/scene-3d/SKILL.md +27 -0
  404. package/Resources/skills/scripting/SKILL.md +17 -0
  405. package/Resources/skills/share-extension/SKILL.md +22 -0
  406. package/Resources/skills/tv-design/SKILL.md +72 -0
  407. package/Resources/skills/web-app/SKILL.md +136 -0
  408. package/Resources/skills/web-game/SKILL.md +95 -0
  409. package/Resources/skills/web-or-native/SKILL.md +103 -0
  410. package/Resources/skills/wish-links/SKILL.md +156 -0
  411. package/THIRD_PARTY_NOTICES.md +170 -0
  412. package/cli/android.mjs +941 -0
  413. package/cli/browser.mjs +268 -0
  414. package/cli/calls.mjs +390 -0
  415. package/cli/dev.mjs +773 -0
  416. package/cli/devices.mjs +984 -0
  417. package/cli/lamp.mjs +1016 -0
  418. package/cli/lampapp.mjs +332 -0
  419. package/cli/lamps/android-host/LampPageGateway.kt +345 -0
  420. package/cli/lamps/android-host/app/build.gradle.kts +40 -0
  421. package/cli/lamps/android-host/build.gradle.kts +4 -0
  422. package/cli/lamps/android-host/game/AndroidManifest.xml +33 -0
  423. package/cli/lamps/android-host/game/MainActivity.kt +173 -0
  424. package/cli/lamps/android-host/gradle/libs.versions.toml +39 -0
  425. package/cli/lamps/android-host/gradle.properties +8 -0
  426. package/cli/lamps/android-host/page/AndroidManifest.xml +35 -0
  427. package/cli/lamps/android-host/page/MainActivity.kt +193 -0
  428. package/cli/lamps/android-host/settings.gradle.kts +30 -0
  429. package/cli/lamps/android-host/tools.json +43 -0
  430. package/cli/lamps/lamp-controllers.js +2 -0
  431. package/cli/lamps/lamp-for-games.js +6863 -0
  432. package/cli/lamps/lamp.js +7023 -0
  433. package/cli/lamps/mac-starter/.lamp/meta.json +1 -0
  434. package/cli/lamps/mac-starter/APP.md +12 -0
  435. package/cli/lamps/mac-starter/Package.swift +8 -0
  436. package/cli/lamps/mac-starter/Sources/App.swift +11 -0
  437. package/cli/lamps/mac-starter/Sources/ContentView.swift +17 -0
  438. package/cli/lamps/mac-starter/Sources/LampSupport.swift +18097 -0
  439. package/cli/lamps/mac-starter/build.sh +336 -0
  440. package/cli/lamps/mac-starter/gitignore +3 -0
  441. package/cli/lamps/mac-starter/lamp-starter.json +6 -0
  442. package/cli/lamps/web-starter/APP.md +8 -0
  443. package/cli/lamps/web-starter/app.js +1 -0
  444. package/cli/lamps/web-starter/index.html +17 -0
  445. package/cli/lamps/web-starter/lamp-starter.json +5 -0
  446. package/cli/lamps/web-starter/lamp.js +7023 -0
  447. package/cli/lamps/web-starter/style.css +25 -0
  448. package/cli/link.mjs +138 -0
  449. package/cli/mac.mjs +621 -0
  450. package/cli/mdns.mjs +205 -0
  451. package/cli/msix.mjs +413 -0
  452. package/cli/network.mjs +209 -0
  453. package/cli/outside.mjs +140 -0
  454. package/cli/page.js +165 -0
  455. package/cli/place.mjs +298 -0
  456. package/cli/png.mjs +184 -0
  457. package/cli/share.mjs +202 -0
  458. package/cli/skill/SKILL.md +255 -0
  459. package/cli/skills.mjs +71 -0
  460. package/cli/state.mjs +173 -0
  461. package/cli/update.mjs +57 -0
  462. package/package.json +20 -4
  463. package/site/lib/quota.js +59 -0
  464. package/site/lib/shared-wishes.js +996 -0
@@ -0,0 +1,345 @@
1
+ // Written by Lamp before every build; don't edit. The page's LampAI (Web/lamp.js), answered through this app.
2
+ package {{PACKAGE}}
3
+
4
+ import android.webkit.WebView
5
+ import androidx.webkit.WebViewCompat
6
+ import androidx.webkit.WebViewFeature
7
+ import java.io.File
8
+ import java.net.InetSocketAddress
9
+ import java.net.Socket
10
+ import org.json.JSONObject
11
+ import kotlin.concurrent.thread
12
+
13
+ object LampPageGateway {
14
+ private val routes = setOf("/ai", "/ai/status", "/ai/image", "/ai/decide", "/ai/act", "/ai/hear")
15
+
16
+ /** Answers the page at `origin`, the page's own, in its main frame: no other page or frame reaches Lamp. */
17
+ fun attach(web: WebView, origin: String) {
18
+ if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) return
19
+ val files = web.context.filesDir
20
+ WebViewCompat.addWebMessageListener(web, "lampGatewayHost", setOf(origin)) { _, message, _, isMainFrame, reply ->
21
+ val call = try { JSONObject(message.data ?: "") } catch (e: Exception) { null }
22
+ if (!isMainFrame || call == null) return@addWebMessageListener
23
+ val id = call.optInt("id")
24
+ val path = call.optString("path")
25
+ val body = call.optString("body", "{}")
26
+ // The page's own wait for an answer (a status check's few seconds, a question's minutes).
27
+ val timeout = call.optInt("timeout", 240_000).coerceIn(1_000, 240_000)
28
+ thread {
29
+ val (status, text) = if (path in routes) post(files, path, body, timeout) else 404 to JSONObject().put("error", "Not found").toString()
30
+ val answer = JSONObject().put("id", id).put("status", status).put("body", text).toString()
31
+ web.post { reply.postMessage(answer) }
32
+ }
33
+ }
34
+ }
35
+
36
+ private fun failure(message: String) = 503 to JSONObject().put("error", message).toString()
37
+
38
+ /** Plain HTTP on the Mac's own network, on a socket, as the Kotlin building blocks speak it: each way to the Mac
39
+ * Lamp wrote down in turn, and all of them once more after a moment, in case Lamp is just starting. */
40
+ private fun post(files: File, path: String, body: String, timeout: Int): Pair<Int, String> {
41
+ val deadline = System.currentTimeMillis() + timeout
42
+ val install = try { JSONObject(File(files, "lamp-install.json").readText()) } catch (e: Exception) {
43
+ return failure("This copy of the app wasn't opened from Lamp, so it can't use AI. Open it from Lamp on your Mac.")
44
+ }
45
+ val token = install.optString("token")
46
+ val list = install.optJSONArray("routes") ?: return failure("Couldn't reach Lamp. Open Lamp on your Mac and try again.")
47
+ val bytes = body.toByteArray(Charsets.UTF_8)
48
+ // What the person said is paid for once heard: tried on each way to the Mac once, never again after a moment
49
+ // (Lamp knows the recording by the id lamp.js gave it, so two ways reaching it still pay once).
50
+ for (attempt in 0 until (if (path == "/ai/hear") 1 else 2)) {
51
+ for (i in 0 until list.length()) {
52
+ val route = list.optJSONObject(i) ?: continue
53
+ val left = (deadline - System.currentTimeMillis()).toInt()
54
+ if (left <= 0) break
55
+ try {
56
+ return exchange(route, path, token, bytes, left)
57
+ } catch (e: Exception) {
58
+ continue
59
+ }
60
+ }
61
+ if (attempt == 0 && deadline - System.currentTimeMillis() > 3000) Thread.sleep(3000) else break
62
+ }
63
+ return failure(if (install.optBoolean("device")) "Couldn't reach Lamp on your Mac. Keep this device on the same Wi‑Fi as the Mac, with Lamp open, and try again."
64
+ else "Couldn't reach Lamp. Open Lamp on your Mac and try again.")
65
+ }
66
+
67
+ private fun exchange(route: JSONObject, path: String, token: String, body: ByteArray, timeout: Int): Pair<Int, String> {
68
+ Socket().use { socket ->
69
+ socket.connect(InetSocketAddress(route.getString("address"), route.getInt("port")), minOf(4000, timeout))
70
+ socket.soTimeout = timeout
71
+ val head = "POST $path HTTP/1.1\r\nHost: ${route.getString("host")}\r\nAuthorization: Bearer $token\r\n" +
72
+ "Content-Type: application/json\r\nContent-Length: ${body.size}\r\nConnection: close\r\n\r\n"
73
+ socket.getOutputStream().apply { write(head.toByteArray()); write(body); flush() }
74
+ val all = socket.getInputStream().readBytes()
75
+ val split = (0..all.size - 4).firstOrNull { all[it] == '\r'.code.toByte() && all[it + 1] == '\n'.code.toByte() && all[it + 2] == '\r'.code.toByte() && all[it + 3] == '\n'.code.toByte() }
76
+ ?: throw java.io.IOException("Lamp's answer was cut short.")
77
+ val status = String(all, 0, split).split("\r\n").first().split(" ").getOrNull(1)?.toIntOrNull()
78
+ ?: throw java.io.IOException("Lamp's answer couldn't be read.")
79
+ return status to String(all, split + 4, all.size - split - 4)
80
+ }
81
+ }
82
+ }
83
+
84
+ /** The page's LampVoice (Web/lamp.js's `lampVoiceHost`): what the person says, recorded by whatever microphone this
85
+ * device gives an app (a TV remote's included, which Android hands over as the voice-recognition source), as
86
+ * 16 kHz mono 16-bit PCM in a WAV for the page to have transcribed (`LampAI.hear`); and what the page says back,
87
+ * read out. Asking what's possible never prompts: only `listen` asks for the microphone. One recording at a time,
88
+ * each with its own stop (`LampVoiceTake`), tagged with the page's session; a stop keeps it, a cancel throws it
89
+ * away, and the app leaving the screen or the page loading again cancels it whether the page says so or not. Every
90
+ * answer goes back to the page that asked, and none to a page loaded since. Only the page's own origin, in its
91
+ * main frame. */
92
+ object LampPageVoice {
93
+ private const val RATE = 16000
94
+ /** One call from the page: answered on its own page's channel, and only while that page is still the one shown. */
95
+ private class Call(val id: Int, val proxy: androidx.webkit.JavaScriptReplyProxy, val page: Int, val tag: String = "")
96
+ private var activity: android.app.Activity? = null
97
+ private var web: WebView? = null
98
+ @Volatile private var page = 0 // counts the pages loaded
99
+ @Volatile private var take: LampVoiceTake? = null // the recording now
100
+ private var worker: Thread? = null
101
+ private var waiting: Call? = null // the listen asking for the microphone
102
+ private var tts: android.speech.tts.TextToSpeech? = null
103
+ private var ttsReady = false
104
+ private var speeches = 0 // a later speak, hush or cancel overtakes an earlier one
105
+ private var queued: Pair<Call, () -> Unit>? = null // a speak waiting for the voice to start
106
+ private val spoken = java.util.concurrent.ConcurrentHashMap<String, Call>()
107
+
108
+ fun attach(activity: android.app.Activity, web: WebView, origin: String) {
109
+ if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) return
110
+ this.activity = activity; this.web = web
111
+ WebViewCompat.addWebMessageListener(web, "lampVoiceHost", setOf(origin)) { _, message, _, isMainFrame, proxy ->
112
+ val msg = try { JSONObject(message.data ?: "") } catch (e: Exception) { null }
113
+ if (!isMainFrame || msg == null) return@addWebMessageListener
114
+ val tag = msg.optString("session")
115
+ val call = Call(msg.optInt("id"), proxy, page, tag)
116
+ when (msg.optString("cmd")) {
117
+ "status" -> answer(call, status())
118
+ "listen" -> listen(call)
119
+ "stop" -> { take?.takeIf { it.tag == tag }?.stop("stopped"); answer(call, JSONObject().put("ok", true)) }
120
+ "cancel" -> {
121
+ take?.takeIf { it.tag == tag }?.stop("cancelled")
122
+ waiting?.takeIf { it.tag == tag }?.let { waiting = null; answer(it, JSONObject().put("ended", "cancelled")) }
123
+ answer(call, JSONObject().put("ok", true))
124
+ }
125
+ "speak" -> speak(msg.optString("text"), call)
126
+ "hush" -> { hush(); answer(call, JSONObject().put("ok", true)) }
127
+ else -> answer(call, JSONObject().put("error", "Unknown."))
128
+ }
129
+ }
130
+ }
131
+
132
+ /** The app left the screen: nothing goes on being heard or said, and a listen waiting for the microphone is
133
+ * answered as cancelled. */
134
+ fun cancel() {
135
+ waiting?.let { waiting = null; answer(it, JSONObject().put("ended", "cancelled")) }
136
+ take?.stop("cancelled")
137
+ hush()
138
+ }
139
+
140
+ /** The page loads again: as leaving the screen, and nothing still under way answers the new page. */
141
+ fun newPage() { cancel(); page++ }
142
+
143
+ private fun hush() {
144
+ speeches++
145
+ queued?.let { queued = null; answer(it.first, JSONObject().put("done", false)) }
146
+ tts?.stop()
147
+ }
148
+
149
+ private fun granted() = activity?.checkSelfPermission(android.Manifest.permission.RECORD_AUDIO) == android.content.pm.PackageManager.PERMISSION_GRANTED
150
+
151
+ fun status(): JSONObject {
152
+ val a = activity
153
+ val permission = when {
154
+ granted() -> "granted"
155
+ a != null && a.shouldShowRequestPermissionRationale(android.Manifest.permission.RECORD_AUDIO) -> "denied"
156
+ else -> "ask"
157
+ }
158
+ val inputs = (a?.getSystemService(android.content.Context.AUDIO_SERVICE) as? android.media.AudioManager)
159
+ ?.getDevices(android.media.AudioManager.GET_DEVICES_INPUTS)?.size ?: 0
160
+ return JSONObject().put("listen", inputs > 0).put("speak", true).put("permission", permission)
161
+ .put("why", if (inputs > 0) "" else "This device has no microphone.").put("maxSeconds", LampVoiceEnd.MAX)
162
+ }
163
+
164
+ private fun listen(call: Call) {
165
+ cancel()
166
+ if (!granted()) {
167
+ waiting = call
168
+ activity?.requestPermissions(arrayOf(android.Manifest.permission.RECORD_AUDIO), 7307)
169
+ return
170
+ }
171
+ record(call)
172
+ }
173
+
174
+ /** The person's answer to the system's question (MainActivity passes it on). Closed meanwhile: nothing starts. */
175
+ fun permissionResult(code: Int, allowed: Boolean) {
176
+ if (code != 7307) return
177
+ val call = waiting ?: return
178
+ waiting = null
179
+ if (call.page != page) return
180
+ if (!allowed) { answer(call, JSONObject().put("ended", "denied")); return }
181
+ record(call)
182
+ }
183
+
184
+ private fun record(call: Call) {
185
+ val mine = LampVoiceTake(call.tag)
186
+ val before = worker
187
+ take = mine
188
+ worker = thread {
189
+ // The recording before lets the microphone go first (it ends within a frame of being cancelled).
190
+ before?.join(2000)
191
+ val result = try { capture(mine, call) } catch (e: Exception) { JSONObject().put("ended", "error").put("error", e.message ?: "The microphone failed.") }
192
+ web?.post { if (take === mine) take = null }
193
+ answer(call, result)
194
+ }
195
+ }
196
+
197
+ /** Records until the person stops speaking, a stop, a cancel or the limit; levels go to the page as it goes. */
198
+ private fun capture(mine: LampVoiceTake, call: Call): JSONObject {
199
+ mine.stopAs?.let { return JSONObject().put("ended", it) }
200
+ val min = android.media.AudioRecord.getMinBufferSize(RATE, android.media.AudioFormat.CHANNEL_IN_MONO, android.media.AudioFormat.ENCODING_PCM_16BIT)
201
+ @Suppress("MissingPermission")
202
+ val rec = android.media.AudioRecord(android.media.MediaRecorder.AudioSource.VOICE_RECOGNITION, RATE,
203
+ android.media.AudioFormat.CHANNEL_IN_MONO, android.media.AudioFormat.ENCODING_PCM_16BIT, maxOf(min, RATE))
204
+ try {
205
+ if (rec.state != android.media.AudioRecord.STATE_INITIALIZED) return JSONObject().put("ended", "error").put("error", "The microphone didn't open.")
206
+ rec.startRecording()
207
+ if (rec.recordingState != android.media.AudioRecord.RECORDSTATE_RECORDING) return JSONObject().put("ended", "error").put("error", "The microphone didn't start.")
208
+ val routed = rec.routedDevice?.type ?: -1
209
+ // Never waits for audio: the clock ends it on time, and a cancel within a frame, even when nothing comes.
210
+ val ended = mine.run({ buf, from -> rec.read(buf, from, buf.size - from, android.media.AudioRecord.READ_NON_BLOCKING) }, System::nanoTime) { level, speech ->
211
+ event(call, JSONObject().put("event", "level").put("session", mine.tag).put("level", minOf(1.0, level * 4)).put("speech", speech))
212
+ }
213
+ val audio = mine.pcm.toByteArray()
214
+ // The microphone stopped sending mid-way: what it heard may be cut off, so it's an error, not something to act on.
215
+ if (ended == "lost") return JSONObject().put("ended", "error").put("error", "The microphone stopped sending sound. Try again.")
216
+ .put("peak", mine.end.peak).put("speech", mine.end.speechAt >= 0).put("input", routed)
217
+ val out = JSONObject().put("ended", ended).put("seconds", audio.size / 2.0 / RATE).put("peak", mine.end.peak)
218
+ .put("speech", mine.end.speechAt >= 0).put("input", routed)
219
+ if (ended == "stopped" || ended == "quiet" || ended == "cap") out.put("audio", android.util.Base64.encodeToString(wav(audio), android.util.Base64.NO_WRAP))
220
+ return out
221
+ } finally {
222
+ try { rec.stop() } catch (e: Exception) {}
223
+ rec.release()
224
+ }
225
+ }
226
+
227
+ private fun wav(pcm: ByteArray): ByteArray {
228
+ val h = java.nio.ByteBuffer.allocate(44).order(java.nio.ByteOrder.LITTLE_ENDIAN)
229
+ h.put("RIFF".toByteArray()).putInt(36 + pcm.size).put("WAVE".toByteArray()).put("fmt ".toByteArray()).putInt(16).putShort(1).putShort(1)
230
+ .putInt(RATE).putInt(RATE * 2).putShort(2).putShort(16).put("data".toByteArray()).putInt(pcm.size)
231
+ return h.array() + pcm
232
+ }
233
+
234
+ /** Reads the words out; resolves once they're said to the end, or false when they couldn't be or were cut off. */
235
+ private fun speak(text: String, call: Call) {
236
+ val a = activity ?: return answer(call, JSONObject().put("done", false))
237
+ if (text.isEmpty() || take != null) return answer(call, JSONObject().put("done", false))
238
+ hush()
239
+ val asked = speeches
240
+ val say = {
241
+ val u = "u${call.page}-${call.id}"
242
+ if (asked != speeches || call.page != page) answer(call, JSONObject().put("done", false))
243
+ else {
244
+ spoken[u] = call
245
+ if (tts?.speak(text, android.speech.tts.TextToSpeech.QUEUE_FLUSH, null, u) != android.speech.tts.TextToSpeech.SUCCESS)
246
+ spoken.remove(u)?.let { answer(it, JSONObject().put("done", false)) }
247
+ }
248
+ }
249
+ if (tts != null && ttsReady) return say()
250
+ queued = call to say
251
+ if (tts != null) return // already starting: it says the latest when it's ready
252
+ tts = android.speech.tts.TextToSpeech(a) { status ->
253
+ web?.post {
254
+ ttsReady = status == android.speech.tts.TextToSpeech.SUCCESS
255
+ if (ttsReady) tts?.setOnUtteranceProgressListener(object : android.speech.tts.UtteranceProgressListener() {
256
+ override fun onStart(u: String) {}
257
+ override fun onDone(u: String) { spoken.remove(u)?.let { answer(it, JSONObject().put("done", true)) } }
258
+ @Deprecated("Deprecated in Java") override fun onError(u: String) { spoken.remove(u)?.let { answer(it, JSONObject().put("done", false)) } }
259
+ override fun onStop(u: String, interrupted: Boolean) { spoken.remove(u)?.let { answer(it, JSONObject().put("done", false)) } }
260
+ }) else { tts?.shutdown(); tts = null }
261
+ val next = queued; queued = null
262
+ if (next != null) { if (ttsReady) next.second() else answer(next.first, JSONObject().put("done", false)) }
263
+ }
264
+ }
265
+ }
266
+
267
+ private fun answer(call: Call, body: JSONObject) = post(call, body.put("id", call.id))
268
+ private fun event(call: Call, body: JSONObject) = post(call, body)
269
+ private fun post(call: Call, body: JSONObject) {
270
+ val w = web ?: return
271
+ w.post { if (call.page == page) try { call.proxy.postMessage(body.toString()) } catch (e: Exception) {} }
272
+ }
273
+ }
274
+
275
+ /** When one recording ends, from its levels and the clock alone (the same rule as LampVoiceEnd in LampPage.swift).
276
+ * Speech is a level three times the room's, and above a floor. The room is the quietest it has been lately: it
277
+ * follows a dip at once and, until speech is heard, rises back over a couple of seconds, so speech from the very
278
+ * first moment counts once its first pause shows the room; once speech is heard the room stays put, so talking on
279
+ * without a pause is never taken for the room. A recording ends as finished (`quiet`) only after 1.2 s of quiet
280
+ * that was received: audio under the speech level after speech, or under the floor (past 6 s) when there was only
281
+ * sound. Missing audio is never quiet: a stream that stops coming for a second ends as `lost`, which nothing acts
282
+ * on. With no sound at all by 6 s it's `nothing`. Anything still going at 15 s is `cap`, which is never acted on.
283
+ * Asked every frame whether audio comes or not, so these hold when audio stops. A rule of thumb on levels, not a
284
+ * way of telling speech from a television. */
285
+ class LampVoiceEnd {
286
+ companion object { const val MAX = 15.0; const val WAIT = 6.0; const val QUIET = 1.2; const val FLOOR = 0.02; const val RISE = 2.0; const val STALL = 1.0 }
287
+ var speechAt = -1.0; private set
288
+ var peak = 0.0; private set
289
+ private var room = -1.0; private var at = 0.0; private var lastHeard = -1.0; private var sound = false
290
+ // When the quiet that's going on now began, by the audio received: under the speech level, and under the floor.
291
+ private var quietFrom = -1.0; private var silentFrom = -1.0
292
+ @Synchronized fun heard(level: Double, top: Double, t: Double) {
293
+ peak = maxOf(peak, top)
294
+ if (room < 0 || level < room) room = level
295
+ else if (speechAt < 0) room += (level - room) * minOf(1.0, (t - at) / RISE)
296
+ at = t
297
+ lastHeard = t
298
+ if (level > FLOOR) { sound = true; silentFrom = -1.0 } else if (silentFrom < 0) silentFrom = t
299
+ if (level > maxOf(room * 3, FLOOR)) { if (speechAt < 0) speechAt = t; quietFrom = -1.0 }
300
+ else if (quietFrom < 0) quietFrom = t
301
+ }
302
+ @Synchronized fun ended(t: Double): String? = when {
303
+ t >= MAX -> "cap"
304
+ lastHeard < 0 -> if (t >= WAIT) "nothing" else null
305
+ speechAt >= 0 && quietFrom >= 0 && lastHeard - quietFrom >= QUIET -> "quiet"
306
+ speechAt < 0 && sound && t >= WAIT && silentFrom >= 0 && lastHeard - silentFrom >= QUIET -> "quiet"
307
+ speechAt < 0 && !sound && t >= WAIT -> "nothing"
308
+ // Audio that has stopped coming isn't quiet: what was heard may have been cut off.
309
+ t - lastHeard >= STALL -> "lost"
310
+ else -> null
311
+ }
312
+ }
313
+
314
+ /** One recording, with its own stop: a stop or cancel meant for one never reaches another. `run` reads 40 ms
315
+ * frames from `read` (what's there, without waiting: 0 when nothing has come) until a stop, a cancel or
316
+ * LampVoiceEnd says it's over, checking both between every read. */
317
+ class LampVoiceTake(val tag: String) {
318
+ @Volatile var stopAs: String? = null; private set
319
+ val end = LampVoiceEnd()
320
+ val pcm = java.io.ByteArrayOutputStream()
321
+ fun stop(how: String) { if (stopAs == null) stopAs = how }
322
+ fun run(read: (ShortArray, Int) -> Int, now: () -> Long, level: (Double, Boolean) -> Unit): String {
323
+ val started = now()
324
+ val frame = ShortArray(640)
325
+ var filled = 0
326
+ while (true) {
327
+ stopAs?.let { return it }
328
+ end.ended((now() - started) / 1e9)?.let { return it }
329
+ val n = read(frame, filled)
330
+ if (n < 0) throw java.io.IOException("The microphone stopped working ($n).")
331
+ if (n == 0) { Thread.sleep(10); continue }
332
+ filled += n
333
+ if (filled < frame.size) continue
334
+ filled = 0
335
+ var sum = 0.0; var top = 0.0
336
+ for (v in frame) {
337
+ val x = v / 32768.0; sum += x * x; top = maxOf(top, kotlin.math.abs(x))
338
+ pcm.write(v.toInt() and 0xff); pcm.write((v.toInt() shr 8) and 0xff)
339
+ }
340
+ val l = kotlin.math.sqrt(sum / frame.size)
341
+ end.heard(l, top, (now() - started) / 1e9)
342
+ level(l, end.speechAt >= 0)
343
+ }
344
+ }
345
+ }
@@ -0,0 +1,40 @@
1
+ // Written by Lamp before every build; don't edit.
2
+ plugins {
3
+ alias(libs.plugins.android.application)
4
+ }
5
+
6
+ android {
7
+ namespace = "{{ANDROID_PACKAGE}}"
8
+ compileSdk = 36
9
+ defaultConfig {
10
+ applicationId = "{{ANDROID_PACKAGE}}"
11
+ minSdk = 26
12
+ targetSdk = 36
13
+ versionCode = 1
14
+ versionName = "1.0"
15
+ }
16
+ compileOptions {
17
+ sourceCompatibility = JavaVersion.VERSION_17
18
+ targetCompatibility = JavaVersion.VERSION_17
19
+ }
20
+ buildFeatures { buildConfig = false }
21
+ sourceSets["main"].kotlin.srcDirs("src/main/kotlin")
22
+ // The page and the icon, which build.sh puts in the game's build/ before Gradle runs.
23
+ sourceSets["main"].assets.srcDirs("../../../build/android-assets")
24
+ sourceSets["main"].res.srcDirs("../../../build/android-res")
25
+ // The page's files are served as they are: a compressed asset can't be streamed to the WebView. And every
26
+ // folder of the page's goes in: Android's own list of what an app's assets leave out drops any folder whose
27
+ // name starts with an underscore, which is where a built site keeps its scripts (_next, _astro).
28
+ androidResources {
29
+ noCompress += listOf("js", "html", "css", "json", "glb", "gltf", "bin", "png", "jpg", "wav", "ogg", "mp3", "m4a")
30
+ ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
31
+ }
32
+ }
33
+
34
+ kotlin {
35
+ jvmToolchain(17)
36
+ }
37
+
38
+ dependencies {
39
+ implementation(libs.androidx.webkit)
40
+ }
@@ -0,0 +1,4 @@
1
+ // Written by Lamp before every build; don't edit.
2
+ plugins {
3
+ alias(libs.plugins.android.application) apply false
4
+ }
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Written by Lamp before every build; don't edit. -->
3
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
4
+ <!-- Back reaches the page as a key (the game's B, a TV page's Back), not Android's predictive back, which would
5
+ leave the app before the page says what going back means. -->
6
+ <!-- One app for phones and TVs: no touch screen needed, and on a TV's home screen through the leanback launcher. -->
7
+ <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
8
+ <uses-feature android:name="android.hardware.gamepad" android:required="false" />
9
+ <uses-feature android:name="android.software.leanback" android:required="false" />
10
+ <!-- A page may read the web (a screen's weather, a game's scores), as it does in every other place. -->
11
+ <uses-permission android:name="android.permission.INTERNET" />
12
+ <application
13
+ android:allowBackup="true"
14
+ android:banner="@drawable/lamp_banner"
15
+ android:enableOnBackInvokedCallback="false"
16
+ android:icon="@mipmap/ic_launcher"
17
+ android:label="{{NAME}}"
18
+ android:hardwareAccelerated="true"
19
+ android:isGame="true"
20
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
21
+ <activity
22
+ android:name=".MainActivity"
23
+ android:exported="true"
24
+ android:screenOrientation="sensorLandscape"
25
+ android:configChanges="orientation|screenSize|screenLayout|keyboard|keyboardHidden|navigation|uiMode">
26
+ <intent-filter>
27
+ <action android:name="android.intent.action.MAIN" />
28
+ <category android:name="android.intent.category.LAUNCHER" />
29
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
30
+ </intent-filter>
31
+ </activity>
32
+ </application>
33
+ </manifest>
@@ -0,0 +1,173 @@
1
+ // Written by Lamp before every build; don't edit. The game is the page in Web/.
2
+ package {{PACKAGE}}
3
+
4
+ import android.annotation.SuppressLint
5
+ import android.app.Activity
6
+ import android.hardware.input.InputManager
7
+ import android.os.Bundle
8
+ import android.util.Log
9
+ import android.view.InputDevice
10
+ import android.view.KeyEvent
11
+ import android.view.MotionEvent
12
+ import android.view.View
13
+ import android.view.WindowInsets
14
+ import android.view.WindowInsetsController
15
+ import android.webkit.ConsoleMessage
16
+ import android.webkit.WebChromeClient
17
+ import android.webkit.WebResourceRequest
18
+ import android.webkit.WebResourceResponse
19
+ import android.webkit.WebView
20
+ import android.webkit.WebViewClient
21
+ import kotlin.math.abs
22
+
23
+ class MainActivity : Activity(), InputManager.InputDeviceListener {
24
+ private lateinit var web: WebView
25
+ // The first controller, as the page's pad 0 is: sticks -1 to 1 (up positive), triggers 0 to 1, buttons as Lamp's bits.
26
+ private var lx = 0f; private var ly = 0f; private var rx = 0f; private var ry = 0f; private var lt = 0f; private var rt = 0f
27
+ private var down = 0
28
+ // The controller's USB vendor, so the page names its buttons as they're marked (a PlayStation's shapes).
29
+ private var vendor = 0
30
+ private var sent = ""
31
+
32
+ @SuppressLint("SetJavaScriptEnabled")
33
+ override fun onCreate(saved: Bundle?) {
34
+ super.onCreate(saved)
35
+ web = WebView(this)
36
+ web.setBackgroundColor(0xFF000000.toInt())
37
+ web.settings.javaScriptEnabled = true
38
+ web.settings.domStorageEnabled = true
39
+ web.settings.mediaPlaybackRequiresUserGesture = false
40
+ web.settings.allowFileAccess = false
41
+ web.webViewClient = object : WebViewClient() {
42
+ // The page's files, from the app's own assets, at an address of the game's own.
43
+ override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
44
+ val url = request.url
45
+ if (url.host != HOST) return null
46
+ val path = (url.path ?: "/").trimStart('/').ifEmpty { "index.html" }
47
+ if (path.split('/').any { it == ".." }) return notFound()
48
+ return try {
49
+ WebResourceResponse(type(path), "utf-8", assets.open("Web/$path"))
50
+ } catch (e: Exception) { notFound() }
51
+ }
52
+ override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = request.url.host != HOST
53
+ }
54
+ web.webChromeClient = object : WebChromeClient() {
55
+ override fun onConsoleMessage(message: ConsoleMessage): Boolean {
56
+ Log.i("Lamp", message.message())
57
+ return true
58
+ }
59
+ }
60
+ setContentView(web)
61
+ LampPageGateway.attach(web, "https://$HOST")
62
+ (getSystemService(INPUT_SERVICE) as InputManager).registerInputDeviceListener(this, null)
63
+ immerse()
64
+ web.loadUrl("https://$HOST/index.html")
65
+ }
66
+
67
+ override fun onWindowFocusChanged(focused: Boolean) {
68
+ super.onWindowFocusChanged(focused)
69
+ if (focused) immerse()
70
+ }
71
+
72
+ private fun immerse() {
73
+ if (android.os.Build.VERSION.SDK_INT >= 30) {
74
+ window.insetsController?.let {
75
+ it.hide(WindowInsets.Type.systemBars())
76
+ it.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
77
+ }
78
+ } else {
79
+ @Suppress("DEPRECATION")
80
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
81
+ }
82
+ }
83
+
84
+ override fun onPause() {
85
+ super.onPause()
86
+ web.onPause()
87
+ }
88
+
89
+ // A controller that goes away mid-press leaves nothing held: the page sees it let go, and its touch controls
90
+ // can come back.
91
+ override fun onInputDeviceRemoved(id: Int) {
92
+ lx = 0f; ly = 0f; rx = 0f; ry = 0f; lt = 0f; rt = 0f; down = 0
93
+ send()
94
+ }
95
+ override fun onInputDeviceAdded(id: Int) {}
96
+ override fun onInputDeviceChanged(id: Int) {}
97
+ override fun onDestroy() {
98
+ (getSystemService(INPUT_SERVICE) as InputManager).unregisterInputDeviceListener(this)
99
+ super.onDestroy()
100
+ }
101
+ override fun onResume() { super.onResume(); web.onResume() }
102
+
103
+ // MARK: The controller
104
+
105
+ private fun bit(code: Int): Int = when (code) {
106
+ KeyEvent.KEYCODE_BUTTON_A -> 1; KeyEvent.KEYCODE_BUTTON_B -> 2; KeyEvent.KEYCODE_BUTTON_X -> 4; KeyEvent.KEYCODE_BUTTON_Y -> 8
107
+ KeyEvent.KEYCODE_BUTTON_L1 -> 16; KeyEvent.KEYCODE_BUTTON_R1 -> 32
108
+ KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_MENU -> 64; KeyEvent.KEYCODE_BUTTON_SELECT -> 128
109
+ KeyEvent.KEYCODE_DPAD_UP -> 256; KeyEvent.KEYCODE_DPAD_DOWN -> 512; KeyEvent.KEYCODE_DPAD_LEFT -> 1024; KeyEvent.KEYCODE_DPAD_RIGHT -> 2048
110
+ KeyEvent.KEYCODE_BUTTON_THUMBL -> 4096; KeyEvent.KEYCODE_BUTTON_THUMBR -> 8192
111
+ else -> 0
112
+ }
113
+
114
+ // A TV remote's d-pad is a controller's to a game: its arrows the d-pad, OK the A button, play/pause Start.
115
+ private fun fromController(event: KeyEvent) =
116
+ event.isFromSource(InputDevice.SOURCE_GAMEPAD) || event.isFromSource(InputDevice.SOURCE_JOYSTICK) || KeyEvent.isGamepadButton(event.keyCode)
117
+ || event.isFromSource(InputDevice.SOURCE_DPAD) || event.keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
118
+ private fun remote(code: Int) = when (code) {
119
+ KeyEvent.KEYCODE_DPAD_CENTER -> KeyEvent.KEYCODE_BUTTON_A; KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> KeyEvent.KEYCODE_BUTTON_START; else -> code
120
+ }
121
+
122
+
123
+ override fun dispatchKeyEvent(event: KeyEvent): Boolean {
124
+ // Back is the game's B, as on a console: the game says what going back means, and stays open.
125
+ val code = if (event.keyCode == KeyEvent.KEYCODE_BACK) KeyEvent.KEYCODE_BUTTON_B else remote(event.keyCode)
126
+ val b = bit(code)
127
+ if (b != 0 && (fromController(event) || event.keyCode == KeyEvent.KEYCODE_BACK)) {
128
+ if (fromController(event)) vendor = event.device?.vendorId ?: 0
129
+ down = if (event.action == KeyEvent.ACTION_DOWN) down or b else down and b.inv()
130
+ send()
131
+ return true
132
+ }
133
+ return super.dispatchKeyEvent(event)
134
+ }
135
+
136
+ private fun dead(v: Float) = if (abs(v) < 0.15f) 0f else (v - Math.signum(v) * 0.15f) / 0.85f
137
+
138
+ override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
139
+ if (!event.isFromSource(InputDevice.SOURCE_JOYSTICK) || event.action != MotionEvent.ACTION_MOVE) return super.dispatchGenericMotionEvent(event)
140
+ vendor = event.device?.vendorId ?: 0
141
+ lx = dead(event.getAxisValue(MotionEvent.AXIS_X)); ly = -dead(event.getAxisValue(MotionEvent.AXIS_Y))
142
+ rx = dead(event.getAxisValue(MotionEvent.AXIS_Z)); ry = -dead(event.getAxisValue(MotionEvent.AXIS_RZ))
143
+ lt = maxOf(event.getAxisValue(MotionEvent.AXIS_LTRIGGER), event.getAxisValue(MotionEvent.AXIS_BRAKE))
144
+ rt = maxOf(event.getAxisValue(MotionEvent.AXIS_RTRIGGER), event.getAxisValue(MotionEvent.AXIS_GAS))
145
+ // A d-pad that reports as a hat.
146
+ val hx = event.getAxisValue(MotionEvent.AXIS_HAT_X); val hy = event.getAxisValue(MotionEvent.AXIS_HAT_Y)
147
+ down = down and (256 or 512 or 1024 or 2048).inv() or
148
+ (if (hy < -0.5f) 256 else 0) or (if (hy > 0.5f) 512 else 0) or (if (hx < -0.5f) 1024 else 0) or (if (hx > 0.5f) 2048 else 0)
149
+ send()
150
+ return true
151
+ }
152
+
153
+ private fun send() {
154
+ val now = "$lx,$ly,$rx,$ry,$lt,$rt,$down,$vendor"
155
+ if (now == sent) return
156
+ sent = now
157
+ web.evaluateJavascript("window.lampNativePad&&lampNativePad($now)", null)
158
+ }
159
+
160
+ private fun type(path: String) = when (path.substringAfterLast('.', "").lowercase()) {
161
+ "html", "htm" -> "text/html"; "js", "mjs" -> "text/javascript"; "css" -> "text/css"; "json" -> "application/json"
162
+ "png" -> "image/png"; "jpg", "jpeg" -> "image/jpeg"; "webp" -> "image/webp"; "svg" -> "image/svg+xml"
163
+ "glb" -> "model/gltf-binary"; "gltf" -> "model/gltf+json"; "wav" -> "audio/wav"; "ogg" -> "audio/ogg"
164
+ "mp3" -> "audio/mpeg"; "m4a" -> "audio/mp4"; "wasm" -> "application/wasm"
165
+ else -> "application/octet-stream"
166
+ }
167
+
168
+ private fun notFound() = WebResourceResponse("text/plain", "utf-8", 404, "Not Found", emptyMap(), "".byteInputStream())
169
+
170
+ companion object {
171
+ const val HOST = "lamp.game"
172
+ }
173
+ }
@@ -0,0 +1,39 @@
1
+ # Written by Lamp before every build; don't edit. Add other libraries in app/build.gradle.kts with their full names.
2
+ [versions]
3
+ androidGradlePlugin = "9.0.1"
4
+ androidxCore = "1.18.0"
5
+ androidxLifecycle = "2.10.0"
6
+ androidxActivity = "1.13.0"
7
+ androidxComposeBom = "2026.03.01"
8
+ androidxNavigation3 = "1.0.1"
9
+ androidxDatastore = "1.1.7"
10
+ androidxWebkit = "1.14.0"
11
+ androidxTvMaterial = "1.1.0"
12
+ coroutines = "1.10.2"
13
+ kotlin = "2.3.20"
14
+ serialization = "1.9.0"
15
+
16
+ [libraries]
17
+ androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCore" }
18
+ androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivity" }
19
+ androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
20
+ androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
21
+ androidx-compose-material-icons = { group = "androidx.compose.material", name = "material-icons-extended" }
22
+ androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
23
+ androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
24
+ androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
25
+ androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" }
26
+ androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidxLifecycle" }
27
+ androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" }
28
+ androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "androidxNavigation3" }
29
+ androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "androidxNavigation3" }
30
+ androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "androidxDatastore" }
31
+ kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
32
+ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
33
+ androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "androidxWebkit" }
34
+ androidx-tv-material = { module = "androidx.tv:tv-material", version.ref = "androidxTvMaterial" }
35
+
36
+ [plugins]
37
+ android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
38
+ compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
39
+ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
@@ -0,0 +1,8 @@
1
+ # Written by Lamp before every build; don't edit.
2
+ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -Dlamp.app={{SLUG}}
3
+ org.gradle.daemon.idletimeout=600000
4
+ org.gradle.caching=true
5
+ org.gradle.configuration-cache=true
6
+ android.useAndroidX=true
7
+ kotlin.code.style=official
8
+ android.nonTransitiveRClass=true