From d56397bc949ec11e20b024a8f6d2ba57b0538281 Mon Sep 17 00:00:00 2001 From: Michal Ziulek Date: Tue, 20 Sep 2022 11:24:57 +0200 Subject: [PATCH 1/3] Created Windows branch. --- build.zig | 211 +- libs/common/build.zig | 10 +- libs/zaudio/README.md | 72 - libs/zaudio/build.zig | 53 - libs/zaudio/libs/miniaudio/miniaudio.c | 2 - libs/zaudio/libs/miniaudio/miniaudio.h | 90463 ---------------- libs/zaudio/libs/miniaudio/zaudio.c | 661 - libs/zaudio/src/zaudio.zig | 2410 - libs/zaudio/system_sdk.zig | 300 - libs/zbullet/src/zbullet.zig | 10 +- libs/zglfw/README.md | 25 - libs/zglfw/build.zig | 112 - libs/zglfw/libs/glfw/LICENSE.md | 23 - libs/zglfw/libs/glfw/include/GLFW/glfw3.h | 5912 - .../libs/glfw/include/GLFW/glfw3native.h | 628 - libs/zglfw/libs/glfw/src/cocoa_init.m | 633 - libs/zglfw/libs/glfw/src/cocoa_joystick.h | 51 - libs/zglfw/libs/glfw/src/cocoa_joystick.m | 488 - libs/zglfw/libs/glfw/src/cocoa_monitor.m | 627 - libs/zglfw/libs/glfw/src/cocoa_platform.h | 220 - libs/zglfw/libs/glfw/src/cocoa_time.c | 62 - libs/zglfw/libs/glfw/src/cocoa_window.m | 1934 - libs/zglfw/libs/glfw/src/context.c | 758 - libs/zglfw/libs/glfw/src/egl_context.c | 809 - libs/zglfw/libs/glfw/src/egl_context.h | 217 - libs/zglfw/libs/glfw/src/glx_context.c | 701 - libs/zglfw/libs/glfw/src/glx_context.h | 179 - libs/zglfw/libs/glfw/src/init.c | 420 - libs/zglfw/libs/glfw/src/input.c | 1380 - libs/zglfw/libs/glfw/src/internal.h | 786 - libs/zglfw/libs/glfw/src/linux_joystick.c | 433 - libs/zglfw/libs/glfw/src/linux_joystick.h | 63 - libs/zglfw/libs/glfw/src/mappings.h | 1001 - libs/zglfw/libs/glfw/src/monitor.c | 542 - libs/zglfw/libs/glfw/src/nsgl_context.h | 66 - libs/zglfw/libs/glfw/src/nsgl_context.m | 376 - libs/zglfw/libs/glfw/src/null_init.c | 52 - libs/zglfw/libs/glfw/src/null_joystick.c | 44 - libs/zglfw/libs/glfw/src/null_joystick.h | 31 - libs/zglfw/libs/glfw/src/null_monitor.c | 77 - libs/zglfw/libs/glfw/src/null_platform.h | 62 - libs/zglfw/libs/glfw/src/null_window.c | 335 - libs/zglfw/libs/glfw/src/osmesa_context.c | 386 - libs/zglfw/libs/glfw/src/osmesa_context.h | 92 - libs/zglfw/libs/glfw/src/posix_thread.c | 105 - libs/zglfw/libs/glfw/src/posix_thread.h | 49 - libs/zglfw/libs/glfw/src/posix_time.c | 87 - libs/zglfw/libs/glfw/src/posix_time.h | 43 - libs/zglfw/libs/glfw/src/vulkan.c | 334 - libs/zglfw/libs/glfw/src/wgl_context.c | 798 - libs/zglfw/libs/glfw/src/wgl_context.h | 158 - libs/zglfw/libs/glfw/src/win32_init.c | 638 - libs/zglfw/libs/glfw/src/win32_joystick.c | 755 - libs/zglfw/libs/glfw/src/win32_joystick.h | 57 - libs/zglfw/libs/glfw/src/win32_monitor.c | 548 - libs/zglfw/libs/glfw/src/win32_platform.h | 458 - libs/zglfw/libs/glfw/src/win32_thread.c | 99 - libs/zglfw/libs/glfw/src/win32_time.c | 60 - libs/zglfw/libs/glfw/src/win32_window.c | 2396 - libs/zglfw/libs/glfw/src/window.c | 1101 - libs/zglfw/libs/glfw/src/wl_init.c | 595 - libs/zglfw/libs/glfw/src/wl_monitor.c | 271 - libs/zglfw/libs/glfw/src/wl_platform.h | 373 - libs/zglfw/libs/glfw/src/wl_window.c | 2794 - libs/zglfw/libs/glfw/src/x11_init.c | 1274 - libs/zglfw/libs/glfw/src/x11_monitor.c | 614 - libs/zglfw/libs/glfw/src/x11_platform.h | 450 - libs/zglfw/libs/glfw/src/x11_window.c | 3174 - libs/zglfw/libs/glfw/src/xkb_unicode.c | 942 - libs/zglfw/libs/glfw/src/xkb_unicode.h | 30 - libs/zglfw/src/zglfw.zig | 519 - libs/zglfw/system_sdk.zig | 300 - libs/zgpu/README.md | 147 - libs/zgpu/build.zig | 99 - libs/zgpu/libs/dawn | 1 - libs/zgpu/src/common_wgsl.zig | 99 - libs/zgpu/src/dawn.cpp | 32 - libs/zgpu/src/wgpu.zig | 2775 - libs/zgpu/src/zgpu.zig | 1744 - libs/zgui/README.md | 71 - libs/zgui/build.zig | 29 - libs/zgui/libs/imgui/LICENSE.txt | 21 - libs/zgui/libs/imgui/imconfig.h | 126 - libs/zgui/libs/imgui/imgui.cpp | 13444 --- libs/zgui/libs/imgui/imgui.h | 3065 - libs/zgui/libs/imgui/imgui_demo.cpp | 7969 -- libs/zgui/libs/imgui/imgui_draw.cpp | 4162 - libs/zgui/libs/imgui/imgui_impl_glfw.cpp | 699 - libs/zgui/libs/imgui/imgui_impl_wgpu.cpp | 732 - libs/zgui/libs/imgui/imgui_internal.h | 2975 - libs/zgui/libs/imgui/imgui_tables.cpp | 4068 - libs/zgui/libs/imgui/imgui_widgets.cpp | 8394 -- libs/zgui/libs/imgui/implot.cpp | 5721 - libs/zgui/libs/imgui/implot.h | 1285 - libs/zgui/libs/imgui/implot_demo.cpp | 2446 - libs/zgui/libs/imgui/implot_internal.h | 1656 - libs/zgui/libs/imgui/implot_items.cpp | 2824 - libs/zgui/libs/imgui/imstb_rectpack.h | 627 - libs/zgui/libs/imgui/imstb_textedit.h | 1447 - libs/zgui/libs/imgui/imstb_truetype.h | 5085 - libs/zgui/src/main.zig | 2 - libs/zgui/src/zgui.cpp | 1672 - libs/zgui/src/zgui.zig | 3192 - libs/zgui/src/zgui_glfw_wgpu.zig | 46 - libs/zjolt/build.zig | 173 - .../libs/Jolt/AABBTree/AABBTreeBuilder.cpp | 224 - .../libs/Jolt/AABBTree/AABBTreeBuilder.h | 109 - .../libs/Jolt/AABBTree/AABBTreeToBuffer.h | 244 - .../NodeCodec/NodeCodecQuadTreeHalfFloat.h | 286 - .../TriangleCodecIndexed8BitPackSOA4Flags.h | 425 - libs/zjolt/libs/Jolt/Core/Atomics.h | 34 - libs/zjolt/libs/Jolt/Core/ByteBuffer.h | 73 - libs/zjolt/libs/Jolt/Core/Color.cpp | 37 - libs/zjolt/libs/Jolt/Core/Color.h | 76 - libs/zjolt/libs/Jolt/Core/Core.h | 329 - libs/zjolt/libs/Jolt/Core/FPControlWord.h | 70 - libs/zjolt/libs/Jolt/Core/FPException.h | 55 - libs/zjolt/libs/Jolt/Core/FPFlushDenormals.h | 31 - libs/zjolt/libs/Jolt/Core/Factory.cpp | 86 - libs/zjolt/libs/Jolt/Core/Factory.h | 53 - libs/zjolt/libs/Jolt/Core/FixedSizeFreeList.h | 122 - .../libs/Jolt/Core/FixedSizeFreeList.inl | 206 - libs/zjolt/libs/Jolt/Core/HashCombine.h | 79 - libs/zjolt/libs/Jolt/Core/InsertionSort.h | 57 - libs/zjolt/libs/Jolt/Core/IssueReporting.cpp | 30 - libs/zjolt/libs/Jolt/Core/IssueReporting.h | 37 - libs/zjolt/libs/Jolt/Core/JobSystem.h | 275 - libs/zjolt/libs/Jolt/Core/JobSystem.inl | 55 - .../libs/Jolt/Core/JobSystemThreadPool.cpp | 573 - .../libs/Jolt/Core/JobSystemThreadPool.h | 162 - libs/zjolt/libs/Jolt/Core/LinearCurve.cpp | 50 - libs/zjolt/libs/Jolt/Core/LinearCurve.h | 66 - libs/zjolt/libs/Jolt/Core/LockFreeHashMap.h | 184 - libs/zjolt/libs/Jolt/Core/LockFreeHashMap.inl | 350 - libs/zjolt/libs/Jolt/Core/Memory.cpp | 71 - libs/zjolt/libs/Jolt/Core/Memory.h | 54 - libs/zjolt/libs/Jolt/Core/Mutex.h | 214 - libs/zjolt/libs/Jolt/Core/MutexArray.h | 97 - libs/zjolt/libs/Jolt/Core/NonCopyable.h | 17 - libs/zjolt/libs/Jolt/Core/Profiler.cpp | 401 - libs/zjolt/libs/Jolt/Core/Profiler.h | 265 - libs/zjolt/libs/Jolt/Core/Profiler.inl | 87 - libs/zjolt/libs/Jolt/Core/QuickSort.h | 136 - libs/zjolt/libs/Jolt/Core/RTTI.cpp | 142 - libs/zjolt/libs/Jolt/Core/RTTI.h | 435 - libs/zjolt/libs/Jolt/Core/Reference.h | 230 - libs/zjolt/libs/Jolt/Core/Result.h | 173 - .../libs/Jolt/Core/STLAlignedAllocator.h | 65 - libs/zjolt/libs/Jolt/Core/STLAllocator.h | 101 - libs/zjolt/libs/Jolt/Core/STLTempAllocator.h | 76 - libs/zjolt/libs/Jolt/Core/StaticArray.h | 307 - libs/zjolt/libs/Jolt/Core/StreamIn.h | 70 - libs/zjolt/libs/Jolt/Core/StreamOut.h | 56 - libs/zjolt/libs/Jolt/Core/StreamWrapper.h | 52 - libs/zjolt/libs/Jolt/Core/StringTools.cpp | 99 - libs/zjolt/libs/Jolt/Core/StringTools.h | 50 - libs/zjolt/libs/Jolt/Core/TempAllocator.h | 114 - libs/zjolt/libs/Jolt/Core/TickCounter.cpp | 117 - libs/zjolt/libs/Jolt/Core/TickCounter.h | 43 - libs/zjolt/libs/Jolt/Core/UnorderedMap.h | 14 - libs/zjolt/libs/Jolt/Core/UnorderedSet.h | 14 - libs/zjolt/libs/Jolt/Geometry/AABox.h | 281 - libs/zjolt/libs/Jolt/Geometry/AABox4.h | 210 - libs/zjolt/libs/Jolt/Geometry/ClipPoly.h | 199 - libs/zjolt/libs/Jolt/Geometry/ClosestPoint.h | 421 - .../libs/Jolt/Geometry/ConvexHullBuilder.cpp | 1464 - .../libs/Jolt/Geometry/ConvexHullBuilder.h | 275 - .../Jolt/Geometry/ConvexHullBuilder2D.cpp | 335 - .../libs/Jolt/Geometry/ConvexHullBuilder2D.h | 104 - libs/zjolt/libs/Jolt/Geometry/ConvexSupport.h | 187 - .../libs/Jolt/Geometry/EPAConvexHullBuilder.h | 826 - .../libs/Jolt/Geometry/EPAPenetrationDepth.h | 450 - libs/zjolt/libs/Jolt/Geometry/Ellipse.h | 76 - .../libs/Jolt/Geometry/GJKClosestPoint.h | 964 - .../libs/Jolt/Geometry/IndexedTriangle.h | 110 - libs/zjolt/libs/Jolt/Geometry/Indexify.cpp | 71 - libs/zjolt/libs/Jolt/Geometry/Indexify.h | 18 - libs/zjolt/libs/Jolt/Geometry/MortonCode.h | 39 - libs/zjolt/libs/Jolt/Geometry/OrientedBox.cpp | 177 - libs/zjolt/libs/Jolt/Geometry/OrientedBox.h | 38 - libs/zjolt/libs/Jolt/Geometry/Plane.h | 75 - libs/zjolt/libs/Jolt/Geometry/RayAABox.h | 240 - libs/zjolt/libs/Jolt/Geometry/RayAABox8.h | 75 - libs/zjolt/libs/Jolt/Geometry/RayCapsule.h | 36 - libs/zjolt/libs/Jolt/Geometry/RayCylinder.h | 100 - libs/zjolt/libs/Jolt/Geometry/RaySphere.h | 95 - libs/zjolt/libs/Jolt/Geometry/RayTriangle.h | 157 - libs/zjolt/libs/Jolt/Geometry/RayTriangle8.h | 90 - libs/zjolt/libs/Jolt/Geometry/Sphere.h | 71 - libs/zjolt/libs/Jolt/Geometry/Triangle.h | 33 - libs/zjolt/libs/Jolt/Jolt.h | 13 - libs/zjolt/libs/Jolt/Math/DVec3.h | 223 - libs/zjolt/libs/Jolt/Math/DVec3.inl | 281 - .../libs/Jolt/Math/EigenValueSymmetric.h | 173 - libs/zjolt/libs/Jolt/Math/FindRoot.h | 41 - libs/zjolt/libs/Jolt/Math/Float2.h | 34 - libs/zjolt/libs/Jolt/Math/Float3.h | 48 - libs/zjolt/libs/Jolt/Math/Float4.h | 32 - .../libs/Jolt/Math/GaussianElimination.h | 101 - libs/zjolt/libs/Jolt/Math/HalfFloat.h | 211 - libs/zjolt/libs/Jolt/Math/Mat44.h | 226 - libs/zjolt/libs/Jolt/Math/Mat44.inl | 1118 - libs/zjolt/libs/Jolt/Math/Math.h | 166 - libs/zjolt/libs/Jolt/Math/MathTypes.h | 27 - libs/zjolt/libs/Jolt/Math/Matrix.h | 258 - libs/zjolt/libs/Jolt/Math/Quat.h | 244 - libs/zjolt/libs/Jolt/Math/Quat.inl | 316 - libs/zjolt/libs/Jolt/Math/Swizzle.h | 18 - libs/zjolt/libs/Jolt/Math/Trigonometry.h | 58 - libs/zjolt/libs/Jolt/Math/UVec4.cpp | 18 - libs/zjolt/libs/Jolt/Math/UVec4.h | 218 - libs/zjolt/libs/Jolt/Math/UVec4.inl | 529 - libs/zjolt/libs/Jolt/Math/UVec8.h | 99 - libs/zjolt/libs/Jolt/Math/UVec8.inl | 137 - libs/zjolt/libs/Jolt/Math/Vec3.cpp | 58 - libs/zjolt/libs/Jolt/Math/Vec3.h | 284 - libs/zjolt/libs/Jolt/Math/Vec3.inl | 775 - libs/zjolt/libs/Jolt/Math/Vec4.h | 275 - libs/zjolt/libs/Jolt/Math/Vec4.inl | 921 - libs/zjolt/libs/Jolt/Math/Vec8.h | 111 - libs/zjolt/libs/Jolt/Math/Vec8.inl | 143 - libs/zjolt/libs/Jolt/Math/Vector.h | 208 - .../ObjectStream/GetPrimitiveTypeOfType.h | 53 - .../libs/Jolt/ObjectStream/ObjectStream.cpp | 33 - .../libs/Jolt/ObjectStream/ObjectStream.h | 317 - .../ObjectStream/ObjectStreamBinaryIn.cpp | 192 - .../Jolt/ObjectStream/ObjectStreamBinaryIn.h | 46 - .../ObjectStream/ObjectStreamBinaryOut.cpp | 126 - .../Jolt/ObjectStream/ObjectStreamBinaryOut.h | 46 - .../libs/Jolt/ObjectStream/ObjectStreamIn.cpp | 546 - .../libs/Jolt/ObjectStream/ObjectStreamIn.h | 142 - .../Jolt/ObjectStream/ObjectStreamOut.cpp | 163 - .../libs/Jolt/ObjectStream/ObjectStreamOut.h | 99 - .../Jolt/ObjectStream/ObjectStreamTextIn.cpp | 345 - .../Jolt/ObjectStream/ObjectStreamTextIn.h | 44 - .../Jolt/ObjectStream/ObjectStreamTextOut.cpp | 185 - .../Jolt/ObjectStream/ObjectStreamTextOut.h | 51 - .../Jolt/ObjectStream/ObjectStreamTypes.h | 18 - .../Jolt/ObjectStream/SerializableAttribute.h | 100 - .../ObjectStream/SerializableAttributeEnum.h | 53 - .../ObjectStream/SerializableAttributeTyped.h | 46 - .../Jolt/ObjectStream/SerializableObject.cpp | 14 - .../Jolt/ObjectStream/SerializableObject.h | 154 - .../Jolt/ObjectStream/TypeDeclarations.cpp | 50 - .../libs/Jolt/ObjectStream/TypeDeclarations.h | 36 - libs/zjolt/libs/Jolt/Physics/Body/Body.cpp | 326 - libs/zjolt/libs/Jolt/Physics/Body/Body.h | 324 - libs/zjolt/libs/Jolt/Physics/Body/Body.inl | 191 - .../libs/Jolt/Physics/Body/BodyAccess.cpp | 17 - .../zjolt/libs/Jolt/Physics/Body/BodyAccess.h | 51 - .../Physics/Body/BodyActivationListener.h | 27 - .../Physics/Body/BodyCreationSettings.cpp | 254 - .../Jolt/Physics/Body/BodyCreationSettings.h | 114 - .../zjolt/libs/Jolt/Physics/Body/BodyFilter.h | 101 - libs/zjolt/libs/Jolt/Physics/Body/BodyID.h | 99 - .../libs/Jolt/Physics/Body/BodyInterface.cpp | 842 - .../libs/Jolt/Physics/Body/BodyInterface.h | 204 - libs/zjolt/libs/Jolt/Physics/Body/BodyLock.h | 110 - .../Jolt/Physics/Body/BodyLockInterface.h | 133 - .../libs/Jolt/Physics/Body/BodyLockMulti.h | 103 - .../libs/Jolt/Physics/Body/BodyManager.cpp | 777 - .../libs/Jolt/Physics/Body/BodyManager.h | 288 - libs/zjolt/libs/Jolt/Physics/Body/BodyPair.h | 32 - .../libs/Jolt/Physics/Body/MassProperties.cpp | 184 - .../libs/Jolt/Physics/Body/MassProperties.h | 57 - .../Jolt/Physics/Body/MotionProperties.cpp | 46 - .../libs/Jolt/Physics/Body/MotionProperties.h | 188 - .../Jolt/Physics/Body/MotionProperties.inl | 127 - .../libs/Jolt/Physics/Body/MotionQuality.h | 30 - .../zjolt/libs/Jolt/Physics/Body/MotionType.h | 16 - .../libs/Jolt/Physics/Character/Character.cpp | 301 - .../libs/Jolt/Physics/Character/Character.h | 137 - .../Jolt/Physics/Character/CharacterBase.cpp | 40 - .../Jolt/Physics/Character/CharacterBase.h | 111 - .../Physics/Character/CharacterVirtual.cpp | 955 - .../Jolt/Physics/Character/CharacterVirtual.h | 336 - .../libs/Jolt/Physics/Collision/AABoxCast.h | 19 - .../Jolt/Physics/Collision/ActiveEdgeMode.h | 16 - .../libs/Jolt/Physics/Collision/ActiveEdges.h | 112 - .../Jolt/Physics/Collision/BackFaceMode.h | 15 - .../Collision/BroadPhase/BroadPhase.cpp | 15 - .../Physics/Collision/BroadPhase/BroadPhase.h | 108 - .../BroadPhase/BroadPhaseBruteForce.cpp | 302 - .../BroadPhase/BroadPhaseBruteForce.h | 36 - .../Collision/BroadPhase/BroadPhaseLayer.h | 131 - .../BroadPhase/BroadPhaseQuadTree.cpp | 588 - .../Collision/BroadPhase/BroadPhaseQuadTree.h | 100 - .../Collision/BroadPhase/BroadPhaseQuery.h | 51 - .../Physics/Collision/BroadPhase/QuadTree.cpp | 1647 - .../Physics/Collision/BroadPhase/QuadTree.h | 378 - .../Collision/CastConvexVsTriangles.cpp | 114 - .../Physics/Collision/CastConvexVsTriangles.h | 47 - .../libs/Jolt/Physics/Collision/CastResult.h | 33 - .../Collision/CastSphereVsTriangles.cpp | 218 - .../Physics/Collision/CastSphereVsTriangles.h | 50 - .../Jolt/Physics/Collision/CollectFacesMode.h | 15 - .../Collision/CollideConvexVsTriangles.cpp | 154 - .../Collision/CollideConvexVsTriangles.h | 55 - .../Physics/Collision/CollidePointResult.h | 24 - .../Jolt/Physics/Collision/CollideShape.h | 104 - .../Collision/CollideSphereVsTriangles.cpp | 112 - .../Collision/CollideSphereVsTriangles.h | 49 - .../Physics/Collision/CollisionCollector.h | 90 - .../Collision/CollisionCollectorImpl.h | 133 - .../Physics/Collision/CollisionDispatch.cpp | 104 - .../Physics/Collision/CollisionDispatch.h | 96 - .../Jolt/Physics/Collision/CollisionGroup.cpp | 32 - .../Jolt/Physics/Collision/CollisionGroup.h | 93 - .../Jolt/Physics/Collision/ContactListener.h | 92 - .../Jolt/Physics/Collision/GroupFilter.cpp | 62 - .../libs/Jolt/Physics/Collision/GroupFilter.h | 40 - .../Physics/Collision/GroupFilterTable.cpp | 37 - .../Jolt/Physics/Collision/GroupFilterTable.h | 129 - .../Collision/ManifoldBetweenTwoFaces.cpp | 228 - .../Collision/ManifoldBetweenTwoFaces.h | 30 - .../Physics/Collision/NarrowPhaseQuery.cpp | 415 - .../Jolt/Physics/Collision/NarrowPhaseQuery.h | 54 - .../Physics/Collision/NarrowPhaseStats.cpp | 52 - .../Jolt/Physics/Collision/NarrowPhaseStats.h | 109 - .../libs/Jolt/Physics/Collision/ObjectLayer.h | 90 - .../Physics/Collision/PhysicsMaterial.cpp | 65 - .../Jolt/Physics/Collision/PhysicsMaterial.h | 51 - .../Collision/PhysicsMaterialSimple.cpp | 37 - .../Physics/Collision/PhysicsMaterialSimple.h | 36 - .../libs/Jolt/Physics/Collision/RayCast.h | 46 - .../Jolt/Physics/Collision/Shape/BoxShape.cpp | 252 - .../Jolt/Physics/Collision/Shape/BoxShape.h | 108 - .../Physics/Collision/Shape/CapsuleShape.cpp | 385 - .../Physics/Collision/Shape/CapsuleShape.h | 123 - .../Physics/Collision/Shape/CompoundShape.cpp | 389 - .../Physics/Collision/Shape/CompoundShape.h | 336 - .../Collision/Shape/CompoundShapeVisitors.h | 459 - .../Collision/Shape/ConvexHullShape.cpp | 1196 - .../Physics/Collision/Shape/ConvexHullShape.h | 171 - .../Physics/Collision/Shape/ConvexShape.cpp | 571 - .../Physics/Collision/Shape/ConvexShape.h | 155 - .../Physics/Collision/Shape/CylinderShape.cpp | 353 - .../Physics/Collision/Shape/CylinderShape.h | 119 - .../Collision/Shape/DecoratedShape.cpp | 81 - .../Physics/Collision/Shape/DecoratedShape.h | 66 - .../Collision/Shape/GetTrianglesContext.h | 243 - .../Collision/Shape/HeightFieldShape.cpp | 1939 - .../Collision/Shape/HeightFieldShape.h | 268 - .../Physics/Collision/Shape/MeshShape.cpp | 1194 - .../Jolt/Physics/Collision/Shape/MeshShape.h | 187 - .../Collision/Shape/MutableCompoundShape.cpp | 559 - .../Collision/Shape/MutableCompoundShape.h | 158 - .../Shape/OffsetCenterOfMassShape.cpp | 206 - .../Collision/Shape/OffsetCenterOfMassShape.h | 129 - .../PolyhedronSubmergedVolumeCalculator.h | 290 - .../Shape/RotatedTranslatedShape.cpp | 256 - .../Collision/Shape/RotatedTranslatedShape.h | 147 - .../Physics/Collision/Shape/ScaleHelpers.h | 67 - .../Physics/Collision/Shape/ScaledShape.cpp | 215 - .../Physics/Collision/Shape/ScaledShape.h | 129 - .../Jolt/Physics/Collision/Shape/Shape.cpp | 329 - .../libs/Jolt/Physics/Collision/Shape/Shape.h | 373 - .../Physics/Collision/Shape/SphereShape.cpp | 322 - .../Physics/Collision/Shape/SphereShape.h | 120 - .../Collision/Shape/StaticCompoundShape.cpp | 659 - .../Collision/Shape/StaticCompoundShape.h | 138 - .../Jolt/Physics/Collision/Shape/SubShapeID.h | 125 - .../Physics/Collision/Shape/SubShapeIDPair.h | 78 - .../Collision/Shape/TaperedCapsuleShape.cpp | 382 - .../Collision/Shape/TaperedCapsuleShape.h | 120 - .../Physics/Collision/Shape/TriangleShape.cpp | 381 - .../Physics/Collision/Shape/TriangleShape.h | 133 - .../libs/Jolt/Physics/Collision/ShapeCast.h | 134 - .../libs/Jolt/Physics/Collision/ShapeFilter.h | 38 - .../Physics/Collision/SortReverseAndStore.h | 46 - .../Physics/Collision/TransformedShape.cpp | 135 - .../Jolt/Physics/Collision/TransformedShape.h | 165 - .../Physics/Constraints/ConeConstraint.cpp | 229 - .../Jolt/Physics/Constraints/ConeConstraint.h | 130 - .../Jolt/Physics/Constraints/Constraint.cpp | 90 - .../Jolt/Physics/Constraints/Constraint.h | 171 - .../Physics/Constraints/ConstraintManager.cpp | 230 - .../Physics/Constraints/ConstraintManager.h | 91 - .../ConstraintPart/AngleConstraintPart.h | 195 - .../ConstraintPart/AxisConstraintPart.h | 406 - .../ConstraintPart/DualAxisConstraintPart.h | 276 - .../ConstraintPart/GearConstraintPart.h | 190 - .../HingeRotationConstraintPart.h | 222 - .../ConstraintPart/PointConstraintPart.h | 224 - .../RackAndPinionConstraintPart.h | 191 - .../RotationEulerConstraintPart.h | 268 - .../RotationQuatConstraintPart.h | 229 - .../Constraints/ConstraintPart/SpringPart.h | 129 - .../ConstraintPart/SwingTwistConstraintPart.h | 465 - .../Constraints/ContactConstraintManager.cpp | 1606 - .../Constraints/ContactConstraintManager.h | 489 - .../Constraints/DistanceConstraint.cpp | 240 - .../Physics/Constraints/DistanceConstraint.h | 125 - .../Physics/Constraints/FixedConstraint.cpp | 185 - .../Physics/Constraints/FixedConstraint.h | 93 - .../Physics/Constraints/GearConstraint.cpp | 182 - .../Jolt/Physics/Constraints/GearConstraint.h | 113 - .../Physics/Constraints/HingeConstraint.cpp | 370 - .../Physics/Constraints/HingeConstraint.h | 167 - .../Physics/Constraints/MotorSettings.cpp | 43 - .../Jolt/Physics/Constraints/MotorSettings.h | 64 - .../Physics/Constraints/PathConstraint.cpp | 435 - .../Jolt/Physics/Constraints/PathConstraint.h | 176 - .../Constraints/PathConstraintPath.cpp | 112 - .../Physics/Constraints/PathConstraintPath.h | 69 - .../Constraints/PathConstraintPathHermite.cpp | 307 - .../Constraints/PathConstraintPathHermite.h | 53 - .../Physics/Constraints/PointConstraint.cpp | 124 - .../Physics/Constraints/PointConstraint.h | 79 - .../Constraints/RackAndPinionConstraint.cpp | 183 - .../Constraints/RackAndPinionConstraint.h | 115 - .../Physics/Constraints/SixDOFConstraint.cpp | 776 - .../Physics/Constraints/SixDOFConstraint.h | 244 - .../Physics/Constraints/SliderConstraint.cpp | 454 - .../Physics/Constraints/SliderConstraint.h | 201 - .../Constraints/SwingTwistConstraint.cpp | 483 - .../Constraints/SwingTwistConstraint.h | 191 - .../Physics/Constraints/TwoBodyConstraint.cpp | 49 - .../Physics/Constraints/TwoBodyConstraint.h | 61 - .../libs/Jolt/Physics/DeterminismLog.cpp | 16 - libs/zjolt/libs/Jolt/Physics/DeterminismLog.h | 139 - libs/zjolt/libs/Jolt/Physics/EActivation.h | 15 - .../zjolt/libs/Jolt/Physics/IslandBuilder.cpp | 477 - libs/zjolt/libs/Jolt/Physics/IslandBuilder.h | 121 - libs/zjolt/libs/Jolt/Physics/PhysicsLock.cpp | 16 - libs/zjolt/libs/Jolt/Physics/PhysicsLock.h | 102 - libs/zjolt/libs/Jolt/Physics/PhysicsScene.cpp | 216 - libs/zjolt/libs/Jolt/Physics/PhysicsScene.h | 89 - .../zjolt/libs/Jolt/Physics/PhysicsSettings.h | 112 - .../libs/Jolt/Physics/PhysicsStepListener.h | 24 - .../zjolt/libs/Jolt/Physics/PhysicsSystem.cpp | 2188 - libs/zjolt/libs/Jolt/Physics/PhysicsSystem.h | 270 - .../Jolt/Physics/PhysicsUpdateContext.cpp | 22 - .../libs/Jolt/Physics/PhysicsUpdateContext.h | 171 - .../libs/Jolt/Physics/Ragdoll/Ragdoll.cpp | 612 - .../zjolt/libs/Jolt/Physics/Ragdoll/Ragdoll.h | 209 - libs/zjolt/libs/Jolt/Physics/StateRecorder.h | 31 - .../libs/Jolt/Physics/StateRecorderImpl.cpp | 83 - .../libs/Jolt/Physics/StateRecorderImpl.h | 43 - .../Vehicle/TrackedVehicleController.cpp | 490 - .../Vehicle/TrackedVehicleController.h | 148 - .../Physics/Vehicle/VehicleAntiRollBar.cpp | 32 - .../Jolt/Physics/Vehicle/VehicleAntiRollBar.h | 30 - .../Vehicle/VehicleCollisionTester.cpp | 177 - .../Physics/Vehicle/VehicleCollisionTester.h | 81 - .../Physics/Vehicle/VehicleConstraint.cpp | 490 - .../Jolt/Physics/Vehicle/VehicleConstraint.h | 138 - .../Physics/Vehicle/VehicleController.cpp | 16 - .../Jolt/Physics/Vehicle/VehicleController.h | 69 - .../Physics/Vehicle/VehicleDifferential.cpp | 38 - .../Physics/Vehicle/VehicleDifferential.h | 30 - .../Jolt/Physics/Vehicle/VehicleEngine.cpp | 119 - .../libs/Jolt/Physics/Vehicle/VehicleEngine.h | 76 - .../Jolt/Physics/Vehicle/VehicleTrack.cpp | 51 - .../libs/Jolt/Physics/Vehicle/VehicleTrack.h | 55 - .../Physics/Vehicle/VehicleTransmission.cpp | 143 - .../Physics/Vehicle/VehicleTransmission.h | 80 - .../zjolt/libs/Jolt/Physics/Vehicle/Wheel.cpp | 75 - libs/zjolt/libs/Jolt/Physics/Vehicle/Wheel.h | 143 - .../Vehicle/WheeledVehicleController.cpp | 476 - .../Vehicle/WheeledVehicleController.h | 156 - libs/zjolt/libs/Jolt/RegisterTypes.cpp | 153 - libs/zjolt/libs/Jolt/RegisterTypes.h | 11 - .../libs/Jolt/Renderer/DebugRenderer.cpp | 991 - libs/zjolt/libs/Jolt/Renderer/DebugRenderer.h | 266 - .../Jolt/Renderer/DebugRendererPlayback.cpp | 166 - .../Jolt/Renderer/DebugRendererPlayback.h | 47 - .../Jolt/Renderer/DebugRendererRecorder.cpp | 156 - .../Jolt/Renderer/DebugRendererRecorder.h | 128 - .../libs/Jolt/Skeleton/SkeletalAnimation.cpp | 109 - .../libs/Jolt/Skeleton/SkeletalAnimation.h | 76 - libs/zjolt/libs/Jolt/Skeleton/Skeleton.cpp | 72 - libs/zjolt/libs/Jolt/Skeleton/Skeleton.h | 67 - .../libs/Jolt/Skeleton/SkeletonMapper.cpp | 155 - .../zjolt/libs/Jolt/Skeleton/SkeletonMapper.h | 104 - .../zjolt/libs/Jolt/Skeleton/SkeletonPose.cpp | 84 - libs/zjolt/libs/Jolt/Skeleton/SkeletonPose.h | 76 - .../Jolt/TriangleGrouper/TriangleGrouper.h | 25 - .../TriangleGrouperClosestCentroid.cpp | 94 - .../TriangleGrouperClosestCentroid.h | 20 - .../TriangleGrouper/TriangleGrouperMorton.cpp | 48 - .../TriangleGrouper/TriangleGrouperMorton.h | 19 - .../TriangleSplitter/TriangleSplitter.cpp | 66 - .../Jolt/TriangleSplitter/TriangleSplitter.h | 82 - .../TriangleSplitterBinning.cpp | 111 - .../TriangleSplitterBinning.h | 51 - .../TriangleSplitterFixedLeafSize.cpp | 169 - .../TriangleSplitterFixedLeafSize.h | 54 - .../TriangleSplitterLongestAxis.cpp | 30 - .../TriangleSplitterLongestAxis.h | 27 - .../TriangleSplitter/TriangleSplitterMean.cpp | 39 - .../TriangleSplitter/TriangleSplitterMean.h | 27 - .../TriangleSplitterMorton.cpp | 62 - .../TriangleSplitter/TriangleSplitterMorton.h | 31 - libs/zjolt/libs/JoltC/JoltC.cpp | 1933 - libs/zjolt/libs/JoltC/JoltC.h | 1150 - libs/zjolt/src/tests.c | 524 - libs/zjolt/src/zjolt.zig | 114 - libs/zmesh/src/Shape.zig | 2 +- libs/zmesh/src/memory.zig | 8 +- libs/zmesh/src/zcgltf.zig | 8 +- libs/znetwork/README.md | 29 - libs/znetwork/build.zig | 23 - libs/znetwork/src/main.zig | 9 - libs/znetwork/src/network.zig | 1514 - libs/zpool/README.md | 99 - libs/zpool/build.zig | 32 - libs/zpool/src/embedded_ring_queue.zig | 174 - libs/zpool/src/handle.zig | 278 - libs/zpool/src/main.zig | 10 - libs/zpool/src/pool.zig | 1403 - libs/zpool/src/utils.zig | 106 - samples/audio_experiments_wgpu/README.md | 11 - .../Broke For Free - Night Owl.mp3 | 3 - .../Broke For Free - Night Owl.txt | 2 - .../Roboto-Medium.ttf | 3 - .../drum_bass_hard.flac | 3 - .../loop_mika.flac | 3 - .../tabla_tas1.flac | 3 - samples/audio_experiments_wgpu/build.zig | 50 - samples/audio_experiments_wgpu/screenshot.png | 3 - .../src/audio_experiments_wgpu.zig | 1016 - .../src/audio_experiments_wgsl.zig | 34 - samples/bullet_physics_test_wgpu/README.md | 11 - samples/bullet_physics_test_wgpu/build.zig | 63 - .../Roboto-Medium.ttf | 3 - .../world.gltf | 3 - .../world_data.bin | 3 - .../bullet_physics_test_wgpu/screenshot.jpg | 3 - .../src/bullet_physics_test_wgpu.zig | 1372 - .../src/bullet_physics_test_wgsl.zig | 178 - samples/gui_test_wgpu/README.md | 7 - samples/gui_test_wgpu/build.zig | 50 - .../gui_test_wgpu_content/FiraCode-Medium.ttf | 3 - .../gui_test_wgpu_content/Roboto-Medium.ttf | 3 - .../gui_test_wgpu_content/genart_0025_5.png | 3 - samples/gui_test_wgpu/screenshot.png | 3 - samples/gui_test_wgpu/src/gui_test_wgpu.zig | 540 - .../physically_based_rendering_wgpu/README.md | 32 - .../physically_based_rendering_wgpu/build.zig | 57 - .../Newport_Loft.hdr | 3 - .../Roboto-Medium.ttf | 3 - .../SciFiHelmet/SciFiHelmet.bin | 3 - .../SciFiHelmet/SciFiHelmet.gltf | 3 - .../SciFiHelmet_AmbientOcclusion.png | 3 - .../SciFiHelmet/SciFiHelmet_BaseColor.png | 3 - .../SciFiHelmet_MetallicRoughness.png | 3 - .../SciFiHelmet/SciFiHelmet_Normal.png | 3 - .../cube.bin | 3 - .../cube.gltf | 3 - .../drackenstein_quarry_4k.hdr | 3 - .../freight_station_4k.hdr | 3 - .../screenshot0.jpg | 3 - .../screenshot1.jpg | 3 - .../screenshot2.jpg | 3 - .../src/physically_based_rendering_wgpu.zig | 1126 - .../src/physically_based_rendering_wgsl.zig | 421 - samples/procedural_mesh_wgpu/README.md | 53 - samples/procedural_mesh_wgpu/build.zig | 54 - .../Roboto-Medium.ttf | 3 - samples/procedural_mesh_wgpu/screenshot.png | 3 - .../src/procedural_mesh_wgpu.zig | 699 - .../src/procedural_mesh_wgsl.zig | 144 - samples/textured_quad_wgpu/README.md | 14 - samples/textured_quad_wgpu/build.zig | 48 - samples/textured_quad_wgpu/screenshot.png | 3 - .../src/textured_quad_wgpu.zig | 374 - .../Roboto-Medium.ttf | 3 - .../genart_0025_5.png | 3 - samples/triangle_wgpu/README.md | 13 - samples/triangle_wgpu/build.zig | 45 - samples/triangle_wgpu/screenshot.png | 3 - samples/triangle_wgpu/src/triangle_wgpu.zig | 356 - .../triangle_wgpu_content/Roboto-Medium.ttf | 3 - 574 files changed, 62 insertions(+), 299891 deletions(-) delete mode 100644 libs/zaudio/README.md delete mode 100644 libs/zaudio/build.zig delete mode 100644 libs/zaudio/libs/miniaudio/miniaudio.c delete mode 100644 libs/zaudio/libs/miniaudio/miniaudio.h delete mode 100644 libs/zaudio/libs/miniaudio/zaudio.c delete mode 100644 libs/zaudio/src/zaudio.zig delete mode 100644 libs/zaudio/system_sdk.zig delete mode 100644 libs/zglfw/README.md delete mode 100644 libs/zglfw/build.zig delete mode 100644 libs/zglfw/libs/glfw/LICENSE.md delete mode 100644 libs/zglfw/libs/glfw/include/GLFW/glfw3.h delete mode 100644 libs/zglfw/libs/glfw/include/GLFW/glfw3native.h delete mode 100644 libs/zglfw/libs/glfw/src/cocoa_init.m delete mode 100644 libs/zglfw/libs/glfw/src/cocoa_joystick.h delete mode 100644 libs/zglfw/libs/glfw/src/cocoa_joystick.m delete mode 100644 libs/zglfw/libs/glfw/src/cocoa_monitor.m delete mode 100644 libs/zglfw/libs/glfw/src/cocoa_platform.h delete mode 100644 libs/zglfw/libs/glfw/src/cocoa_time.c delete mode 100644 libs/zglfw/libs/glfw/src/cocoa_window.m delete mode 100644 libs/zglfw/libs/glfw/src/context.c delete mode 100644 libs/zglfw/libs/glfw/src/egl_context.c delete mode 100644 libs/zglfw/libs/glfw/src/egl_context.h delete mode 100644 libs/zglfw/libs/glfw/src/glx_context.c delete mode 100644 libs/zglfw/libs/glfw/src/glx_context.h delete mode 100644 libs/zglfw/libs/glfw/src/init.c delete mode 100644 libs/zglfw/libs/glfw/src/input.c delete mode 100644 libs/zglfw/libs/glfw/src/internal.h delete mode 100644 libs/zglfw/libs/glfw/src/linux_joystick.c delete mode 100644 libs/zglfw/libs/glfw/src/linux_joystick.h delete mode 100644 libs/zglfw/libs/glfw/src/mappings.h delete mode 100644 libs/zglfw/libs/glfw/src/monitor.c delete mode 100644 libs/zglfw/libs/glfw/src/nsgl_context.h delete mode 100644 libs/zglfw/libs/glfw/src/nsgl_context.m delete mode 100644 libs/zglfw/libs/glfw/src/null_init.c delete mode 100644 libs/zglfw/libs/glfw/src/null_joystick.c delete mode 100644 libs/zglfw/libs/glfw/src/null_joystick.h delete mode 100644 libs/zglfw/libs/glfw/src/null_monitor.c delete mode 100644 libs/zglfw/libs/glfw/src/null_platform.h delete mode 100644 libs/zglfw/libs/glfw/src/null_window.c delete mode 100644 libs/zglfw/libs/glfw/src/osmesa_context.c delete mode 100644 libs/zglfw/libs/glfw/src/osmesa_context.h delete mode 100644 libs/zglfw/libs/glfw/src/posix_thread.c delete mode 100644 libs/zglfw/libs/glfw/src/posix_thread.h delete mode 100644 libs/zglfw/libs/glfw/src/posix_time.c delete mode 100644 libs/zglfw/libs/glfw/src/posix_time.h delete mode 100644 libs/zglfw/libs/glfw/src/vulkan.c delete mode 100644 libs/zglfw/libs/glfw/src/wgl_context.c delete mode 100644 libs/zglfw/libs/glfw/src/wgl_context.h delete mode 100644 libs/zglfw/libs/glfw/src/win32_init.c delete mode 100644 libs/zglfw/libs/glfw/src/win32_joystick.c delete mode 100644 libs/zglfw/libs/glfw/src/win32_joystick.h delete mode 100644 libs/zglfw/libs/glfw/src/win32_monitor.c delete mode 100644 libs/zglfw/libs/glfw/src/win32_platform.h delete mode 100644 libs/zglfw/libs/glfw/src/win32_thread.c delete mode 100644 libs/zglfw/libs/glfw/src/win32_time.c delete mode 100644 libs/zglfw/libs/glfw/src/win32_window.c delete mode 100644 libs/zglfw/libs/glfw/src/window.c delete mode 100644 libs/zglfw/libs/glfw/src/wl_init.c delete mode 100644 libs/zglfw/libs/glfw/src/wl_monitor.c delete mode 100644 libs/zglfw/libs/glfw/src/wl_platform.h delete mode 100644 libs/zglfw/libs/glfw/src/wl_window.c delete mode 100644 libs/zglfw/libs/glfw/src/x11_init.c delete mode 100644 libs/zglfw/libs/glfw/src/x11_monitor.c delete mode 100644 libs/zglfw/libs/glfw/src/x11_platform.h delete mode 100644 libs/zglfw/libs/glfw/src/x11_window.c delete mode 100644 libs/zglfw/libs/glfw/src/xkb_unicode.c delete mode 100644 libs/zglfw/libs/glfw/src/xkb_unicode.h delete mode 100644 libs/zglfw/src/zglfw.zig delete mode 100644 libs/zglfw/system_sdk.zig delete mode 100644 libs/zgpu/README.md delete mode 100644 libs/zgpu/build.zig delete mode 160000 libs/zgpu/libs/dawn delete mode 100644 libs/zgpu/src/common_wgsl.zig delete mode 100644 libs/zgpu/src/dawn.cpp delete mode 100644 libs/zgpu/src/wgpu.zig delete mode 100644 libs/zgpu/src/zgpu.zig delete mode 100644 libs/zgui/README.md delete mode 100644 libs/zgui/build.zig delete mode 100644 libs/zgui/libs/imgui/LICENSE.txt delete mode 100644 libs/zgui/libs/imgui/imconfig.h delete mode 100644 libs/zgui/libs/imgui/imgui.cpp delete mode 100644 libs/zgui/libs/imgui/imgui.h delete mode 100644 libs/zgui/libs/imgui/imgui_demo.cpp delete mode 100644 libs/zgui/libs/imgui/imgui_draw.cpp delete mode 100644 libs/zgui/libs/imgui/imgui_impl_glfw.cpp delete mode 100644 libs/zgui/libs/imgui/imgui_impl_wgpu.cpp delete mode 100644 libs/zgui/libs/imgui/imgui_internal.h delete mode 100644 libs/zgui/libs/imgui/imgui_tables.cpp delete mode 100644 libs/zgui/libs/imgui/imgui_widgets.cpp delete mode 100644 libs/zgui/libs/imgui/implot.cpp delete mode 100644 libs/zgui/libs/imgui/implot.h delete mode 100644 libs/zgui/libs/imgui/implot_demo.cpp delete mode 100644 libs/zgui/libs/imgui/implot_internal.h delete mode 100644 libs/zgui/libs/imgui/implot_items.cpp delete mode 100644 libs/zgui/libs/imgui/imstb_rectpack.h delete mode 100644 libs/zgui/libs/imgui/imstb_textedit.h delete mode 100644 libs/zgui/libs/imgui/imstb_truetype.h delete mode 100644 libs/zgui/src/main.zig delete mode 100644 libs/zgui/src/zgui.cpp delete mode 100644 libs/zgui/src/zgui.zig delete mode 100644 libs/zgui/src/zgui_glfw_wgpu.zig delete mode 100644 libs/zjolt/build.zig delete mode 100644 libs/zjolt/libs/Jolt/AABBTree/AABBTreeBuilder.cpp delete mode 100644 libs/zjolt/libs/Jolt/AABBTree/AABBTreeBuilder.h delete mode 100644 libs/zjolt/libs/Jolt/AABBTree/AABBTreeToBuffer.h delete mode 100644 libs/zjolt/libs/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h delete mode 100644 libs/zjolt/libs/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h delete mode 100644 libs/zjolt/libs/Jolt/Core/Atomics.h delete mode 100644 libs/zjolt/libs/Jolt/Core/ByteBuffer.h delete mode 100644 libs/zjolt/libs/Jolt/Core/Color.cpp delete mode 100644 libs/zjolt/libs/Jolt/Core/Color.h delete mode 100644 libs/zjolt/libs/Jolt/Core/Core.h delete mode 100644 libs/zjolt/libs/Jolt/Core/FPControlWord.h delete mode 100644 libs/zjolt/libs/Jolt/Core/FPException.h delete mode 100644 libs/zjolt/libs/Jolt/Core/FPFlushDenormals.h delete mode 100644 libs/zjolt/libs/Jolt/Core/Factory.cpp delete mode 100644 libs/zjolt/libs/Jolt/Core/Factory.h delete mode 100644 libs/zjolt/libs/Jolt/Core/FixedSizeFreeList.h delete mode 100644 libs/zjolt/libs/Jolt/Core/FixedSizeFreeList.inl delete mode 100644 libs/zjolt/libs/Jolt/Core/HashCombine.h delete mode 100644 libs/zjolt/libs/Jolt/Core/InsertionSort.h delete mode 100644 libs/zjolt/libs/Jolt/Core/IssueReporting.cpp delete mode 100644 libs/zjolt/libs/Jolt/Core/IssueReporting.h delete mode 100644 libs/zjolt/libs/Jolt/Core/JobSystem.h delete mode 100644 libs/zjolt/libs/Jolt/Core/JobSystem.inl delete mode 100644 libs/zjolt/libs/Jolt/Core/JobSystemThreadPool.cpp delete mode 100644 libs/zjolt/libs/Jolt/Core/JobSystemThreadPool.h delete mode 100644 libs/zjolt/libs/Jolt/Core/LinearCurve.cpp delete mode 100644 libs/zjolt/libs/Jolt/Core/LinearCurve.h delete mode 100644 libs/zjolt/libs/Jolt/Core/LockFreeHashMap.h delete mode 100644 libs/zjolt/libs/Jolt/Core/LockFreeHashMap.inl delete mode 100644 libs/zjolt/libs/Jolt/Core/Memory.cpp delete mode 100644 libs/zjolt/libs/Jolt/Core/Memory.h delete mode 100644 libs/zjolt/libs/Jolt/Core/Mutex.h delete mode 100644 libs/zjolt/libs/Jolt/Core/MutexArray.h delete mode 100644 libs/zjolt/libs/Jolt/Core/NonCopyable.h delete mode 100644 libs/zjolt/libs/Jolt/Core/Profiler.cpp delete mode 100644 libs/zjolt/libs/Jolt/Core/Profiler.h delete mode 100644 libs/zjolt/libs/Jolt/Core/Profiler.inl delete mode 100644 libs/zjolt/libs/Jolt/Core/QuickSort.h delete mode 100644 libs/zjolt/libs/Jolt/Core/RTTI.cpp delete mode 100644 libs/zjolt/libs/Jolt/Core/RTTI.h delete mode 100644 libs/zjolt/libs/Jolt/Core/Reference.h delete mode 100644 libs/zjolt/libs/Jolt/Core/Result.h delete mode 100644 libs/zjolt/libs/Jolt/Core/STLAlignedAllocator.h delete mode 100644 libs/zjolt/libs/Jolt/Core/STLAllocator.h delete mode 100644 libs/zjolt/libs/Jolt/Core/STLTempAllocator.h delete mode 100644 libs/zjolt/libs/Jolt/Core/StaticArray.h delete mode 100644 libs/zjolt/libs/Jolt/Core/StreamIn.h delete mode 100644 libs/zjolt/libs/Jolt/Core/StreamOut.h delete mode 100644 libs/zjolt/libs/Jolt/Core/StreamWrapper.h delete mode 100644 libs/zjolt/libs/Jolt/Core/StringTools.cpp delete mode 100644 libs/zjolt/libs/Jolt/Core/StringTools.h delete mode 100644 libs/zjolt/libs/Jolt/Core/TempAllocator.h delete mode 100644 libs/zjolt/libs/Jolt/Core/TickCounter.cpp delete mode 100644 libs/zjolt/libs/Jolt/Core/TickCounter.h delete mode 100644 libs/zjolt/libs/Jolt/Core/UnorderedMap.h delete mode 100644 libs/zjolt/libs/Jolt/Core/UnorderedSet.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/AABox.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/AABox4.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/ClipPoly.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/ClosestPoint.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/ConvexHullBuilder.cpp delete mode 100644 libs/zjolt/libs/Jolt/Geometry/ConvexHullBuilder.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/ConvexHullBuilder2D.cpp delete mode 100644 libs/zjolt/libs/Jolt/Geometry/ConvexHullBuilder2D.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/ConvexSupport.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/EPAConvexHullBuilder.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/EPAPenetrationDepth.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/Ellipse.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/GJKClosestPoint.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/IndexedTriangle.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/Indexify.cpp delete mode 100644 libs/zjolt/libs/Jolt/Geometry/Indexify.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/MortonCode.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/OrientedBox.cpp delete mode 100644 libs/zjolt/libs/Jolt/Geometry/OrientedBox.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/Plane.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/RayAABox.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/RayAABox8.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/RayCapsule.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/RayCylinder.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/RaySphere.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/RayTriangle.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/RayTriangle8.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/Sphere.h delete mode 100644 libs/zjolt/libs/Jolt/Geometry/Triangle.h delete mode 100644 libs/zjolt/libs/Jolt/Jolt.h delete mode 100644 libs/zjolt/libs/Jolt/Math/DVec3.h delete mode 100644 libs/zjolt/libs/Jolt/Math/DVec3.inl delete mode 100644 libs/zjolt/libs/Jolt/Math/EigenValueSymmetric.h delete mode 100644 libs/zjolt/libs/Jolt/Math/FindRoot.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Float2.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Float3.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Float4.h delete mode 100644 libs/zjolt/libs/Jolt/Math/GaussianElimination.h delete mode 100644 libs/zjolt/libs/Jolt/Math/HalfFloat.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Mat44.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Mat44.inl delete mode 100644 libs/zjolt/libs/Jolt/Math/Math.h delete mode 100644 libs/zjolt/libs/Jolt/Math/MathTypes.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Matrix.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Quat.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Quat.inl delete mode 100644 libs/zjolt/libs/Jolt/Math/Swizzle.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Trigonometry.h delete mode 100644 libs/zjolt/libs/Jolt/Math/UVec4.cpp delete mode 100644 libs/zjolt/libs/Jolt/Math/UVec4.h delete mode 100644 libs/zjolt/libs/Jolt/Math/UVec4.inl delete mode 100644 libs/zjolt/libs/Jolt/Math/UVec8.h delete mode 100644 libs/zjolt/libs/Jolt/Math/UVec8.inl delete mode 100644 libs/zjolt/libs/Jolt/Math/Vec3.cpp delete mode 100644 libs/zjolt/libs/Jolt/Math/Vec3.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Vec3.inl delete mode 100644 libs/zjolt/libs/Jolt/Math/Vec4.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Vec4.inl delete mode 100644 libs/zjolt/libs/Jolt/Math/Vec8.h delete mode 100644 libs/zjolt/libs/Jolt/Math/Vec8.inl delete mode 100644 libs/zjolt/libs/Jolt/Math/Vector.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/GetPrimitiveTypeOfType.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStream.cpp delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStream.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamBinaryIn.cpp delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamBinaryIn.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamBinaryOut.cpp delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamBinaryOut.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamIn.cpp delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamIn.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamOut.cpp delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamOut.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamTextIn.cpp delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamTextIn.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamTextOut.cpp delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamTextOut.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/ObjectStreamTypes.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/SerializableAttribute.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/SerializableAttributeEnum.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/SerializableAttributeTyped.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/SerializableObject.cpp delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/SerializableObject.h delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/TypeDeclarations.cpp delete mode 100644 libs/zjolt/libs/Jolt/ObjectStream/TypeDeclarations.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/Body.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/Body.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/Body.inl delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyAccess.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyAccess.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyActivationListener.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyCreationSettings.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyCreationSettings.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyFilter.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyID.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyInterface.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyInterface.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyLock.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyLockInterface.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyLockMulti.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyManager.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyManager.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/BodyPair.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/MassProperties.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/MassProperties.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/MotionProperties.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/MotionProperties.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/MotionProperties.inl delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/MotionQuality.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Body/MotionType.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Character/Character.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Character/Character.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Character/CharacterBase.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Character/CharacterBase.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Character/CharacterVirtual.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Character/CharacterVirtual.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/AABoxCast.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/ActiveEdgeMode.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/ActiveEdges.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BackFaceMode.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BroadPhase/BroadPhase.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BroadPhase/BroadPhase.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BroadPhase/QuadTree.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/BroadPhase/QuadTree.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CastConvexVsTriangles.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CastConvexVsTriangles.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CastResult.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CastSphereVsTriangles.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CastSphereVsTriangles.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollectFacesMode.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollideConvexVsTriangles.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollideConvexVsTriangles.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollidePointResult.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollideShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollideSphereVsTriangles.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollideSphereVsTriangles.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollisionCollector.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollisionCollectorImpl.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollisionDispatch.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollisionDispatch.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollisionGroup.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/CollisionGroup.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/ContactListener.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/GroupFilter.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/GroupFilter.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/GroupFilterTable.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/GroupFilterTable.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/NarrowPhaseQuery.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/NarrowPhaseQuery.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/NarrowPhaseStats.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/NarrowPhaseStats.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/ObjectLayer.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/PhysicsMaterial.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/PhysicsMaterial.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/PhysicsMaterialSimple.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/PhysicsMaterialSimple.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/RayCast.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/BoxShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/BoxShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/CapsuleShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/CapsuleShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/CompoundShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/CompoundShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/ConvexHullShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/ConvexHullShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/ConvexShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/ConvexShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/CylinderShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/CylinderShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/DecoratedShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/DecoratedShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/GetTrianglesContext.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/HeightFieldShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/HeightFieldShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/MeshShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/MeshShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/MutableCompoundShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/MutableCompoundShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/ScaleHelpers.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/ScaledShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/ScaledShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/Shape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/Shape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/SphereShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/SphereShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/StaticCompoundShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/SubShapeID.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/SubShapeIDPair.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/TriangleShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/Shape/TriangleShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/ShapeCast.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/ShapeFilter.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/SortReverseAndStore.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/TransformedShape.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Collision/TransformedShape.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConeConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConeConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/Constraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/Constraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintManager.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintManager.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/GearConstraintPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/PointConstraintPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/SpringPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ContactConstraintManager.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/ContactConstraintManager.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/DistanceConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/DistanceConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/FixedConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/FixedConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/GearConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/GearConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/HingeConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/HingeConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/MotorSettings.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/MotorSettings.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/PathConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/PathConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/PathConstraintPath.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/PathConstraintPath.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/PathConstraintPathHermite.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/PathConstraintPathHermite.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/PointConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/PointConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/RackAndPinionConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/RackAndPinionConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/SixDOFConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/SixDOFConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/SliderConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/SliderConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/SwingTwistConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/SwingTwistConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/TwoBodyConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Constraints/TwoBodyConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/DeterminismLog.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/DeterminismLog.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/EActivation.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/IslandBuilder.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/IslandBuilder.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/PhysicsLock.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/PhysicsLock.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/PhysicsScene.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/PhysicsScene.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/PhysicsSettings.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/PhysicsStepListener.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/PhysicsSystem.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/PhysicsSystem.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/PhysicsUpdateContext.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/PhysicsUpdateContext.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Ragdoll/Ragdoll.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Ragdoll/Ragdoll.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/StateRecorder.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/StateRecorderImpl.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/StateRecorderImpl.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/TrackedVehicleController.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/TrackedVehicleController.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleAntiRollBar.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleAntiRollBar.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleCollisionTester.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleCollisionTester.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleConstraint.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleConstraint.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleController.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleController.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleDifferential.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleDifferential.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleEngine.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleEngine.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleTrack.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleTrack.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleTransmission.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/VehicleTransmission.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/Wheel.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/Wheel.h delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/WheeledVehicleController.cpp delete mode 100644 libs/zjolt/libs/Jolt/Physics/Vehicle/WheeledVehicleController.h delete mode 100644 libs/zjolt/libs/Jolt/RegisterTypes.cpp delete mode 100644 libs/zjolt/libs/Jolt/RegisterTypes.h delete mode 100644 libs/zjolt/libs/Jolt/Renderer/DebugRenderer.cpp delete mode 100644 libs/zjolt/libs/Jolt/Renderer/DebugRenderer.h delete mode 100644 libs/zjolt/libs/Jolt/Renderer/DebugRendererPlayback.cpp delete mode 100644 libs/zjolt/libs/Jolt/Renderer/DebugRendererPlayback.h delete mode 100644 libs/zjolt/libs/Jolt/Renderer/DebugRendererRecorder.cpp delete mode 100644 libs/zjolt/libs/Jolt/Renderer/DebugRendererRecorder.h delete mode 100644 libs/zjolt/libs/Jolt/Skeleton/SkeletalAnimation.cpp delete mode 100644 libs/zjolt/libs/Jolt/Skeleton/SkeletalAnimation.h delete mode 100644 libs/zjolt/libs/Jolt/Skeleton/Skeleton.cpp delete mode 100644 libs/zjolt/libs/Jolt/Skeleton/Skeleton.h delete mode 100644 libs/zjolt/libs/Jolt/Skeleton/SkeletonMapper.cpp delete mode 100644 libs/zjolt/libs/Jolt/Skeleton/SkeletonMapper.h delete mode 100644 libs/zjolt/libs/Jolt/Skeleton/SkeletonPose.cpp delete mode 100644 libs/zjolt/libs/Jolt/Skeleton/SkeletonPose.h delete mode 100644 libs/zjolt/libs/Jolt/TriangleGrouper/TriangleGrouper.h delete mode 100644 libs/zjolt/libs/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.cpp delete mode 100644 libs/zjolt/libs/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.h delete mode 100644 libs/zjolt/libs/Jolt/TriangleGrouper/TriangleGrouperMorton.cpp delete mode 100644 libs/zjolt/libs/Jolt/TriangleGrouper/TriangleGrouperMorton.h delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitter.cpp delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitter.h delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitterBinning.cpp delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitterBinning.h delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.h delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.cpp delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.h delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitterMean.cpp delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitterMean.h delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitterMorton.cpp delete mode 100644 libs/zjolt/libs/Jolt/TriangleSplitter/TriangleSplitterMorton.h delete mode 100644 libs/zjolt/libs/JoltC/JoltC.cpp delete mode 100644 libs/zjolt/libs/JoltC/JoltC.h delete mode 100644 libs/zjolt/src/tests.c delete mode 100644 libs/zjolt/src/zjolt.zig delete mode 100644 libs/znetwork/README.md delete mode 100644 libs/znetwork/build.zig delete mode 100644 libs/znetwork/src/main.zig delete mode 100644 libs/znetwork/src/network.zig delete mode 100644 libs/zpool/README.md delete mode 100644 libs/zpool/build.zig delete mode 100644 libs/zpool/src/embedded_ring_queue.zig delete mode 100644 libs/zpool/src/handle.zig delete mode 100644 libs/zpool/src/main.zig delete mode 100644 libs/zpool/src/pool.zig delete mode 100644 libs/zpool/src/utils.zig delete mode 100644 samples/audio_experiments_wgpu/README.md delete mode 100644 samples/audio_experiments_wgpu/audio_experiments_wgpu_content/Broke For Free - Night Owl.mp3 delete mode 100644 samples/audio_experiments_wgpu/audio_experiments_wgpu_content/Broke For Free - Night Owl.txt delete mode 100644 samples/audio_experiments_wgpu/audio_experiments_wgpu_content/Roboto-Medium.ttf delete mode 100644 samples/audio_experiments_wgpu/audio_experiments_wgpu_content/drum_bass_hard.flac delete mode 100644 samples/audio_experiments_wgpu/audio_experiments_wgpu_content/loop_mika.flac delete mode 100644 samples/audio_experiments_wgpu/audio_experiments_wgpu_content/tabla_tas1.flac delete mode 100644 samples/audio_experiments_wgpu/build.zig delete mode 100644 samples/audio_experiments_wgpu/screenshot.png delete mode 100644 samples/audio_experiments_wgpu/src/audio_experiments_wgpu.zig delete mode 100644 samples/audio_experiments_wgpu/src/audio_experiments_wgsl.zig delete mode 100644 samples/bullet_physics_test_wgpu/README.md delete mode 100644 samples/bullet_physics_test_wgpu/build.zig delete mode 100644 samples/bullet_physics_test_wgpu/bullet_physics_test_wgpu_content/Roboto-Medium.ttf delete mode 100644 samples/bullet_physics_test_wgpu/bullet_physics_test_wgpu_content/world.gltf delete mode 100644 samples/bullet_physics_test_wgpu/bullet_physics_test_wgpu_content/world_data.bin delete mode 100644 samples/bullet_physics_test_wgpu/screenshot.jpg delete mode 100644 samples/bullet_physics_test_wgpu/src/bullet_physics_test_wgpu.zig delete mode 100644 samples/bullet_physics_test_wgpu/src/bullet_physics_test_wgsl.zig delete mode 100644 samples/gui_test_wgpu/README.md delete mode 100644 samples/gui_test_wgpu/build.zig delete mode 100644 samples/gui_test_wgpu/gui_test_wgpu_content/FiraCode-Medium.ttf delete mode 100644 samples/gui_test_wgpu/gui_test_wgpu_content/Roboto-Medium.ttf delete mode 100644 samples/gui_test_wgpu/gui_test_wgpu_content/genart_0025_5.png delete mode 100644 samples/gui_test_wgpu/screenshot.png delete mode 100644 samples/gui_test_wgpu/src/gui_test_wgpu.zig delete mode 100644 samples/physically_based_rendering_wgpu/README.md delete mode 100644 samples/physically_based_rendering_wgpu/build.zig delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/Newport_Loft.hdr delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/Roboto-Medium.ttf delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/SciFiHelmet/SciFiHelmet.bin delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/SciFiHelmet/SciFiHelmet.gltf delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/SciFiHelmet/SciFiHelmet_AmbientOcclusion.png delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/SciFiHelmet/SciFiHelmet_BaseColor.png delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/SciFiHelmet/SciFiHelmet_MetallicRoughness.png delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/SciFiHelmet/SciFiHelmet_Normal.png delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/cube.bin delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/cube.gltf delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/drackenstein_quarry_4k.hdr delete mode 100644 samples/physically_based_rendering_wgpu/physically_based_rendering_wgpu_content/freight_station_4k.hdr delete mode 100644 samples/physically_based_rendering_wgpu/screenshot0.jpg delete mode 100644 samples/physically_based_rendering_wgpu/screenshot1.jpg delete mode 100644 samples/physically_based_rendering_wgpu/screenshot2.jpg delete mode 100644 samples/physically_based_rendering_wgpu/src/physically_based_rendering_wgpu.zig delete mode 100644 samples/physically_based_rendering_wgpu/src/physically_based_rendering_wgsl.zig delete mode 100644 samples/procedural_mesh_wgpu/README.md delete mode 100644 samples/procedural_mesh_wgpu/build.zig delete mode 100644 samples/procedural_mesh_wgpu/procedural_mesh_wgpu_content/Roboto-Medium.ttf delete mode 100644 samples/procedural_mesh_wgpu/screenshot.png delete mode 100644 samples/procedural_mesh_wgpu/src/procedural_mesh_wgpu.zig delete mode 100644 samples/procedural_mesh_wgpu/src/procedural_mesh_wgsl.zig delete mode 100644 samples/textured_quad_wgpu/README.md delete mode 100644 samples/textured_quad_wgpu/build.zig delete mode 100644 samples/textured_quad_wgpu/screenshot.png delete mode 100644 samples/textured_quad_wgpu/src/textured_quad_wgpu.zig delete mode 100644 samples/textured_quad_wgpu/textured_quad_wgpu_content/Roboto-Medium.ttf delete mode 100644 samples/textured_quad_wgpu/textured_quad_wgpu_content/genart_0025_5.png delete mode 100644 samples/triangle_wgpu/README.md delete mode 100644 samples/triangle_wgpu/build.zig delete mode 100644 samples/triangle_wgpu/screenshot.png delete mode 100644 samples/triangle_wgpu/src/triangle_wgpu.zig delete mode 100644 samples/triangle_wgpu/triangle_wgpu_content/Roboto-Medium.ttf diff --git a/build.zig b/build.zig index 0bf0e9448..66df21583 100644 --- a/build.zig +++ b/build.zig @@ -2,13 +2,13 @@ const builtin = @import("builtin"); const std = @import("std"); pub fn build(b: *std.build.Builder) void { - ensureGitLfs(b.allocator); - { - var child = std.ChildProcess.init(&.{ "git", "submodule", "update", "--init", "--remote" }, b.allocator); - child.cwd = thisDir(); - child.stderr = std.io.getStdErr(); - child.stdout = std.io.getStdOut(); - _ = child.spawnAndWait() catch unreachable; + // TODO: Make 'windows' branch work with stage2/stage3 compiler. + if (@import("builtin").target.os.tag != .windows or @import("builtin").zig_backend != .stage1) { + std.debug.print( + "This branch compiles only on Windows. It requires stage1 compiler for now (zig build -fstage1)\n", + .{}, + ); + std.process.exit(1); } var options = Options{ @@ -18,121 +18,45 @@ pub fn build(b: *std.build.Builder) void { options.ztracy_enable = b.option(bool, "ztracy-enable", "Enable Tracy profiler") orelse false; - // - // Cross-platform demos - // - if (!builtin.is_test) { - installDemo(b, procedural_mesh_wgpu.build(b, options), "procedural_mesh_wgpu"); - installDemo(b, triangle_wgpu.build(b, options), "triangle_wgpu"); - installDemo(b, textured_quad_wgpu.build(b, options), "textured_quad_wgpu"); - installDemo(b, gui_test_wgpu.build(b, options), "gui_test_wgpu"); - installDemo(b, audio_experiments_wgpu.build(b, options), "audio_experiments_wgpu"); - installDemo(b, bullet_physics_test_wgpu.build(b, options), "bullet_physics_test_wgpu"); - installDemo( - b, - physically_based_rendering_wgpu.build(b, options), - "physically_based_rendering_wgpu", - ); - } - - // - // Windows-only demos - // - if (!builtin.is_test and @import("builtin").target.os.tag == .windows and - @import("builtin").zig_backend == .stage1) - { - options.zpix_enable = b.option( - bool, - "zpix-enable", - "Enable PIX GPU events and markers", - ) orelse false; - options.enable_dx_debug = b.option( - bool, - "enable-dx-debug", - "Enable debug layer for D3D12, D2D1, DirectML and DXGI", - ) orelse false; - options.enable_dx_gpu_debug = b.option( - bool, - "enable-dx-gpu-debug", - "Enable GPU-based validation for D3D12", - ) orelse false; - - installDemo(b, audio_experiments.build(b, options), "audio_experiments"); - installDemo(b, audio_playback_test.build(b, options), "audio_playback_test"); - installDemo(b, bindless.build(b, options), "bindless"); - installDemo(b, bullet_physics_test.build(b, options), "bullet_physics_test"); - installDemo(b, directml_convolution_test.build(b, options), "directml_convolution_test"); - installDemo(b, mesh_shader_test.build(b, options), "mesh_shader_test"); - installDemo(b, physically_based_rendering.build(b, options), "physically_based_rendering"); - installDemo(b, rasterization.build(b, options), "rasterization"); - installDemo(b, simple3d.build(b, options), "simple3d"); - installDemo(b, simple_raytracer.build(b, options), "simple_raytracer"); - installDemo(b, textured_quad.build(b, options), "textured_quad"); - installDemo(b, vector_graphics_test.build(b, options), "vector_graphics_test"); - installDemo(b, triangle.build(b, options), "triangle"); - installDemo(b, minimal.build(b, options), "minimal"); - installDemo(b, procedural_mesh.build(b, options), "procedural_mesh"); - - comptime var intro_index: u32 = 0; - inline while (intro_index < 7) : (intro_index += 1) { - const name = "intro" ++ comptime std.fmt.comptimePrint("{}", .{intro_index}); - installDemo(b, intro.build(b, options, intro_index), name); - } - } - - // - // Tests - // - const test_step = b.step("test", "Run all tests"); - - const zpool_tests = @import("libs/zpool/build.zig").buildTests(b, options.build_mode, options.target); - test_step.dependOn(&zpool_tests.step); - - const zgpu_tests = @import("libs/zgpu/build.zig").buildTests(b, options.build_mode, options.target); - zgpu_tests.want_lto = false; // TODO: Problems with LTO on Windows. - zglfw.link(zgpu_tests); - test_step.dependOn(&zgpu_tests.step); - - const zmath_tests = zmath.buildTests(b, options.build_mode, options.target); - test_step.dependOn(&zmath_tests.step); - - const zbullet_tests = @import("libs/zbullet/build.zig").buildTests(b, options.build_mode, options.target); - zbullet_tests.addPackage(zmath.pkg); - test_step.dependOn(&zbullet_tests.step); - - const znoise_tests = @import("libs/znoise/build.zig").buildTests(b, options.build_mode, options.target); - test_step.dependOn(&znoise_tests.step); - - const zmesh_tests = @import("libs/zmesh/build.zig").buildTests(b, options.build_mode, options.target); - test_step.dependOn(&zmesh_tests.step); - - const zaudio_tests = @import("libs/zaudio/build.zig").buildTests(b, options.build_mode, options.target); - test_step.dependOn(&zaudio_tests.step); - - const zjolt_tests = @import("libs/zjolt/build.zig").buildTests(b, options.build_mode, options.target); - test_step.dependOn(&zjolt_tests.step); - - const zglfw_tests = @import("libs/zglfw/build.zig").buildTests(b, options.build_mode, options.target); - test_step.dependOn(&zglfw_tests.step); - - const znetwork_tests = @import("libs/znetwork/build.zig").buildTests(b, options.build_mode, options.target); - test_step.dependOn(&znetwork_tests.step); - - // - // Benchmarks - // - if (!builtin.is_test) { - const benchmark_step = b.step("benchmark", "Run all benchmarks"); - { - const run_cmd = zmath.buildBenchmarks(b, options.target).run(); - benchmark_step.dependOn(&run_cmd.step); - } + options.zpix_enable = b.option( + bool, + "zpix-enable", + "Enable PIX GPU events and markers", + ) orelse false; + options.enable_dx_debug = b.option( + bool, + "enable-dx-debug", + "Enable debug layer for D3D12, D2D1, DirectML and DXGI", + ) orelse false; + options.enable_dx_gpu_debug = b.option( + bool, + "enable-dx-gpu-debug", + "Enable GPU-based validation for D3D12", + ) orelse false; + + installDemo(b, audio_experiments.build(b, options), "audio_experiments"); + installDemo(b, audio_playback_test.build(b, options), "audio_playback_test"); + installDemo(b, bindless.build(b, options), "bindless"); + installDemo(b, bullet_physics_test.build(b, options), "bullet_physics_test"); + installDemo(b, directml_convolution_test.build(b, options), "directml_convolution_test"); + installDemo(b, mesh_shader_test.build(b, options), "mesh_shader_test"); + installDemo(b, physically_based_rendering.build(b, options), "physically_based_rendering"); + installDemo(b, rasterization.build(b, options), "rasterization"); + installDemo(b, simple3d.build(b, options), "simple3d"); + installDemo(b, simple_raytracer.build(b, options), "simple_raytracer"); + installDemo(b, textured_quad.build(b, options), "textured_quad"); + installDemo(b, vector_graphics_test.build(b, options), "vector_graphics_test"); + installDemo(b, triangle.build(b, options), "triangle"); + installDemo(b, minimal.build(b, options), "minimal"); + installDemo(b, procedural_mesh.build(b, options), "procedural_mesh"); + + comptime var intro_index: u32 = 0; + inline while (intro_index < 7) : (intro_index += 1) { + const name = "intro" ++ comptime std.fmt.comptimePrint("{}", .{intro_index}); + installDemo(b, intro.build(b, options, intro_index), name); } } -const zmath = @import("libs/zmath/build.zig"); -const zglfw = @import("libs/zglfw/build.zig"); - const audio_experiments = @import("samples/audio_experiments/build.zig"); const audio_playback_test = @import("samples/audio_playback_test/build.zig"); const bindless = @import("samples/bindless/build.zig"); @@ -149,13 +73,6 @@ const vector_graphics_test = @import("samples/vector_graphics_test/build.zig"); const intro = @import("samples/intro/build.zig"); const minimal = @import("samples/minimal/build.zig"); const procedural_mesh = @import("samples/procedural_mesh/build.zig"); -const triangle_wgpu = @import("samples/triangle_wgpu/build.zig"); -const procedural_mesh_wgpu = @import("samples/procedural_mesh_wgpu/build.zig"); -const textured_quad_wgpu = @import("samples/textured_quad_wgpu/build.zig"); -const physically_based_rendering_wgpu = @import("samples/physically_based_rendering_wgpu/build.zig"); -const bullet_physics_test_wgpu = @import("samples/bullet_physics_test_wgpu/build.zig"); -const audio_experiments_wgpu = @import("samples/audio_experiments_wgpu/build.zig"); -const gui_test_wgpu = @import("samples/gui_test_wgpu/build.zig"); pub const Options = struct { build_mode: std.builtin.Mode, @@ -192,45 +109,3 @@ fn installDemo( b.getInstallStep().dependOn(install); } - -inline fn thisDir() []const u8 { - return comptime std.fs.path.dirname(@src().file) orelse "."; -} - -fn ensureGitLfs(allocator: std.mem.Allocator) void { - const printErrorMsg = (struct { - fn impl() void { - std.debug.print( - \\--------------------------------------------------------------------------- - \\git-lfs not found. - \\ - \\Please install Git LFS (Large File Support) and run (in the repo): - \\ - \\git lfs install - \\git lfs pull - \\ - \\For more info please see: https://git-lfs.github.com/ - \\--------------------------------------------------------------------------- - \\ - , .{}); - } - }).impl; - - const argv = &[_][]const u8{ "git-lfs", "--version" }; - const result = std.ChildProcess.exec(.{ - .allocator = allocator, - .argv = argv, - .cwd = ".", - }) catch { // e.g. FileNotFound - printErrorMsg(); - std.process.exit(1); - }; - defer { - allocator.free(result.stderr); - allocator.free(result.stdout); - } - if (result.term.Exited != 0) { - printErrorMsg(); - std.process.exit(1); - } -} diff --git a/libs/common/build.zig b/libs/common/build.zig index c1a4610a5..775d6a787 100644 --- a/libs/common/build.zig +++ b/libs/common/build.zig @@ -15,10 +15,10 @@ pub fn build(b: *std.build.Builder) void { pub fn link(exe: *std.build.LibExeObjStep) void { const lib = buildLibrary(exe); exe.linkLibrary(lib); - exe.addIncludePath(thisDir() ++ "/src/c"); + //exe.addIncludePath(thisDir() ++ "/src/c"); exe.addIncludePath(thisDir() ++ "/libs/imgui"); exe.addIncludePath(thisDir() ++ "/../zmesh/libs/cgltf"); - exe.addIncludePath(thisDir() ++ "/../zgpu/libs/stb"); + exe.addIncludePath(thisDir() ++ "/../zstbi/libs/stbi"); } fn buildLibrary(exe: *std.build.LibExeObjStep) *std.build.LibExeObjStep { @@ -26,7 +26,7 @@ fn buildLibrary(exe: *std.build.LibExeObjStep) *std.build.LibExeObjStep { lib.setBuildMode(exe.build_mode); lib.setTarget(exe.target); - lib.addIncludePath(thisDir() ++ "/src/c"); + //lib.addIncludePath(thisDir() ++ "/src/c"); lib.linkSystemLibraryName("c"); lib.linkSystemLibraryName("c++"); @@ -43,8 +43,8 @@ fn buildLibrary(exe: *std.build.LibExeObjStep) *std.build.LibExeObjStep { lib.addIncludePath(thisDir() ++ "/../zmesh/libs/cgltf"); lib.addCSourceFile(thisDir() ++ "/../zmesh/libs/cgltf/cgltf.c", &.{"-std=c99"}); - lib.addIncludePath(thisDir() ++ "/../zgpu/libs/stb"); - lib.addCSourceFile(thisDir() ++ "/../zgpu/libs/stb/stb_image.c", &.{"-std=c99"}); + lib.addIncludePath(thisDir() ++ "/../zstbi/libs/stbi"); + lib.addCSourceFile(thisDir() ++ "/../zstbi/libs/stbi/stb_image.c", &.{"-std=c99"}); return lib; } diff --git a/libs/zaudio/README.md b/libs/zaudio/README.md deleted file mode 100644 index a6802bee0..000000000 --- a/libs/zaudio/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# zaudio v0.2 - Cross-platform audio - -Zig friendly bindings for great [miniaudio](https://github.com/mackron/miniaudio) library. Tested on Windows, Linux and macOS but should also work on mobile/web platforms. - -As an example program please see [audio experiments (wgpu)](https://github.com/michal-z/zig-gamedev/tree/main/samples/audio_experiments_wgpu). - -## Features - -Provided structs: - -- [x] `Device` -- [x] `Engine` -- [x] `Sound` -- [x] `SoundGroup` -- [x] `NodeGraph` -- [x] `Fence` -- [ ] `Context` (missing methods) -- [ ] `ResourceManager` (missing methods) -- [ ] `Log` (missing methods) -- [ ] `DataSource` (missing methods) - - [x] `WaveformDataSource` - - [x] `NoiseDataSource` - - [ ] custom data sources -- [x] `Node` - - [x] `DataSourceNode` - - [x] `SplitterNode` - - [x] `BiquadNode` - - [x] `LpfNode` - - [x] `HpfNode` - - [x] `NotchNode` - - [x] `PeakNode` - - [x] `LoshelfNode` - - [x] `HishelfNode` - - [x] `DelayNode` - - [ ] custom nodes - -## Getting started - -Copy `zaudio` folder to a `libs` subdirectory of the root of your project. - -Then in your `build.zig` add: - -```zig -const std = @import("std"); -const zaudio = @import("libs/zaudio/build.zig"); - -pub fn build(b: *std.build.Builder) void { - ... - exe.addPackage(zaudio.pkg); - - zaudio.link(exe); -} -``` - -Now in your code you may import and use `zaudio`: - -```zig -const zaudio = @import("zaudio"); - -pub fn main() !void { - ... - const engine = try zaudio.createEngine(allocator, null); - - const music = try engine.createSoundFromFile( - allocator, - content_dir ++ "Broke For Free - Night Owl.mp3", - .{ .flags = .{ .stream = true } }, - ); - try music.start(); - ... -} -``` diff --git a/libs/zaudio/build.zig b/libs/zaudio/build.zig deleted file mode 100644 index 1a46ccd75..000000000 --- a/libs/zaudio/build.zig +++ /dev/null @@ -1,53 +0,0 @@ -const std = @import("std"); - -pub const pkg = std.build.Pkg{ - .name = "zaudio", - .source = .{ .path = thisDir() ++ "/src/zaudio.zig" }, -}; - -pub fn link(exe: *std.build.LibExeObjStep) void { - exe.addIncludePath(thisDir() ++ "/libs/miniaudio"); - exe.linkSystemLibraryName("c"); - - const target = (std.zig.system.NativeTargetInfo.detect(exe.target) catch unreachable).target; - - if (target.os.tag == .macos) { - const system_sdk = @import("system_sdk.zig"); - system_sdk.include(exe.builder, exe, .{}); - exe.linkFramework("CoreAudio"); - exe.linkFramework("CoreFoundation"); - exe.linkFramework("AudioUnit"); - exe.linkFramework("AudioToolbox"); - } else if (target.os.tag == .linux) { - exe.linkSystemLibraryName("pthread"); - exe.linkSystemLibraryName("m"); - exe.linkSystemLibraryName("dl"); - } - - exe.addCSourceFile(thisDir() ++ "/libs/miniaudio/zaudio.c", &.{"-std=c99"}); - exe.addCSourceFile(thisDir() ++ "/libs/miniaudio/miniaudio.c", &.{ - "-DMA_NO_WEBAUDIO", - "-DMA_NO_ENCODING", - "-DMA_NO_NULL", - "-DMA_NO_JACK", - "-std=c99", - "-fno-sanitize=undefined", - if (target.os.tag == .macos) "-DMA_NO_RUNTIME_LINKING" else "", - }); -} - -pub fn buildTests( - b: *std.build.Builder, - build_mode: std.builtin.Mode, - target: std.zig.CrossTarget, -) *std.build.LibExeObjStep { - const tests = b.addTest(pkg.source.path); - tests.setBuildMode(build_mode); - tests.setTarget(target); - link(tests); - return tests; -} - -inline fn thisDir() []const u8 { - return comptime std.fs.path.dirname(@src().file) orelse "."; -} diff --git a/libs/zaudio/libs/miniaudio/miniaudio.c b/libs/zaudio/libs/miniaudio/miniaudio.c deleted file mode 100644 index afcddad04..000000000 --- a/libs/zaudio/libs/miniaudio/miniaudio.c +++ /dev/null @@ -1,2 +0,0 @@ -#define MINIAUDIO_IMPLEMENTATION -#include "miniaudio.h" diff --git a/libs/zaudio/libs/miniaudio/miniaudio.h b/libs/zaudio/libs/miniaudio/miniaudio.h deleted file mode 100644 index f774f0d5f..000000000 --- a/libs/zaudio/libs/miniaudio/miniaudio.h +++ /dev/null @@ -1,90463 +0,0 @@ -/* -Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.9 - 2022-04-20 - -David Reid - mackron@gmail.com - -Website: https://miniaud.io -Documentation: https://miniaud.io/docs -GitHub: https://github.com/mackron/miniaudio -*/ - -/* -1. Introduction -=============== -miniaudio is a single file library for audio playback and capture. To use it, do the following in -one .c file: - - ```c - #define MINIAUDIO_IMPLEMENTATION - #include "miniaudio.h" - ``` - -You can do `#include "miniaudio.h"` in other parts of the program just like any other header. - -miniaudio includes both low level and high level APIs. The low level API is good for those who want -to do all of their mixing themselves and only require a light weight interface to the underlying -audio device. The high level API is good for those who have complex mixing and effect requirements. - -In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles -to opaque objects which means you need to allocate memory for objects yourself. In the examples -presented in this documentation you will often see objects declared on the stack. You need to be -careful when translating these examples to your own code so that you don't accidentally declare -your objects on the stack and then cause them to become invalid once the function returns. In -addition, you must ensure the memory address of your objects remain the same throughout their -lifetime. You therefore cannot be making copies of your objects. - -A config/init pattern is used throughout the entire library. The idea is that you set up a config -object and pass that into the initialization routine. The advantage to this system is that the -config object can be initialized with logical defaults and new properties added to it without -breaking the API. The config object can be allocated on the stack and does not need to be -maintained after initialization of the corresponding object. - - -1.1. Low Level API ------------------- -The low level API gives you access to the raw audio data of an audio device. It supports playback, -capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which -physical device(s) you want to connect to. - -The low level API uses the concept of a "device" as the abstraction for physical devices. The idea -is that you choose a physical device to emit or capture audio from, and then move data to/from the -device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a -callback which you specify when initializing the device. - -When initializing the device you first need to configure it. The device configuration allows you to -specify things like the format of the data delivered via the callback, the size of the internal -buffer and the ID of the device you want to emit or capture audio from. - -Once you have the device configuration set up you can initialize the device. When initializing a -device you need to allocate memory for the device object beforehand. This gives the application -complete control over how the memory is allocated. In the example below we initialize a playback -device on the stack, but you could allocate it on the heap if that suits your situation better. - - ```c - void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both - // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than - // frameCount frames. - } - - int main() - { - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. - config.playback.channels = 2; // Set to 0 to use the device's native channel count. - config.sampleRate = 48000; // Set to 0 to use the device's native sample rate. - config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. - config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData). - - ma_device device; - if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) { - return -1; // Failed to initialize the device. - } - - ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually. - - // Do something here. Probably your program's main loop. - - ma_device_uninit(&device); // This will stop the device so no need to do that manually. - return 0; - } - ``` - -In the example above, `data_callback()` is where audio data is written and read from the device. -The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data -to the output buffer (`pOutput` in the example). In capture mode you read data from the input -buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you -how many frames can be written to the output buffer and read from the input buffer. A "frame" is -one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 -samples: one for the left, one for the right. The channel count is defined by the device config. -The size in bytes of an individual sample is defined by the sample format which is also specified -in the device config. Multi-channel audio data is always interleaved, which means the samples for -each frame are stored next to each other in memory. For example, in a stereo stream the first pair -of samples will be the left and right samples for the first frame, the second pair of samples will -be the left and right samples for the second frame, etc. - -The configuration of the device is defined by the `ma_device_config` structure. The config object -is always initialized with `ma_device_config_init()`. It's important to always initialize the -config with this function as it initializes it with logical defaults and ensures your program -doesn't break when new members are added to the `ma_device_config` structure. The example above -uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes -a single parameter, which is whether or not the device is a playback, capture, duplex or loopback -device (loopback devices are not supported on all backends). The `config.playback.format` member -sets the sample format which can be one of the following (all formats are native-endian): - - +---------------+----------------------------------------+---------------------------+ - | Symbol | Description | Range | - +---------------+----------------------------------------+---------------------------+ - | ma_format_f32 | 32-bit floating point | [-1, 1] | - | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | - | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | - | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | - | ma_format_u8 | 8-bit unsigned integer | [0, 255] | - +---------------+----------------------------------------+---------------------------+ - -The `config.playback.channels` member sets the number of channels to use with the device. The -channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate -(which must be the same for both playback and capture in full-duplex configurations). This is -usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between -8000 and 384000, however. - -Note that leaving the format, channel count and/or sample rate at their default values will result -in the internal device's native configuration being used which is useful if you want to avoid the -overhead of miniaudio's automatic data conversion. - -In addition to the sample format, channel count and sample rate, the data callback and user data -pointer are also set via the config. The user data pointer is not passed into the callback as a -parameter, but is instead set to the `pUserData` member of `ma_device` which you can access -directly since all miniaudio structures are transparent. - -Initializing the device is done with `ma_device_init()`. This will return a result code telling you -what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is -complete the device will be in a stopped state. To start it, use `ma_device_start()`. -Uninitializing the device will stop it, which is what the example above does, but you can also stop -the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. -Note that it's important to never stop or start the device from inside the callback. This will -result in a deadlock. Instead you set a variable or signal an event indicating that the device -needs to stop and handle it in a different thread. The following APIs must never be called inside -the callback: - - ```c - ma_device_init() - ma_device_init_ex() - ma_device_uninit() - ma_device_start() - ma_device_stop() - ``` - -You must never try uninitializing and reinitializing a device inside the callback. You must also -never try to stop and start it from inside the callback. There are a few other things you shouldn't -do in the callback depending on your requirements, however this isn't so much a thread-safety -thing, but rather a real-time processing thing which is beyond the scope of this introduction. - -The example above demonstrates the initialization of a playback device, but it works exactly the -same for capture. All you need to do is change the device type from `ma_device_type_playback` to -`ma_device_type_capture` when setting up the config, like so: - - ```c - ma_device_config config = ma_device_config_init(ma_device_type_capture); - config.capture.format = MY_FORMAT; - config.capture.channels = MY_CHANNEL_COUNT; - ``` - -In the data callback you just read from the input buffer (`pInput` in the example above) and leave -the output buffer alone (it will be set to NULL when the device type is set to -`ma_device_type_capture`). - -These are the available device types and how you should handle the buffers in the callback: - - +-------------------------+--------------------------------------------------------+ - | Device Type | Callback Behavior | - +-------------------------+--------------------------------------------------------+ - | ma_device_type_playback | Write to output buffer, leave input buffer untouched. | - | ma_device_type_capture | Read from input buffer, leave output buffer untouched. | - | ma_device_type_duplex | Read from input buffer, write to output buffer. | - | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. | - +-------------------------+--------------------------------------------------------+ - -You will notice in the example above that the sample format and channel count is specified -separately for playback and capture. This is to support different data formats between the playback -and capture devices in a full-duplex system. An example may be that you want to capture audio data -as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you -use different formats between playback and capture in a full-duplex configuration you will need to -convert the data yourself. There are functions available to help you do this which will be -explained later. - -The example above did not specify a physical device to connect to which means it will use the -operating system's default device. If you have multiple physical devices connected and you want to -use a specific one you will need to specify the device ID in the configuration, like so: - - ```c - config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device. - config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device. - ``` - -To retrieve the device ID you will need to perform device enumeration, however this requires the -use of a new concept called the "context". Conceptually speaking the context sits above the device. -There is one context to many devices. The purpose of the context is to represent the backend at a -more global level and to perform operations outside the scope of an individual device. Mainly it is -used for performing run-time linking against backend libraries, initializing backends and -enumerating devices. The example below shows how to enumerate devices. - - ```c - ma_context context; - if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { - // Error. - } - - ma_device_info* pPlaybackInfos; - ma_uint32 playbackCount; - ma_device_info* pCaptureInfos; - ma_uint32 captureCount; - if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) { - // Error. - } - - // Loop over each device info and do something with it. Here we just print the name with their index. You may want - // to give the user the opportunity to choose which device they'd prefer. - for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) { - printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name); - } - - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id; - config.playback.format = MY_FORMAT; - config.playback.channels = MY_CHANNEL_COUNT; - config.sampleRate = MY_SAMPLE_RATE; - config.dataCallback = data_callback; - config.pUserData = pMyCustomData; - - ma_device device; - if (ma_device_init(&context, &config, &device) != MA_SUCCESS) { - // Error - } - - ... - - ma_device_uninit(&device); - ma_context_uninit(&context); - ``` - -The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. -The first parameter is a pointer to a list of `ma_backend` values which are used to override the -default backend priorities. When this is NULL, as in this example, miniaudio's default priorities -are used. The second parameter is the number of backends listed in the array pointed to by the -first parameter. The third parameter is a pointer to a `ma_context_config` object which can be -NULL, in which case defaults are used. The context configuration is used for setting the logging -callback, custom memory allocation callbacks, user-defined data and some backend-specific -configurations. - -Once the context has been initialized you can enumerate devices. In the example above we use the -simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by -using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer -to a pointer that will, upon output, be set to a pointer to a buffer containing a list of -`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive -the number of items in the returned buffer. Do not free the returned buffers as their memory is -managed internally by miniaudio. - -The `ma_device_info` structure contains an `id` member which is the ID you pass to the device -config. It also contains the name of the device which is useful for presenting a list of devices -to the user via the UI. - -When creating your own context you will want to pass it to `ma_device_init()` when initializing the -device. Passing in NULL, like we do in the first example, will result in miniaudio creating the -context for you, which you don't want to do since you've already created a context. Note that -internally the context is only tracked by it's pointer which means you must not change the location -of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for -the context. - - -1.2. High Level API -------------------- -The high level API consists of three main parts: - - * Resource management for loading and streaming sounds. - * A node graph for advanced mixing and effect processing. - * A high level "engine" that wraps around the resource manager and node graph. - -The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds -fully into memory and also streaming. It will also deal with reference counting for you which -avoids the same sound being loaded multiple times. - -The node graph is used for mixing and effect processing. The idea is that you connect a number of -nodes into the graph by connecting each node's outputs to another node's inputs. Each node can -implement it's own effect. By chaining nodes together, advanced mixing and effect processing can -be achieved. - -The engine encapsulates both the resource manager and the node graph to create a simple, easy to -use high level API. The resource manager and node graph APIs are covered in more later sections of -this manual. - -The code below shows how you can initialize an engine using it's default configuration. - - ```c - ma_result result; - ma_engine engine; - - result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -This creates an engine instance which will initialize a device internally which you can access with -`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed -with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which -means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a -cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast. - -Note that all objects in miniaudio, including the `ma_engine` object in the example above, are -transparent structures. There are no handles to opaque structures in miniaudio which means you need -to be mindful of how you declare them. In the example above we are declaring it on the stack, but -this will result in the struct being invalidated once the function encapsulating it returns. If -allocating the engine on the heap is more appropriate, you can easily do so with a standard call -to `malloc()` or whatever heap allocation routine you like: - - ```c - ma_engine* pEngine = malloc(sizeof(*pEngine)); - ``` - -The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure -an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of -`ma_engine_init()`: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage. - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; - } - ``` - -This creates an engine instance using a custom config. In this particular example it's showing how -you can specify a custom resource manager rather than having the engine initialize one internally. -This is particularly useful if you want to have multiple engine's share the same resource manager. - -The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed. - -By default the engine will be started, but nothing will be playing because no sounds have been -initialized. The easiest but least flexible way of playing a sound is like so: - - ```c - ma_engine_play_sound(&engine, "my_sound.wav", NULL); - ``` - -This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the -internal sound up for recycling. The last parameter is used to specify which sound group the sound -should be associated with which will be explained later. This particular way of playing a sound is -simple, but lacks flexibility and features. A more flexible way of playing a sound is to first -initialize a sound: - - ```c - ma_result result; - ma_sound sound; - - result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound); - if (result != MA_SUCCESS) { - return result; - } - - ma_sound_start(&sound); - ``` - -This returns a `ma_sound` object which represents a single instance of the specified sound file. If -you want to play the same file multiple times simultaneously, you need to create one sound for each -instance. - -Sounds should be uninitialized with `ma_sound_uninit()`. - -Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with -`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use -`ma_sound_seek_to_pcm_frames(&sound, 0)` to seek back to the start of a sound. By default, starting -and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound -the be started and/or stopped at a specific time. This can be done with the following functions: - - ```c - ma_sound_set_start_time_in_pcm_frames() - ma_sound_set_start_time_in_milliseconds() - ma_sound_set_stop_time_in_pcm_frames() - ma_sound_set_stop_time_in_milliseconds() - ``` - -The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`. -The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if -required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()` -before anything will play: - - ```c - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2); - ma_sound_start(&sound); - ``` - -The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be -loaded and a few options on which features should be enabled for that sound. By default, the sound -is synchronously loaded fully into memory straight from the file system without any kind of -decoding. If you want to decode the sound before storing it in memory, you need to specify the -`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier -stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing -time which might be too expensive on the audio thread. - -If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This -will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing -until the sound has had some audio decoded. - -The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise -sounds into groups which have their own effect processing and volume control. An example is a game -which might have separate groups for sfx, voice and music. Each of these groups have their own -independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize -a sound group. - -Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node` -API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex -effect chains. - -A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume -control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. - -Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know -a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect, -you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. - -By default, sounds and sound groups have spatialization enabled. If you don't ever want to -spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The -spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and -environmental occlusion are not currently supported, but planned for the future. The supported -features include: - - * Sound and listener positioning and orientation with cones - * Attenuation models: none, inverse, linear and exponential - * Doppler effect - -Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`. - -To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound -is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with -`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping. - - - -2. Building -=========== -miniaudio should work cleanly out of the box without the need to download or install any -dependencies. See below for platform-specific details. - - -2.1. Windows ------------- -The Windows build should compile cleanly on all popular compilers without the need to configure any -include paths nor link to any libraries. - -The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external -symbol for `ActivateAudioInterfaceAsync()`. - - -2.2. macOS and iOS ------------------- -The macOS build should compile cleanly without the need to download any dependencies nor link to -any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to -link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling -through the command line requires linking to `-lpthread` and `-lm`. - -Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's -notarization process. To fix this there are two options. The first is to use the -`MA_NO_RUNTIME_LINKING` option, like so: - - ```c - #ifdef __APPLE__ - #define MA_NO_RUNTIME_LINKING - #endif - #define MINIAUDIO_IMPLEMENTATION - #include "miniaudio.h" - ``` - -This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`. -Alternatively, if you would rather keep using runtime linking you can add the following to your -entitlements.xcent file: - - ``` - com.apple.security.cs.allow-dyld-environment-variables - - com.apple.security.cs.allow-unsigned-executable-memory - - ``` - -See this discussion for more info: https://github.com/mackron/miniaudio/issues/203. - - -2.3. Linux ----------- -The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any -development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM. - - -2.4. BSD --------- -The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses -sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit -ARM. - - -2.5. Android ------------- -AAudio is the highest priority backend on Android. This should work out of the box without needing -any kind of compiler configuration. Support for AAudio starts with Android 8 which means older -versions will fall back to OpenSL|ES which requires API level 16+. - -There have been reports that the OpenSL|ES backend fails to initialize on some Android based -devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform -you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. - - -2.6. Emscripten ---------------- -The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. -You cannot use `-std=c*` compiler flags, nor `-ansi`. - - -2.7. Build Options ------------------- -`#define` these options before including miniaudio.h. - - +----------------------------------+--------------------------------------------------------------------+ - | Option | Description | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WASAPI | Disables the WASAPI backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DSOUND | Disables the DirectSound backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WINMM | Disables the WinMM backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ALSA | Disables the ALSA backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_JACK | Disables the JACK backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_COREAUDIO | Disables the Core Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_SNDIO | Disables the sndio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AUDIO4 | Disables the audio(4) backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_OSS | Disables the OSS backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AAUDIO | Disables the AAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_OPENSL | Disables the OpenSL|ES backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WEBAUDIO | Disables the Web Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NULL | Disables the null backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to | - | | enable specific backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the WASAPI backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the DirectSound backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the WinMM backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the ALSA backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the PulseAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the JACK backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the Core Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the sndio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the audio(4) backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the OSS backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the AAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the OpenSL|ES backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the Web Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the null backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DECODING | Disables decoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ENCODING | Disables encoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WAV | Disables the built-in WAV decoder and encoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_FLAC | Disables the built-in FLAC decoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_MP3 | Disables the built-in MP3 decoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` | - | | and `ma_device` APIs. This is useful if you only want to use | - | | miniaudio's data conversion and/or decoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | - | | `ma_event` APIs. This option is useful if you only need to use | - | | miniaudio for data conversion, decoding and/or encoding. Some | - | | families of APIsrequire threading which means the following | - | | options must also be set: | - | | | - | | ``` | - | | MA_NO_DEVICE_IO | - | | ``` | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_SSE2 | Disables SSE2 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AVX2 | Disables AVX2 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NEON | Disables NEON optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's | - | | notarization process. When enabling this, you may need to avoid | - | | using `-std=c89` or `-std=c99` on Linux builds or else you may end | - | | up with compilation errors due to conflicts with `timespec` and | - | | `timeval` data types. | - | | | - | | You may need to enable this if your target platform does not allow | - | | runtime linking via `dlopen()`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | - +----------------------------------+--------------------------------------------------------------------+ - | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | - | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_API | Controls how public APIs should be decorated. Default is `extern`. | - +----------------------------------+--------------------------------------------------------------------+ - - -3. Definitions -============== -This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity -in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio -uses each term. - -3.1. Sample ------------ -A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit -floating point number. - -3.2. Frame / PCM Frame ----------------------- -A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 -samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" -and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. -If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always -clarify what it's referring to with something like "FLAC frame". - -3.3. Channel ------------- -A stream of monaural audio that is emitted from an individual speaker in a speaker system, or -received from an individual microphone in a microphone system. A stereo stream has two channels (a -left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio -systems refer to a channel as a complex audio stream that's mixed with other channels to produce -the final mix - this is completely different to miniaudio's use of the term "channel" and should -not be confused. - -3.4. Sample Rate ----------------- -The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number -of PCM frames that are processed per second. - -3.5. Formats ------------- -Throughout miniaudio you will see references to different sample formats: - - +---------------+----------------------------------------+---------------------------+ - | Symbol | Description | Range | - +---------------+----------------------------------------+---------------------------+ - | ma_format_f32 | 32-bit floating point | [-1, 1] | - | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | - | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | - | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | - | ma_format_u8 | 8-bit unsigned integer | [0, 255] | - +---------------+----------------------------------------+---------------------------+ - -All formats are native-endian. - - - -4. Data Sources -=============== -The data source abstraction in miniaudio is used for retrieving audio data from some source. A few -examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data -sources in order to make sense of some of the higher level concepts in miniaudio. - -The `ma_data_source` API is a generic interface for reading from a data source. Any object that -implements the data source interface can be plugged into any `ma_data_source` function. - -To read data from a data source: - - ```c - ma_result result; - ma_uint64 framesRead; - - result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead, loop); - if (result != MA_SUCCESS) { - return result; // Failed to read data from the data source. - } - ``` - -If you don't need the number of frames that were successfully read you can pass in `NULL` to the -`pFramesRead` parameter. If this returns a value less than the number of frames requested it means -the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames -read is 0. - -When calling any data source function, with the exception of `ma_data_source_init()` and -`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example, -you could plug in a decoder like so: - - ```c - ma_result result; - ma_uint64 framesRead; - ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. - - result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead, loop); - if (result != MA_SUCCESS) { - return result; // Failed to read data from the decoder. - } - ``` - -If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you -can use `ma_data_source_seek_pcm_frames()`. - -To seek to a specific PCM frame: - - ```c - result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); - if (result != MA_SUCCESS) { - return result; // Failed to seek to PCM frame. - } - ``` - -You can retrieve the total length of a data source in PCM frames, but note that some data sources -may not have the notion of a length, such as noise and waveforms, and others may just not have a -way of determining the length such as some decoders. To retrieve the length: - - ```c - ma_uint64 length; - - result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve the length. - } - ``` - -Care should be taken when retrieving the length of a data source where the underlying decoder is -pulling data from a data stream with an undefined length, such as internet radio or some kind of -broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return. - -The current position of the cursor in PCM frames can also be retrieved: - - ```c - ma_uint64 cursor; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve the cursor. - } - ``` - -You will often need to know the data format that will be returned after reading. This can be -retrieved like so: - - ```c - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - - result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve data format. - } - ``` - -If you do not need a specific data format property, just pass in NULL to the respective parameter. - -There may be cases where you want to implement something like a sound bank where you only want to -read data within a certain range of the underlying data. To do this you can use a range: - - ```c - result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames); - if (result != MA_SUCCESS) { - return result; // Failed to set the range. - } - ``` - -This is useful if you have a sound bank where many sounds are stored in the same file and you want -the data source to only play one of those sub-sounds. - -Custom loop points can also be used with data sources. By default, data sources will loop after -they reach the end of the data source, but if you need to loop at a specific location, you can do -the following: - - ```c - result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); - if (result != MA_SUCCESS) { - return result; // Failed to set the loop point. - } - ``` - -The loop point is relative to the current range. - -It's sometimes useful to chain data sources together so that a seamless transition can be achieved. -To do this, you can use chaining: - - ```c - ma_decoder decoder1; - ma_decoder decoder2; - - // ... initialize decoders with ma_decoder_init_*() ... - - result = ma_data_source_set_next(&decoder1, &decoder2); - if (result != MA_SUCCESS) { - return result; // Failed to set the next data source. - } - - result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead, MA_FALSE); - if (result != MA_SUCCESS) { - return result; // Failed to read from the decoder. - } - ``` - -In the example above we're using decoders. When reading from a chain, you always want to read from -the top level data source in the chain. In the example above, `decoder1` is the top level data -source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any -gaps. - -Note that the `loop` parameter is set to false in the example above. When this is set to true, only -the current data source will be looped. You can loop the entire chain by linking in a loop like so: - - ```c - ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 - ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start). - ``` - -Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically -changing links while the audio thread is in the middle of reading. - -Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple -instances of the same sound simultaneously. Instead, initialize multiple data sources for each -instance. This can be extremely inefficient depending on the data source and can result in -glitching due to subtle changes to the state of internal filters. - - -4.1. Custom Data Sources ------------------------- -You can implement a custom data source by implementing the functions in `ma_data_source_vtable`. -Your custom object must have `ma_data_source_base` as it's first member: - - ```c - struct my_data_source - { - ma_data_source_base base; - ... - }; - ``` - -In your initialization routine, you need to call `ma_data_source_init()` in order to set up the -base object (`ma_data_source_base`): - - ```c - static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) - { - // Read data here. Output in the same format returned by my_data_source_get_data_format(). - } - - static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) - { - // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported. - } - - static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) - { - // Return the format of the data here. - } - - static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) - { - // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor. - } - - static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength) - { - // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. - } - - static g_my_data_source_vtable = - { - my_data_source_read, - my_data_source_seek, - my_data_source_get_data_format, - my_data_source_get_cursor, - my_data_source_get_length - }; - - ma_result my_data_source_init(my_data_source* pMyDataSource) - { - ma_result result; - ma_data_source_config baseConfig; - - baseConfig = ma_data_source_config_init(); - baseConfig.vtable = &g_my_data_source_vtable; - - result = ma_data_source_init(&baseConfig, &pMyDataSource->base); - if (result != MA_SUCCESS) { - return result; - } - - // ... do the initialization of your custom data source here ... - - return MA_SUCCESS; - } - - void my_data_source_uninit(my_data_source* pMyDataSource) - { - // ... do the uninitialization of your custom data source here ... - - // You must uninitialize the base data source. - ma_data_source_uninit(&pMyDataSource->base); - } - ``` - -Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside -of the custom data source. It's up to the custom data source itself to call these within their own -init/uninit functions. - - - -5. Engine -========= -The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The -`ma_engine` object encapsulates a resource manager and a node graph, both of which will be -explained in more detail later. - -Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing -group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and -`ma_sound_group` objects are nodes within the engine's node graph. - -When the engine is initialized, it will normally create a device internally. If you would rather -manage the device yourself, you can do so and just pass a pointer to it via the engine config when -you initialize the engine. You can also just use the engine without a device, which again can be -configured via the engine config. - -The most basic way to initialize the engine is with a default config, like so: - - ```c - ma_result result; - ma_engine engine; - - result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -This will result in the engine initializing a playback device using the operating system's default -device. This will be sufficient for many use cases, but if you need more flexibility you'll want to -configure the engine with an engine config: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pPlaybackDevice = &myDevice; - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -In the example above we're passing in a pre-initialized device. Since the caller is the one in -control of the device's data callback, it's their responsibility to manually call -`ma_engine_read_pcm_frames()` from inside their data callback: - - ```c - void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL); - } - ``` - -You can also use the engine independent of a device entirely: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.noDevice = MA_TRUE; - engineConfig.channels = 2; // Must be set when not using a device. - engineConfig.sampleRate = 48000; // Must be set when not using a device. - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -Note that when you're not using a device, you must set the channel count and sample rate in the -config or else miniaudio won't know what to use (miniaudio will use the device to determine this -normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio -data from the engine. This kind of setup is useful if you want to do something like offline -processing. - -When a sound is loaded it goes through a resource manager. By default the engine will initialize a -resource manager internally, but you can also specify a pre-initialized resource manager: - - ```c - ma_result result; - ma_engine engine1; - ma_engine engine2; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pResourceManager = &myResourceManager; - - ma_engine_init(&engineConfig, &engine1); - ma_engine_init(&engineConfig, &engine2); - ``` - -In this example we are initializing two engines, both of which are sharing the same resource -manager. This is especially useful for saving memory when loading the same file across multiple -engines. If you were not to use a shared resource manager, each engine instance would use their own -which would result in any sounds that are used between both engine's being loaded twice. By using -a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you -need to output to multiple playback devices, such as in a local multiplayer game where each player -is using their own set of headphones. - -By default an engine will be in a started state. To make it so the engine is not automatically -started you can configure it as such: - - ```c - engineConfig.noAutoStart = MA_TRUE; - - // The engine will need to be started manually. - ma_engine_start(&engine); - - // Later on the engine can be stopped with ma_engine_stop(). - ma_engine_stop(&engine); - ``` - -The concept of starting or stopping an engine is only relevant when using the engine with a -device. Attempting to start or stop an engine that is not associated with a device will result in -`MA_INVALID_OPERATION`. - -The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a -linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you -prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear. - -When a sound is spatialized, it is done so relative to a listener. An engine can be configured to -have multiple listeners which can be configured via the config: - - ```c - engineConfig.listenerCount = 2; - ``` - -The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a -sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound -to a specific listener which will be explained later. Listener's have a position, direction, cone, -and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up -to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The -position, direction and velocity are all specified in absolute terms: - - ```c - ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ); - ``` - -The direction of the listener represents it's forward vector. The listener's up vector can also be -specified and defaults to +1 on the Y axis. - - ```c - ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ); - ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0); - ``` - -The engine supports directional attenuation. The listener can have a cone the controls how sound is -attenuated based on the listener's direction. When a sound is between the inner and outer cones, it -will be attenuated between 1 and the cone's outer gain: - - ```c - ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain); - ``` - -When a sound is inside the inner code, no directional attenuation is applied. When the sound is -outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When -the sound is in between the inner and outer cones, the attenuation will be interpolated between 1 -and the outer gain. - -The engine's coordinate system follows the OpenGL coordinate system where positive X points right, -positive Y points up and negative Z points forward. - -The simplest and least flexible way to play a sound is like so: - - ```c - ma_engine_play_sound(&engine, "my_sound.wav", pGroup); - ``` - -This is a "fire and forget" style of function. The engine will manage the `ma_sound` object -internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility -you'll want to initialize a sound object: - - ```c - ma_sound sound; - - result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound); - if (result != MA_SUCCESS) { - return result; // Failed to load sound. - } - ``` - -Sounds need to be uninitialized with `ma_sound_uninit()`. - -The example above loads a sound from a file. If the resource manager has been disabled you will not -be able to use this function and instead you'll need to initialize a sound directly from a data -source: - - ```c - ma_sound sound; - - result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound); - if (result != MA_SUCCESS) { - return result; - } - ``` - -Each `ma_sound` object represents a single instance of the sound. If you want to play the same -sound multiple times at the same time, you need to initialize a separate `ma_sound` object. - -For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's -standard config/init pattern: - - ```c - ma_sound sound; - ma_sound_config soundConfig; - - soundConfig = ma_sound_config_init(); - soundConfig.pFilePath = NULL; // Set this to load from a file path. - soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source. - soundConfig.pInitialAttachment = &someNodeInTheNodeGraph; - soundConfig.initialAttachmentInputBusIndex = 0; - soundConfig.channelsIn = 1; - soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count. - - result = ma_sound_init_ex(&soundConfig, &sound); - if (result != MA_SUCCESS) { - return result; - } - ``` - -In the example above, the sound is being initialized without a file nor a data source. This is -valid, in which case the sound acts as a node in the middle of the node graph. This means you can -connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly -what a `ma_sound_group` is. - -When loading a sound, you specify a set of flags that control how the sound is loaded and what -features are enabled for that sound. When no flags are set, the sound will be fully loaded into -memory in exactly the same format as how it's stored on the file system. The resource manager will -allocate a block of memory and then load the file directly into it. When reading audio data, it -will be decoded dynamically on the fly. In order to save processing time on the audio thread, it -might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound); - ``` - -By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until -the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously -by specificying the `MA_SOUND_FLAG_ASYNC` flag: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); - ``` - -This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully -loaded. When you start the sound, it won't output anything until some sound is available. The sound -will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE` -is specified. - -If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A -fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal -counter hit's zero. You can specify a fence like so: - - ```c - ma_result result; - ma_fence fence; - ma_sound sounds[4]; - - result = ma_fence_init(&fence); - if (result != MA_SUCCES) { - return result; - } - - // Load some sounds asynchronously. - for (int iSound = 0; iSound < 4; iSound += 1) { - ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]); - } - - // ... do some other stuff here in the mean time ... - - // Wait for all sounds to finish loading. - ma_fence_wait(&fence); - ``` - -If loading the entire sound into memory is prohibitive, you can also configure the engine to stream -the audio data: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound); - ``` - -When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work -fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music -tracks in games. - -When you initialize a sound, if you specify a sound group the sound will be attached to that group -automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. -If you would instead rather leave the sound unattached by default, you can can specify the -`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node -graph. - -Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with -`ma_sound_stop()`. - -Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the -engine's master volume. - -Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan -to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas -+1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger -value will result in a higher pitch. The pitch must be greater than 0. - -The engine supports 3D spatialization of sounds. By default sounds will have spatialization -enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways -to disable spatialization of a sound: - - ```c - // Disable spatialization at initialization time via a flag: - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound); - - // Dynamically disable or enable spatialization post-initialization: - ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled); - ``` - -By default sounds will be spatialized based on the closest listener. If a sound should always be -spatialized relative to a specific listener it can be pinned to one: - - ```c - ma_sound_set_pinned_listener_index(&sound, listenerIndex); - ``` - -Like listeners, sounds have a position. By default, the position of a sound is in absolute space, -but it can be changed to be relative to a listener: - - ```c - ma_sound_set_positioning(&sound, ma_positioning_relative); - ``` - -Note that relative positioning of a sound only makes sense if there is either only one listener, or -the sound is pinned to a specific listener. To set the position of a sound: - - ```c - ma_sound_set_position(&sound, posX, posY, posZ); - ``` - -The direction works the same way as a listener and represents the sound's forward direction: - - ```c - ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ); - ``` - -Sound's also have a cone for controlling directional attenuation. This works exactly the same as -listeners: - - ```c - ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain); - ``` - -The velocity of a sound is used for doppler effect and can be set as such: - - ```c - ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ); - ``` - -The engine supports different attenuation models which can be configured on a per-sound basis. By -default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to -OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so: - - ```c - ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse); - ``` - -The supported attenuation models include the following: - - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_none | No distance attenuation. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_linear | Linear attenuation. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_exponential | Exponential attenuation. | - +----------------------------------+----------------------------------------------+ - -To control how quickly a sound rolls off as it moves away from the listener, you need to configure -the rolloff: - - ```c - ma_sound_set_rolloff(&sound, rolloff); - ``` - -You can control the minimum and maximum gain to apply from spatialization: - - ```c - ma_sound_set_min_gain(&sound, minGain); - ma_sound_set_max_gain(&sound, maxGain); - ``` - -Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for -the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain -volume after the listener moves further away and to have sounds play a maximum volume when the -listener is within a certain distance: - - ```c - ma_sound_set_min_distance(&sound, minDistance); - ma_sound_set_max_distance(&sound, maxDistance); - ``` - -The engine's spatialization system supports doppler effect. The doppler factor can be configure on -a per-sound basis like so: - - ```c - ma_sound_set_doppler_factor(&sound, dopplerFactor); - ``` - -You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and -`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the -starting volume: - - ```c - // Fade in over 1 second. - ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000); - - // ... sometime later ... - - // Fade out over 1 second, starting from the current volume. - ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000); - ``` - -By default sounds will start immediately, but sometimes for timing and synchronization purposes it -can be useful to schedule a sound to start or stop: - - ```c - // Start the sound in 1 second from now. - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); - - // Stop the sound in 2 seconds from now. - ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); - ``` - -Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before -anything will play. - -The time is specified in global time which is controlled by the engine. You can get the engine's -current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as -audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be -resynchronized for some reason. - -To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will -take the scheduled start and stop times into account. - -Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not -be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. - -Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping -sound this should never return true. - -Internally a sound wraps around a data source. Some APIs exist to control the underlying data -source, mainly for convenience: - - ```c - ma_sound_seek_to_pcm_frame(&sound, frameIndex); - ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity); - ma_sound_get_cursor_in_pcm_frames(&sound, &cursor); - ma_sound_get_length_in_pcm_frames(&sound, &length); - ``` - -Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do -not have any notion of a data source, anything relating to a data source is unavailable. - -Internally, sound data is loaded via the `ma_decoder` API which means by default in only supports -file formats that have built-in support in miniaudio. You can extend this to support any kind of -file format through the use of custom decoders. To do this you'll need to use a self-managed -resource manager and configure it appropriately. See the "Resource Management" section below for -details on how to set this up. - - -6. Resource Management -====================== -Many programs will want to manage sound resources for things such as reference counting and -streaming. This is supported by miniaudio via the `ma_resource_manager` API. - -The resource manager is mainly responsible for the following: - - * Loading of sound files into memory with reference counting. - * Streaming of sound data - -When loading a sound file, the resource manager will give you back a `ma_data_source` compatible -object called `ma_resource_manager_data_source`. This object can be passed into any -`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you -specify whether or not you want the sound to be fully loaded into memory (and optionally -pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want -the data to be loaded asynchronously. - -The example below is how you can initialize a resource manager using it's default configuration: - - ```c - ma_resource_manager_config config; - ma_resource_manager resourceManager; - - config = ma_resource_manager_config_init(); - result = ma_resource_manager_init(&config, &resourceManager); - if (result != MA_SUCCESS) { - ma_device_uninit(&device); - printf("Failed to initialize the resource manager."); - return -1; - } - ``` - -You can configure the format, channels and sample rate of the decoded audio data. By default it -will use the file's native data format, but you can configure it to use a consistent format. This -is useful for offloading the cost of data conversion to load time rather than dynamically -converting at mixing time. To do this, you configure the decoded format, channels and sample rate -like the code below: - - ```c - config = ma_resource_manager_config_init(); - config.decodedFormat = device.playback.format; - config.decodedChannels = device.playback.channels; - config.decodedSampleRate = device.sampleRate; - ``` - -In the code above, the resource manager will be configured so that any decoded audio data will be -pre-converted at load time to the device's native data format. If instead you used defaults and -the data format of the file did not match the device's data format, you would need to convert the -data at mixing time which may be prohibitive in high-performance and large scale scenarios like -games. - -Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it -only supports decoders that are built into miniaudio. It's possible to support additional encoding -formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable` -vtables into the resource manager config: - - ```c - ma_decoding_backend_vtable* pCustomBackendVTables[] = - { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus - }; - - ... - - resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; - resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); - resourceManagerConfig.pCustomDecodingBackendUserData = NULL; - ``` - -This system can allow you to support any kind of file format. See the "Decoding" section for -details on how to implement custom decoders. The miniaudio repository includes examples for Opus -via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. - -Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the -decoding of a page, a job will be posted to a queue which will then be processed by a job thread. -By default there will be only one job thread running, but this can be configured, like so: - - ```c - config = ma_resource_manager_config_init(); - config.jobThreadCount = MY_JOB_THREAD_COUNT; - ``` - -By default job threads are managed internally by the resource manager, however you can also self -manage your job threads if, for example, you want to integrate the job processing into your -existing job infrastructure, or if you simply don't like the way the resource manager does it. To -do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first -need to retrieve a job using `ma_resource_manager_next_job()` and then process it using -`ma_job_process()`: - - ```c - config = ma_resource_manager_config_init(); - config.jobThreadCount = 0; // Don't manage any job threads internally. - config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking. - - // ... Initialize your custom job threads ... - - void my_custom_job_thread(...) - { - for (;;) { - ma_job job; - ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); - if (result != MA_SUCCESS) { - if (result == MA_NOT_DATA_AVAILABLE) { - // No jobs are available. Keep going. Will only get this if the resource manager was initialized - // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. - continue; - } else if (result == MA_CANCELLED) { - // MA_JOB_TYPE_QUIT was posted. Exit. - break; - } else { - // Some other error occurred. - break; - } - } - - ma_job_process(&job); - } - } - ``` - -In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination -indicator, but you can use whatever you would like to terminate the thread. The call to -`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking -by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration -flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This -is to give every thread the opportunity to catch the event and terminate naturally. - -When loading a file, it's sometimes convenient to be able to customize how files are opened and -read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by -default. This can be done by setting `pVFS` member of the resource manager's config: - - ```c - // Initialize your custom VFS object. See documentation for VFS for information on how to do this. - my_custom_vfs vfs = my_custom_vfs_init(); - - config = ma_resource_manager_config_init(); - config.pVFS = &vfs; - ``` - -This is particularly useful in programs like games where you want to read straight from an archive -rather than the normal file system. If you do not specify a custom VFS, the resource manager will -use the operating system's normal file operations. This is default. - -To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When -loading a sound you need to specify the file path and options for how the sounds should be loaded. -By default a sound will be loaded synchronously. The returned data source is owned by the caller -which means the caller is responsible for the allocation and freeing of the data source. Below is -an example for initializing a data source: - - ```c - ma_resource_manager_data_source dataSource; - ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource); - if (result != MA_SUCCESS) { - // Error. - } - - // ... - - // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call - // the `ma_data_source_read_pcm_frames()` like you would with any normal data source. - result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead); - if (result != MA_SUCCESS) { - // Failed to read PCM frames. - } - - // ... - - ma_resource_manager_data_source_uninit(pResourceManager, &dataSource); - ``` - -The `flags` parameter specifies how you want to perform loading of the sound file. It can be a -combination of the following flags: - - ``` - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT - ``` - -When no flags are specified (set to 0), the sound will be fully loaded into memory, but not -decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when -`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in -memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will -be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after -the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You -can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag. -This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be -returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is -available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by -`ma_data_source_read_pcm_frames()`. - -For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you -can instead stream audio data which you can do by specifying the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1 -second pages. When a new page needs to be decoded, a job will be posted to the job queue and then -subsequently processed in a job thread. - -For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means -multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in -the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be -matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful -for a program to register self-managed raw audio data and associate it with a file path. Use the -`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this. -`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed -decoded audio data in the specified data format with the specified name. Likewise, -`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed -encoded audio data (the raw file data) with the specified name. Note that these names need not be -actual file paths. When `ma_resource_manager_data_source_init()` is called (without the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these -explicitly registered data buffers and, if found, will use it as the backing data for the data -source. Note that the resource manager does *not* make a copy of this data so it is up to the -caller to ensure the pointer stays valid for it's lifetime. Use -`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use -`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and -unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` -flag with a self-managed data pointer. - - -6.1. Asynchronous Loading and Synchronization ---------------------------------------------- -When loading asynchronously, it can be useful to poll whether or not loading has finished. Use -`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will -return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, -`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed -to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been -decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` -will be returned. Otherwise, some other error code will be returned if the sound failed to load. - -In addition to polling, you can also use a simple synchronization object called a "fence" to wait -for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a -fence is that it can be used to wait for a group of sounds to finish loading rather than waiting -for sounds on an individual basis. There are two stages to loading a sound: - - * Initialization of the internal decoder; and - * Completion of decoding of the file (the file is fully decoded) - -You can specify separate fences for each of the different stages. Waiting for the initialization -of the internal decoder is important for when you need to know the sample format, channels and -sample rate of the file. - -The example below shows how you could use a fence when loading a number of sounds: - - ```c - // This fence will be released when all sounds are finished loading entirely. - ma_fence fence; - ma_fence_init(&fence); - - // This will be passed into the initialization routine for each sound. - ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pFence = &fence; - - // Now load a bunch of sounds: - for (iSound = 0; iSound < soundCount; iSound += 1) { - ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]); - } - - // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ... - - // Wait for loading of sounds to finish. - ma_fence_wait(&fence); - ``` - -In the example above we used a fence for waiting until the entire file has been fully decoded. If -you only need to wait for the initialization of the internal decoder to complete, you can use the -`init` member of the `ma_resource_manager_pipeline_notifications` object: - - ```c - notifications.init.pFence = &fence; - ``` - -If a fence is not appropriate for your situation, you can instead use a callback that is fired on -an individual sound basis. This is done in a very similar way to fences: - - ```c - typedef struct - { - ma_async_notification_callbacks cb; - void* pMyData; - } my_notification; - - void my_notification_callback(ma_async_notification* pNotification) - { - my_notification* pMyNotification = (my_notification*)pNotification; - - // Do something in response to the sound finishing loading. - } - - ... - - my_notification myCallback; - myCallback.cb.onSignal = my_notification_callback; - myCallback.pMyData = pMyData; - - ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pNotification = &myCallback; - - ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound); - ``` - -In the example above we just extend the `ma_async_notification_callbacks` object and pass an -instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with -the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same -time and they should both work as expected. If using the `pNotification` system, you need to ensure -your `ma_async_notification_callbacks` object stays valid. - - - -6.2. Resource Manager Implementation Details --------------------------------------------- -Resources are managed in two main ways: - - * By storing the entire sound inside an in-memory buffer (referred to as a data buffer) - * By streaming audio data on the fly (referred to as a data stream) - -A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or -data stream, depending on whether or not the data source was initialized with the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a -`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer` -object. Both of these objects are data sources which means they can be used with any -`ma_data_source_*()` API. - -Another major feature of the resource manager is the ability to asynchronously decode audio files. -This relieves the audio thread of time-consuming decoding which can negatively affect scalability -due to the audio thread needing to complete it's work extremely quickly to avoid glitching. -Asynchronous decoding is achieved through a job system. There is a central multi-producer, -multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is -posted to the queue which is then read by a job thread. The number of job threads can be -configured for improved scalability, and job threads can all run in parallel without needing to -worry about the order of execution (how this is achieved is explained below). - -When a sound is being loaded asynchronously, playback can begin before the sound has been fully -decoded. This enables the application to start playback of the sound quickly, while at the same -time allowing to resource manager to keep loading in the background. Since there may be less -threads than the number of sounds being loaded at a given time, a simple scheduling system is used -to keep decoding time balanced and fair. The resource manager solves this by splitting decoding -into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a -new job will be posted to start decoding the next page. By dividing up decoding into pages, an -individual sound shouldn't ever delay every other sound from having their first page decoded. Of -course, when loading many sounds at the same time, there will always be an amount of time required -to process jobs in the queue so in heavy load situations there will still be some delay. To -determine if a data source is ready to have some frames read, use -`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames -available starting from the current position. - - -6.2.1. Job Queue ----------------- -The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity. -This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety. -Only a fixed number of jobs can be allocated and inserted into the queue which is done through a -lock-free data structure for allocating an index into a fixed sized array, with reference counting -for mitigation of the ABA problem. The reference count is 32-bit. - -For many types of jobs it's important that they execute in a specific order. In these cases, jobs -are executed serially. For the resource manager, serial execution of jobs is only required on a -per-object basis (per data buffer or per data stream). Each of these objects stores an execution -counter. When a job is posted it is associated with an execution counter. When the job is -processed, it checks if the execution counter of the job equals the execution counter of the -owning object and if so, processes the job. If the counters are not equal, the job will be posted -back onto the job queue for later processing. When the job finishes processing the execution order -of the main object is incremented. This system means the no matter how many job threads are -executing, decoding of an individual sound will always get processed serially. The advantage to -having multiple threads comes into play when loading multiple sounds at the same time. - -The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve -thread-safety for a very small section of code. This is only relevant when the resource manager -uses more than one job thread. If only using a single job thread, which is the default, the -lock should never actually wait in practice. The amount of time spent locking should be quite -short, but it's something to be aware of for those who have pedantic lock-free requirements and -need to use more than one job thread. There are plans to remove this lock in a future version. - -In addition, posting a job will release a semaphore, which on Win32 is implemented with -`ReleaseSemaphore` and on POSIX platforms via a condition variable: - - ```c - pthread_mutex_lock(&pSemaphore->lock); - { - pSemaphore->value += 1; - pthread_cond_signal(&pSemaphore->cond); - } - pthread_mutex_unlock(&pSemaphore->lock); - ``` - -Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid -this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING` -flag) and implement your own job processing routine (see the "Resource Manager" section above for -details on how to do this). - - - -6.2.2. Data Buffers -------------------- -When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the -resource manager will try to load the data into an in-memory data buffer. Before doing so, however, -it will first check if the specified file is already loaded. If so, it will increment a reference -counter and just use the already loaded data. This saves both time and memory. When the data buffer -is uninitialized, the reference counter will be decremented. If the counter hits zero, the file -will be unloaded. This is a detail to keep in mind because it could result in excessive loading and -unloading of a sound. For example, the following sequence will result in a file be loaded twice, -once after the other: - - ```c - ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. - ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded. - - ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. - ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded. - ``` - -A binary search tree (BST) is used for storing data buffers as it has good balance between -efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed -into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves -memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST -due to the random nature of the hash. The disadvantage is that file names are case-sensitive. If -this is an issue, you should normalize your file names to upper- or lower-case before initializing -your data sources. - -When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` -flag is excluded, the file will be decoded synchronously by the calling thread. There are two -options for controlling how the audio is stored in the data buffer - encoded or decoded. When the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored -in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is -a very simple and standard process of simply adding an item to the BST, allocating a block of -memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified). - -When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer -is done asynchronously. In this case, a job is posted to the queue to start loading and then the -function immediately returns, setting an internal result code to `MA_BUSY`. This result code is -returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully -completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed. - -When loading asynchronously, a single job is posted to the queue of the type -`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and -associating it with job. When the job is processed by the job thread, it will first load the file -using the VFS associated with the resource manager. When using a custom VFS, it's important that it -be completely thread-safe because it will be used from one or more job threads at the same time. -Individual files should only ever be accessed by one thread at a time, however. After opening the -file via the VFS, the job will determine whether or not the file is being decoded. If not, it -simply allocates a block of memory and loads the raw file contents into it and returns. On the -other hand, when the file is being decoded, it will first allocate a decoder on the heap and -initialize it. Then it will check if the length of the file is known. If so it will allocate a -block of memory to store the decoded output and initialize it to silence. If the size is unknown, -it will allocate room for one page. After memory has been allocated, the first page will be -decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the -completion event will be signalled and loading is now complete. If, however, there is more to -decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job -will decode the next page and perform the same process if it reaches the end. If there is more to -decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will -keep on happening until the sound has been fully decoded. For sounds of an unknown length, each -page will be linked together as a linked list. Internally this is implemented via the -`ma_paged_audio_buffer` object. - - -6.2.3. Data Streams -------------------- -Data streams only ever store two pages worth of data for each instance. They are most useful for -large sounds like music tracks in games that would consume too much memory if fully decoded in -memory. After every frame from a page has been read, a job will be posted to load the next page -which is done from the VFS. - -For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or -not initialization of the data source waits until the two pages have been decoded. When unset, -`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise -it will return immediately. - -When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, -`MA_BUSY` will be returned if there are no frames available. If there are some frames available, -but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames -read will be less than the number requested. Due to the asynchronous nature of data streams, -seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be -returned when trying to read frames. - -When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed -a job is posted to load the next page. This will be posted from the same thread that called -`ma_resource_manager_data_source_read_pcm_frames()`. - -Data streams are uninitialized by posting a job to the queue, but the function won't return until -that job has been processed. The reason for this is that the caller owns the data stream object and -therefore miniaudio needs to ensure everything completes before handing back control to the caller. -Also, if the data stream is uninitialized while pages are in the middle of decoding, they must -complete before destroying any underlying object and the job system handles this cleanly. - -Note that when a new page needs to be loaded, a job will be posted to the resource manager's job -thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue" -section above regarding locking when posting an event if you require a strictly lock-free audio -thread. - - - -7. Node Graph -============= -miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a -node whose outputs are attached to inputs of another node, thereby creating a graph. There are -different types of nodes, with each node in the graph processing input data to produce output, -which is then fed through the chain. Each node in the graph can apply their own custom effects. At -the start of the graph will usually be one or more data source nodes which have no inputs, but -instead pull their data from a data source. At the end of the graph is an endpoint which represents -the end of the chain and is where the final output is ultimately extracted from. - -Each node has a number of input buses and a number of output buses. An output bus from a node is -attached to an input bus of another. Multiple nodes can connect their output buses to another -node's input bus, in which case their outputs will be mixed before processing by the node. Below is -a diagram that illustrates a hypothetical node graph setup: - - ``` - >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - +---------------+ +-----------------+ - | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ - +---------------+ | | =----+ +-----------------+ | +----------+ - +----= Splitter | +----= ENDPOINT | - +---------------+ | | =----+ +-----------------+ | +----------+ - | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ - +---------------+ +-----------------+ - ``` - -In the above graph, it starts with two data sources whose outputs are attached to the input of a -splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter -performs it's processing routine and produces two outputs which is simply a duplication of the -input stream. One output is attached to a low pass filter, whereas the other output is attached to -a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and -since they're both connected to the same input but, they'll be mixed. - -Each input bus must be configured to accept the same number of channels, but the number of channels -used by input buses can be different to the number of channels for output buses in which case -miniaudio will automatically convert the input data to the output channel count before processing. -The number of channels of an output bus of one node must match the channel count of the input bus -it's attached to. The channel counts cannot be changed after the node has been initialized. If you -attempt to attach an output bus to an input bus with a different channel count, attachment will -fail. - -To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a -container around the entire graph. The `ma_node_graph` object is required for some thread-safety -issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's -standard config/init system: - - ```c - ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount); - - result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks. - if (result != MA_SUCCESS) { - // Failed to initialize node graph. - } - ``` - -When you initialize the node graph, you're specifying the channel count of the endpoint. The -endpoint is a special node which has one input bus and one output bus, both of which have the -same channel count, which is specified in the config. Any nodes that connect directly to the -endpoint must be configured such that their output buses have the same channel count. When you read -audio data from the node graph, it'll have the channel count you specified in the config. To read -data from the graph: - - ```c - ma_uint32 framesRead; - result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - // Failed to read data from the node graph. - } - ``` - -When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in -data from it's input attachments, which in turn recusively pull in data from their inputs, and so -on. At the start of the graph there will be some kind of data source node which will have zero -inputs and will instead read directly from a data source. The base nodes don't literally need to -read from a `ma_data_source` object, but they will always have some kind of underlying object that -sources some kind of audio. The `ma_data_source_node` node can be used to read from a -`ma_data_source`. Data is always in floating-point format and in the number of channels you -specified when the graph was initialized. The sample rate is defined by the underlying data sources. -It's up to you to ensure they use a consistent and appropraite sample rate. - -The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but -miniaudio includes a few stock nodes for common functionality. This is how you would initialize a -node which reads directly from a data source (`ma_data_source_node`) which is an example of one -of the stock nodes that comes with miniaudio: - - ```c - ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource); - - ma_data_source_node dataSourceNode; - result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode); - if (result != MA_SUCCESS) { - // Failed to create data source node. - } - ``` - -The data source node will use the output channel count to determine the channel count of the output -bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data -source). The data source must output to floating-point (`ma_format_f32`) or else an error will be -returned from `ma_data_source_node_init()`. - -By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`: - - ```c - result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); - if (result != MA_SUCCESS) { - // Failed to attach node. - } - ``` - -The code above connects the data source node directly to the endpoint. Since the data source node -has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single -input bus which means the input bus index will also always be 0. - -To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use -`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to -another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll -deal with it for you. - -Less frequently you may want to create a specialized node. This will be a node where you implement -your own processing callback to apply a custom effect of some kind. This is similar to initalizing -one of the stock node types, only this time you need to specify a pointer to a vtable containing a -pointer to the processing function and the number of input and output buses. Example: - - ```c - static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) - { - // Do some processing of ppFramesIn (one stream of audio data per input bus) - const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0. - const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1. - float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0. - - // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each - // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers - // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames - // your node consumed and `pFrameCountOut` should be set the number of output frames that - // were produced. - // - // You should process as many frames as you can. If your effect consumes input frames at the - // same rate as output frames (always the case, unless you're doing resampling), you need - // only look at `ppFramesOut` and process that exact number of frames. If you're doing - // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut` - // properly. - } - - static ma_node_vtable my_custom_node_vtable = - { - my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. - NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. - 2, // 2 input buses. - 1, // 1 output bus. - 0 // Default flags. - }; - - ... - - // Each bus needs to have a channel count specified. To do this you need to specify the channel - // counts in an array and then pass that into the node config. - ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. - ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specicied in the vtable. - - inputChannels[0] = channelsIn; - inputChannels[1] = channelsIn; - outputChannels[0] = channelsOut; - - ma_node_config nodeConfig = ma_node_config_init(); - nodeConfig.vtable = &my_custom_node_vtable; - nodeConfig.pInputChannels = inputChannels; - nodeConfig.pOutputChannels = outputChannels; - - ma_node_base node; - result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node); - if (result != MA_SUCCESS) { - // Failed to initialize node. - } - ``` - -When initializing a custom node, as in the code above, you'll normally just place your vtable in -static space. The number of input and output buses are specified as part of the vtable. If you need -a variable number of buses on a per-node bases, the vtable should have the relevant bus count set -to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config: - - ```c - static ma_node_vtable my_custom_node_vtable = - { - my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. - NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. - MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis. - 1, // 1 output bus. - 0 // Default flags. - }; - - ... - - ma_node_config nodeConfig = ma_node_config_init(); - nodeConfig.vtable = &my_custom_node_vtable; - nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here. - nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array. - nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array. - ``` - -In the above example it's important to never set the `inputBusCount` and `outputBusCount` members -to anything other than their defaults if the vtable specifies an explicit count. They can only be -set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count. - -Most often you'll want to create a structure to encapsulate your node with some extra data. You -need to make sure the `ma_node_base` object is your first member of the structure: - - ```c - typedef struct - { - ma_node_base base; // <-- Make sure this is always the first member. - float someCustomData; - } my_custom_node; - ``` - -By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the -graph just like any other node. - -In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the -number of channels for each bus is what was specified by the config when the node was initialized -with `ma_node_init()`. In addition, all attachments to each of the input buses will have been -pre-mixed by miniaudio. The config allows you to specify different channel counts for each -individual input and output bus. It's up to the effect to handle it appropriate, and if it can't, -return an error in it's initialization routine. - -Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable -and include the following: - - +-----------------------------------------+---------------------------------------------------+ - | Flag Name | Description | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio | - | | processing, but are instead used for tracking | - | | time, handling events, etc. Also used by the | - | | internal endpoint node. It reads directly from | - | | the input bus to the output bus. Nodes with this | - | | flag must have exactly 1 input bus and 1 output | - | | bus, and both buses must have the same channel | - | | counts. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | - | | when no data is available to be read from input | - | | attachments. This is useful for effects like | - | | echos where there will be a tail of audio data | - | | that still needs to be processed even when the | - | | original data sources have reached their ends. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | - | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | - | | is set, the `ppFramesIn` parameter of the | - | | processing callback will be set to NULL when | - | | there are no input frames are available. When | - | | this is unset, silence will be posted to the | - | | processing callback. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output | - | | frames are processed at different rates. You | - | | should set this for any nodes that perform | - | | resampling. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only | - | | silent output. This is useful for nodes where you | - | | don't want the output to contribute to the final | - | | mix. An example might be if you want split your | - | | stream and have one branch be output to a file. | - | | When using this flag, you should avoid writing to | - | | the output buffer of the node's processing | - | | callback because miniaudio will ignore it anyway. | - +-----------------------------------------+---------------------------------------------------+ - - -If you need to make a copy of an audio stream for effect processing you can use a splitter node -called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses. -You can use it like this: - - ```c - ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut); - - ma_splitter_node splitterNode; - result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); - if (result != MA_SUCCESS) { - // Failed to create node. - } - - // Attach your output buses to two different input buses (can be on two different nodes). - ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint. - ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node. - ``` - -The volume of an output bus can be configured on a per-bus basis: - - ```c - ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f); - ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f); - ``` - -In the code above we're using the splitter node from before and changing the volume of each of the -copied streams. - -You can start and stop a node with the following: - - ```c - ma_node_set_state(&splitterNode, ma_node_state_started); // The default state. - ma_node_set_state(&splitterNode, ma_node_state_stopped); - ``` - -By default the node is in a started state, but since it won't be connected to anything won't -actually be invoked by the node graph until it's connected. When you stop a node, data will not be -read from any of it's input connections. You can use this property to stop a group of sounds -atomically. - -You can configure the initial state of a node in it's config: - - ```c - nodeConfig.initialState = ma_node_state_stopped; - ``` - -Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member -which is the config to use with the base node. This is where the initial state can be configured -for specialized nodes: - - ```c - dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped; - ``` - -When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not -modify the `vtable` member of the `nodeConfig` object. - - -7.1. Timing ------------ -The node graph supports starting and stopping nodes at scheduled times. This is especially useful -for data source nodes where you want to get the node set up, but only start playback at a specific -time. There are two clocks: local and global. - -A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can -only be done based on the global clock because the local clock will not be running while the node -is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the -other hand, the local clock only advances when the node's processing callback is fired, and is -advanced based on the output frame count. - -To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with -`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline. -Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time, -and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the -audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these -outside of the node processing callbacks which are always run on the audio thread. - -There is basic support for scheduling the starting and stopping of nodes. You can only schedule one -start and one stop at a time. This is mainly intended for putting nodes into a started or stopped -state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited -to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks -of several milliseconds. The following APIs can be used for scheduling node states: - - ```c - ma_node_set_state_time() - ma_node_get_state_time() - ``` - -The time is absolute and must be based on the global clock. An example is below: - - ```c - ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second. - ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds. - ``` - -An example for changing the state using a relative time. - - ```c - ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph)); - ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph)); - ``` - -Note that due to the nature of multi-threading the times may not be 100% exact. If this is an -issue, consider scheduling state changes from within a processing callback. An idea might be to -have some kind of passthrough trigger node that is used specifically for tracking time and handling -events. - - - -7.2. Thread Safety and Locking ------------------------------- -When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's -expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so -without the use of any locks. This section discusses the implementation used by miniaudio and goes -over some of the compromises employed by miniaudio to achieve this goal. Note that the current -implementation may not be ideal - feedback and critiques are most welcome. - -The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected -to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the -implementation, but are crafted in a way such that such locking is not required when reading audio -data from the graph. Locking in these areas are achieved by means of spinlocks. - -The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact -that a node can be uninitialized, and it's memory potentially freed, while in the middle of being -processed on the audio thread. There are times when the audio thread will be referencing a node, -which means the uninitialization process of a node needs to make sure it delays returning until the -audio thread is finished so that control is not handed back to the caller thereby giving them a -chance to free the node's memory. - -When the audio thread is processing a node, it does so by reading from each of the output buses of -the node. In order for a node to process data for one of it's output buses, it needs to read from -each of it's input buses, and so on an so forth. It follows that once all output buses of a node -are detached, the node as a whole will be disconnected and no further processing will occur unless -it's output buses are reattached, which won't be happening when the node is being uninitialized. -By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can -simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By -doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output -nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean -up. - -With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as -it takes to process the output bus being detached. This will happen if it's called at just the -wrong moment where the audio thread has just iterated it and has just started processing. The -caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which -includes the cost of recursively processing it's inputs. This is the biggest compromise made with -the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes -earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching -higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass -detachments, detach starting from the lowest level nodes and work your way towards the final -endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not -running, detachment will be fast and detachment in any order will be the same. The reason nodes -need to wait for their input attachments to complete is due to the potential for desyncs between -data sources. If the node was to terminate processing mid way through processing it's inputs, -there's a chance that some of the underlying data sources will have been read, but then others not. -That will then result in a potential desynchronization when detaching and reattaching higher-level -nodes. A possible solution to this is to have an option when detaching to terminate processing -before processing all input attachments which should be fairly simple. - -Another compromise, albeit less significant, is locking when attaching and detaching nodes. This -locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present -for each input bus and output bus. When an output bus is connected to an input bus, both the output -bus and input bus is locked. This locking is specifically for attaching and detaching across -different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and -unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when -considering that iterating over attachments must not break as a result of attaching or detaching a -node while iteration is occuring. - -Attaching and detaching are both quite simple. When an output bus of a node is attached to an input -bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where -each item in the list is and output bus. We have some intentional (and convenient) restrictions on -what can done with the linked list in order to simplify the implementation. First of all, whenever -something needs to iterate over the list, it must do so in a forward direction. Backwards iteration -is not supported. Also, items can only be added to the start of the list. - -The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer -to the next item in the list, and another to the previous item. A pointer to the previous item is -only required for fast detachment of the node - it is never used in iteration. This is an -important property because it means from the perspective of iteration, attaching and detaching of -an item can be done with a single atomic assignment. This is exploited by both the attachment and -detachment process. When attaching the node, the first thing that is done is the setting of the -local "next" and "previous" pointers of the node. After that, the item is "attached" to the list -by simply performing an atomic exchange with the head pointer. After that, the node is "attached" -to the list from the perspective of iteration. Even though the "previous" pointer of the next item -hasn't yet been set, from the perspective of iteration it's been attached because iteration will -only be happening in a forward direction which means the "previous" pointer won't actually ever get -used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and -`ma_node_detach_output_bus()` for the implementation of this mechanism. - - - -8. Decoding -=========== -The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from -devices and can be used independently. The following formats are supported: - - +---------+------------------+----------+ - | Format | Decoding Backend | Built-In | - +---------+------------------+----------+ - | WAV | dr_wav | Yes | - | MP3 | dr_mp3 | Yes | - | FLAC | dr_flac | Yes | - | Vorbis | stb_vorbis | No | - +---------+------------------+----------+ - -Vorbis is supported via stb_vorbis which can be enabled by including the header section before the -implementation of miniaudio, like the following: - - ```c - #define STB_VORBIS_HEADER_ONLY - #include "extras/stb_vorbis.c" // Enables Vorbis decoding. - - #define MINIAUDIO_IMPLEMENTATION - #include "miniaudio.h" - - // The stb_vorbis implementation must come after the implementation of miniaudio. - #undef STB_VORBIS_HEADER_ONLY - #include "extras/stb_vorbis.c" - ``` - -A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio). - -Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the -built-in decoders by specifying one or more of the following options before the miniaudio -implementation: - - ```c - #define MA_NO_WAV - #define MA_NO_MP3 - #define MA_NO_FLAC - ``` - -Disabling built-in decoding libraries is useful if you use these libraries independantly of the -`ma_decoder` API. - -A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with -`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is -an example for loading a decoder from a file: - - ```c - ma_decoder decoder; - ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder); - if (result != MA_SUCCESS) { - return false; // An error occurred. - } - - ... - - ma_decoder_uninit(&decoder); - ``` - -When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object -(the `NULL` argument in the example above) which allows you to configure the output format, channel -count, sample rate and channel map: - - ```c - ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); - ``` - -When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the -same as that defined by the decoding backend. - -Data is read from the decoder as PCM frames. This will output the number of PCM frames actually -read. If this is less than the requested number of PCM frames it means you've reached the end. The -return value will be `MA_AT_END` if no samples have been read and the end has been reached. - - ```c - ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead); - if (framesRead < framesToRead) { - // Reached the end. - } - ``` - -You can also seek to a specific frame like so: - - ```c - ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame); - if (result != MA_SUCCESS) { - return false; // An error occurred. - } - ``` - -If you want to loop back to the start, you can simply seek back to the first PCM frame: - - ```c - ma_decoder_seek_to_pcm_frame(pDecoder, 0); - ``` - -When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding -backend. This can be unnecessarily inefficient if the type is already known. In this case you can -use `encodingFormat` variable in the device config to specify a specific encoding format you want -to decode: - - ```c - decoderConfig.encodingFormat = ma_encoding_format_wav; - ``` - -See the `ma_encoding_format` enum for possible encoding formats. - -The `ma_decoder_init_file()` API will try using the file extension to determine which decoding -backend to prefer. - - -8.1. Custom Decoders --------------------- -It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful -when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of -the stock formats supported by miniaudio. This can be put to particularly good use when using the -`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for -example, you wanted to support Opus, you can do so with a custom decoder (there if a reference -Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile). - -A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs -to be implemented which is then passed into the decoder config: - - ```c - ma_decoding_backend_vtable* pCustomBackendVTables[] = - { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus - }; - - ... - - decoderConfig = ma_decoder_config_init_default(); - decoderConfig.pCustomBackendUserData = NULL; - decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; - decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); - ``` - -The `ma_decoding_backend_vtable` vtable has the following functions: - - ``` - onInit - onInitFile - onInitFileW - onInitMemory - onUninit - ``` - -There are only two functions that must be implemented - `onInit` and `onUninit`. The other -functions can be implemented for a small optimization for loading from a file path or memory. If -these are not specified, miniaudio will deal with it for you via a generic implementation. - -When you initialize a custom data source (by implementing the `onInit` function in the vtable) you -will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the -section about data sources for details on how to implemen this. Alternatively, see the -"custom_decoders" example in the miniaudio repository. - -The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data -from some abitrary source. You'll use these functions to read from the raw data and perform the -decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant -parameter. - -The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only -used as a hint and can be ignored. However, if any of the properties are relevant to your decoder, -an optimal implementation will handle the relevant properties appropriately. - -If memory allocation is required, it should be done so via the specified allocation callbacks if -possible (the `pAllocationCallbacks` parameter). - -If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to -NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned. -When multiple custom backends are specified, miniaudio will cycle through the vtables in the order -they're listed in the array that's passed into the decoder config so it's important that your -initialization routine is clean. - -When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an -opportunity to clean up and internal data. - - - -9. Encoding -=========== -The `ma_encoding` API is used for writing audio files. The only supported output format is WAV -which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio. -This can be disabled by specifying the following option before the implementation of miniaudio: - - ```c - #define MA_NO_WAV - ``` - -An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data -delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder -to output to a file. - - ```c - ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); - ma_encoder encoder; - ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder); - if (result != MA_SUCCESS) { - // Error - } - - ... - - ma_encoder_uninit(&encoder); - ``` - -When initializing an encoder you must specify a config which is initialized with -`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output -channel count and output sample rate. The following file types are supported: - - +------------------------+-------------+ - | Enum | Description | - +------------------------+-------------+ - | ma_encoding_format_wav | WAV | - +------------------------+-------------+ - -If the format, channel count or sample rate is not supported by the output file type an error will -be returned. The encoder will not perform data conversion so you will need to convert it before -outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the -example below: - - ```c - framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite); - ``` - -Encoders must be uninitialized with `ma_encoder_uninit()`. - - - -10. Data Conversion -=================== -A data conversion API is included with miniaudio which supports the majority of data conversion -requirements. This supports conversion between sample formats, channel counts (with channel -mapping) and sample rates. - - -10.1. Sample Format Conversion ------------------------------- -Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and -`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific -formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use -`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count -and channel count as a variable instead of the total sample count. - - -10.1.1. Dithering ------------------ -Dithering can be set using the ditherMode parameter. - -The different dithering modes include the following, in order of efficiency: - - +-----------+--------------------------+ - | Type | Enum Token | - +-----------+--------------------------+ - | None | ma_dither_mode_none | - | Rectangle | ma_dither_mode_rectangle | - | Triangle | ma_dither_mode_triangle | - +-----------+--------------------------+ - -Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be -ignored for conversions where dithering is not needed. Dithering is available for the following -conversions: - - ``` - s16 -> u8 - s24 -> u8 - s32 -> u8 - f32 -> u8 - s24 -> s16 - s32 -> s16 - f32 -> s16 - ``` - -Note that it is not an error to pass something other than ma_dither_mode_none for conversions where -dither is not used. It will just be ignored. - - - -10.2. Channel Conversion ------------------------- -Channel conversion is used for channel rearrangement and conversion from one channel count to -another. The `ma_channel_converter` API is used for channel conversion. Below is an example of -initializing a simple channel converter which converts from mono to stereo. - - ```c - ma_channel_converter_config config = ma_channel_converter_config_init( - ma_format, // Sample format - 1, // Input channels - NULL, // Input channel map - 2, // Output channels - NULL, // Output channel map - ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. - - result = ma_channel_converter_init(&config, NULL, &converter); - if (result != MA_SUCCESS) { - // Error. - } - ``` - -To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so: - - ```c - ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount); - if (result != MA_SUCCESS) { - // Error. - } - ``` - -It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM -frames. - -Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. - - -10.2.1. Channel Mapping ------------------------ -In addition to converting from one channel count to another, like the example above, the channel -converter can also be used to rearrange channels. When initializing the channel converter, you can -optionally pass in channel maps for both the input and output frames. If the channel counts are the -same, and each channel map contains the same channel positions with the exception that they're in -a different order, a simple shuffling of the channels will be performed. If, however, there is not -a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed -based on a mixing mode which is specified when initializing the `ma_channel_converter_config` -object. - -When converting from mono to multi-channel, the mono channel is simply copied to each output -channel. When going the other way around, the audio of each output channel is simply averaged and -copied to the mono channel. - -In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess -channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th -channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and -4th channels. - -The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a -simple distribution between input and output. Imagine sitting in the middle of a room, with -speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be -thought of as being in the corner of the front and left walls. - -Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined -weights. Custom weights can be passed in as the last parameter of -`ma_channel_converter_config_init()`. - -Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a -`ma_standard_channel_map` enum as it's first parameter, which can be one of the following: - - +-----------------------------------+-----------------------------------------------------------+ - | Name | Description | - +-----------------------------------+-----------------------------------------------------------+ - | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. | - | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. | - | ma_standard_channel_map_alsa | Default ALSA channel map. | - | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. | - | ma_standard_channel_map_flac | FLAC channel map. | - | ma_standard_channel_map_vorbis | Vorbis channel map. | - | ma_standard_channel_map_sound4 | FreeBSD's sound(4). | - | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. | - | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | - +-----------------------------------+-----------------------------------------------------------+ - -Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`): - - +---------------+---------------------------------+ - | Channel Count | Mapping | - +---------------+---------------------------------+ - | 1 (Mono) | 0: MA_CHANNEL_MONO | - +---------------+---------------------------------+ - | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT | - +---------------+---------------------------------+ - | 3 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER | - +---------------+---------------------------------+ - | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_BACK_CENTER | - +---------------+---------------------------------+ - | 5 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_BACK_LEFT
| - | | 4: MA_CHANNEL_BACK_RIGHT | - +---------------+---------------------------------+ - | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_SIDE_LEFT
| - | | 5: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | 7 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_BACK_CENTER
| - | | 4: MA_CHANNEL_SIDE_LEFT
| - | | 5: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_BACK_LEFT
| - | | 5: MA_CHANNEL_BACK_RIGHT
| - | | 6: MA_CHANNEL_SIDE_LEFT
| - | | 7: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | Other | All channels set to 0. This | - | | is equivalent to the same | - | | mapping as the device. | - +---------------+---------------------------------+ - - - -10.3. Resampling ----------------- -Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something -like the following: - - ```c - ma_resampler_config config = ma_resampler_config_init( - ma_format_s16, - channels, - sampleRateIn, - sampleRateOut, - ma_resample_algorithm_linear); - - ma_resampler resampler; - ma_result result = ma_resampler_init(&config, &resampler); - if (result != MA_SUCCESS) { - // An error occurred... - } - ``` - -Do the following to uninitialize the resampler: - - ```c - ma_resampler_uninit(&resampler); - ``` - -The following example shows how data can be processed - - ```c - ma_uint64 frameCountIn = 1000; - ma_uint64 frameCountOut = 2000; - ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); - if (result != MA_SUCCESS) { - // An error occurred... - } - - // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the - // number of output frames written. - ``` - -To initialize the resampler you first need to set up a config (`ma_resampler_config`) with -`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of -channels, the input and output sample rate, and the algorithm. - -The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format -you will need to perform pre- and post-conversions yourself where necessary. Note that the format -is the same for both input and output. The format cannot be changed after initialization. - -The resampler supports multiple channels and is always interleaved (both input and output). The -channel count cannot be changed after initialization. - -The sample rates can be anything other than zero, and are always specified in hertz. They should be -set to something like 44100, etc. The sample rate is the only configuration property that can be -changed after initialization. - -The miniaudio resampler has built-in support for the following algorithms: - - +-----------+------------------------------+ - | Algorithm | Enum Token | - +-----------+------------------------------+ - | Linear | ma_resample_algorithm_linear | - | Custom | ma_resample_algorithm_custom | - +-----------+------------------------------+ - -The algorithm cannot be changed after initialization. - -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. -De-interleaved processing is not supported. To process frames, use -`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you -can fit in the output buffer and the number of input frames contained in the input buffer. On -output these variables contain the number of output frames that were written to the output buffer -and the number of input frames that were consumed in the process. You can pass in NULL for the -input buffer in which case it will be treated as an infinitely large buffer of zeros. The output -buffer can also be NULL, in which case the processing will be treated as seek. - -The sample rate can be changed dynamically on the fly. You can change this with explicit sample -rates with `ma_resampler_set_rate()` and also with a decimal ratio with -`ma_resampler_set_rate_ratio()`. The ratio is in/out. - -Sometimes it's useful to know exactly how many input frames will be required to output a specific -number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`. -Likewise, it's sometimes useful to know exactly how many frames would be output given a certain -number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. - -Due to the nature of how resampling works, the resampler introduces some latency. This can be -retrieved in terms of both the input rate and the output rate with -`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. - - -10.3.1. Resampling Algorithms ------------------------------ -The choice of resampling algorithm depends on your situation and requirements. - - -10.3.1.1. Linear Resampling ---------------------------- -The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, -some control over the quality of the linear resampler which may make it a suitable option depending -on your requirements. - -The linear resampler performs low-pass filtering before or after downsampling or upsampling, -depending on the sample rates you're converting between. When decreasing the sample rate, the -low-pass filter will be applied before downsampling. When increasing the rate it will be performed -after upsampling. By default a fourth order low-pass filter will be applied. This can be configured -via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. - -The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of -the input and output sample rates (Nyquist Frequency). - -The API for the linear resampler is the same as the main resampler API, only it's called -`ma_linear_resampler`. - - -10.3.2. Custom Resamplers -------------------------- -You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling -algorithm and setting a vtable in the resampler config: - - ```c - ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom); - config.pBackendVTable = &g_customResamplerVTable; - ``` - -Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You -need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all -functions in the vtable need to be implemented, but if it's possible to implement, they should be. - -You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The -`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom -resampler will need to make given the supplied config. When you initialize the resampler via the -`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store -the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage -it for you. - -The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn` -points to a variable containing the number of frames in the `pFramesIn` buffer and -`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer. -On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed, -whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`. - -The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If -dynamic rate changes are not supported, you can set this callback to NULL. - -The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in -input and output rates respectively. These can be NULL in which case latency calculations will be -assumed to be NULL. - -The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input -frames are required to be available to produce the given number of output frames. Likewise, the -`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be -produced given the specified number of input frames. miniaudio will use these as a hint, but they -are optional and can be set to NULL if you're unable to implement them. - - - -10.4. General Data Conversion ------------------------------ -The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and -resampling into one operation. This is what miniaudio uses internally to convert between the format -requested when the device was initialized and the format of the backend's native device. The API -for general data conversion is very similar to the resampling API. Create a `ma_data_converter` -object like this: - - ```c - ma_data_converter_config config = ma_data_converter_config_init( - inputFormat, - outputFormat, - inputChannels, - outputChannels, - inputSampleRate, - outputSampleRate - ); - - ma_data_converter converter; - ma_result result = ma_data_converter_init(&config, NULL, &converter); - if (result != MA_SUCCESS) { - // An error occurred... - } - ``` - -In the example above we use `ma_data_converter_config_init()` to initialize the config, however -there's many more properties that can be configured, such as channel maps and resampling quality. -Something like the following may be more suitable depending on your requirements: - - ```c - ma_data_converter_config config = ma_data_converter_config_init_default(); - config.formatIn = inputFormat; - config.formatOut = outputFormat; - config.channelsIn = inputChannels; - config.channelsOut = outputChannels; - config.sampleRateIn = inputSampleRate; - config.sampleRateOut = outputSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn); - config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER; - ``` - -Do the following to uninitialize the data converter: - - ```c - ma_data_converter_uninit(&converter, NULL); - ``` - -The following example shows how data can be processed - - ```c - ma_uint64 frameCountIn = 1000; - ma_uint64 frameCountOut = 2000; - ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); - if (result != MA_SUCCESS) { - // An error occurred... - } - - // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number - // of output frames written. - ``` - -The data converter supports multiple channels and is always interleaved (both input and output). -The channel count cannot be changed after initialization. - -Sample rates can be anything other than zero, and are always specified in hertz. They should be set -to something like 44100, etc. The sample rate is the only configuration property that can be -changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of -`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use -`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. -The resampling algorithm cannot be changed after initialization. - -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. -De-interleaved processing is not supported. To process frames, use -`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames -you can fit in the output buffer and the number of input frames contained in the input buffer. On -output these variables contain the number of output frames that were written to the output buffer -and the number of input frames that were consumed in the process. You can pass in NULL for the -input buffer in which case it will be treated as an infinitely large -buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated -as seek. - -Sometimes it's useful to know exactly how many input frames will be required to output a specific -number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`. -Likewise, it's sometimes useful to know exactly how many frames would be output given a certain -number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. - -Due to the nature of how resampling works, the data converter introduces some latency if resampling -is required. This can be retrieved in terms of both the input rate and the output rate with -`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. - - - -11. Filtering -============= - -11.1. Biquad Filtering ----------------------- -Biquad filtering is achieved with the `ma_biquad` API. Example: - - ```c - ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - ma_result result = ma_biquad_init(&config, &biquad); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount); - ``` - -Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, -b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and -coefficients must not be pre-normalized. - -Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format -you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use -fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. - -Input and output frames are always interleaved. - -Filtering can be applied in-place by passing in the same pointer for both the input and output -buffers, like so: - - ```c - ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount); - ``` - -If you need to change the values of the coefficients, but maintain the values in the registers you -can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the -filter while keeping the values of registers valid to avoid glitching. Do not use -`ma_biquad_init()` for this as it will do a full initialization which involves clearing the -registers to 0. Note that changing the format or channel count after initialization is invalid and -will result in an error. - - -11.2. Low-Pass Filtering ------------------------- -Low-pass filtering is achieved with the following APIs: - - +---------+------------------------------------------+ - | API | Description | - +---------+------------------------------------------+ - | ma_lpf1 | First order low-pass filter | - | ma_lpf2 | Second order low-pass filter | - | ma_lpf | High order low-pass filter (Butterworth) | - +---------+------------------------------------------+ - -Low-pass filter example: - - ```c - ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - ma_result result = ma_lpf_init(&config, &lpf); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount); - ``` - -Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format -you need to convert it yourself beforehand. Input and output frames are always interleaved. - -Filtering can be applied in-place by passing in the same pointer for both the input and output -buffers, like so: - - ```c - ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); - ``` - -The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, -you can chain first and second order filters together. - - ```c - for (iFilter = 0; iFilter < filterCount; iFilter += 1) { - ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount); - } - ``` - -If you need to change the configuration of the filter, but need to maintain the state of internal -registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample -rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the -format or channel count after initialization is invalid and will result in an error. - -The `ma_lpf` object supports a configurable order, but if you only need a first order filter you -may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use -`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. - -If an even filter order is specified, a series of second order filters will be processed in a -chain. If an odd filter order is specified, a first order filter will be applied, followed by a -series of second order filters in a chain. - - -11.3. High-Pass Filtering -------------------------- -High-pass filtering is achieved with the following APIs: - - +---------+-------------------------------------------+ - | API | Description | - +---------+-------------------------------------------+ - | ma_hpf1 | First order high-pass filter | - | ma_hpf2 | Second order high-pass filter | - | ma_hpf | High order high-pass filter (Butterworth) | - +---------+-------------------------------------------+ - -High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, -`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage. - - -11.4. Band-Pass Filtering -------------------------- -Band-pass filtering is achieved with the following APIs: - - +---------+-------------------------------+ - | API | Description | - +---------+-------------------------------+ - | ma_bpf2 | Second order band-pass filter | - | ma_bpf | High order band-pass filter | - +---------+-------------------------------+ - -Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and -`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for -band-pass filters must be an even number which means there is no first order band-pass filter, -unlike low-pass and high-pass filters. - - -11.5. Notch Filtering ---------------------- -Notch filtering is achieved with the following APIs: - - +-----------+------------------------------------------+ - | API | Description | - +-----------+------------------------------------------+ - | ma_notch2 | Second order notching filter | - +-----------+------------------------------------------+ - - -11.6. Peaking EQ Filtering -------------------------- -Peaking filtering is achieved with the following APIs: - - +----------+------------------------------------------+ - | API | Description | - +----------+------------------------------------------+ - | ma_peak2 | Second order peaking filter | - +----------+------------------------------------------+ - - -11.7. Low Shelf Filtering -------------------------- -Low shelf filtering is achieved with the following APIs: - - +-------------+------------------------------------------+ - | API | Description | - +-------------+------------------------------------------+ - | ma_loshelf2 | Second order low shelf filter | - +-------------+------------------------------------------+ - -Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to -just turn them down rather than eliminate them entirely. - - -11.8. High Shelf Filtering --------------------------- -High shelf filtering is achieved with the following APIs: - - +-------------+------------------------------------------+ - | API | Description | - +-------------+------------------------------------------+ - | ma_hishelf2 | Second order high shelf filter | - +-------------+------------------------------------------+ - -The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` -instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies, -the high shelf filter does the same thing for high frequencies. - - - - -12. Waveform and Noise Generation -================================= - -12.1. Waveforms ---------------- -miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved -with the `ma_waveform` API. Example: - - ```c - ma_waveform_config config = ma_waveform_config_init( - FORMAT, - CHANNELS, - SAMPLE_RATE, - ma_waveform_type_sine, - amplitude, - frequency); - - ma_waveform waveform; - ma_result result = ma_waveform_init(&config, &waveform); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); - ``` - -The amplitude, frequency, type, and sample rate can be changed dynamically with -`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and -`ma_waveform_set_sample_rate()` respectively. - -You can invert the waveform by setting the amplitude to a negative value. You can use this to -control whether or not a sawtooth has a positive or negative ramp, for example. - -Below are the supported waveform types: - - +---------------------------+ - | Enum Name | - +---------------------------+ - | ma_waveform_type_sine | - | ma_waveform_type_square | - | ma_waveform_type_triangle | - | ma_waveform_type_sawtooth | - +---------------------------+ - - - -12.2. Noise ------------ -miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: - - ```c - ma_noise_config config = ma_noise_config_init( - FORMAT, - CHANNELS, - ma_noise_type_white, - SEED, - amplitude); - - ma_noise noise; - ma_result result = ma_noise_init(&config, &noise); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_noise_read_pcm_frames(&noise, pOutput, frameCount); - ``` - -The noise API uses simple LCG random number generation. It supports a custom seed which is useful -for things like automated testing requiring reproducibility. Setting the seed to zero will default -to `MA_DEFAULT_LCG_SEED`. - -The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`, -`ma_noise_set_seed()`, and `ma_noise_set_type()` respectively. - -By default, the noise API will use different values for different channels. So, for example, the -left side in a stereo stream will be different to the right side. To instead have each channel use -the same random value, set the `duplicateChannels` member of the noise config to true, like so: - - ```c - config.duplicateChannels = MA_TRUE; - ``` - -Below are the supported noise types. - - +------------------------+ - | Enum Name | - +------------------------+ - | ma_noise_type_white | - | ma_noise_type_pink | - | ma_noise_type_brownian | - +------------------------+ - - - -13. Audio Buffers -================= -miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can -read from memory that's managed by the application, but can also handle the memory management for -you internally. Memory management is flexible and should support most use cases. - -Audio buffers are initialised using the standard configuration system used everywhere in miniaudio: - - ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init( - format, - channels, - sizeInFrames, - pExistingData, - &allocationCallbacks); - - ma_audio_buffer buffer; - result = ma_audio_buffer_init(&config, &buffer); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_audio_buffer_uninit(&buffer); - ``` - -In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an -application can do self-managed memory allocation. If you would rather make a copy of the data, use -`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. - -Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the -raw audio data in a contiguous block of memory. That is, the raw audio data will be located -immediately after the `ma_audio_buffer` structure. To do this, use -`ma_audio_buffer_alloc_and_init()`: - - ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init( - format, - channels, - sizeInFrames, - pExistingData, - &allocationCallbacks); - - ma_audio_buffer* pBuffer - result = ma_audio_buffer_alloc_and_init(&config, &pBuffer); - if (result != MA_SUCCESS) { - // Error - } - - ... - - ma_audio_buffer_uninit_and_free(&buffer); - ``` - -If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it -with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by -`pExistingData` will be copied into the buffer, which is contrary to the behavior of -`ma_audio_buffer_init()`. - -An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the -cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should -loop. The return value is the number of frames actually read. If this is less than the number of -frames requested it means the end has been reached. This should never happen if the `loop` -parameter is set to true. If you want to manually loop back to the start, you can do so with with -`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an -audio buffer. - - ```c - ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping); - if (framesRead < desiredFrameCount) { - // If not looping, this means the end has been reached. This should never happen in looping mode with valid input. - } - ``` - -Sometimes you may want to avoid the cost of data movement between the internal buffer and the -output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data: - - ```c - void* pMappedFrames; - ma_uint64 frameCount = frameCountToTryMapping; - ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount); - if (result == MA_SUCCESS) { - // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be - // less due to the end of the buffer being reached. - ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels); - - // You must unmap the buffer. - ma_audio_buffer_unmap(pAudioBuffer, frameCount); - } - ``` - -When you use memory mapping, the read cursor is increment by the frame count passed in to -`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller -than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is -that it does not handle looping for you. You can determine if the buffer is at the end for the -purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of -`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` -as an error when returned by `ma_audio_buffer_unmap()`. - - - -14. Ring Buffers -================ -miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via -the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb` -operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around -`ma_rb`. - -Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved -streams. The caller can also allocate their own backing memory for the ring buffer to use -internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for -you. - -The examples below use the PCM frame variant of the ring buffer since that's most likely the one -you will want to use. To initialize a ring buffer, do something like the following: - - ```c - ma_pcm_rb rb; - ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb); - if (result != MA_SUCCESS) { - // Error - } - ``` - -The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because -it's the PCM varient of the ring buffer API. For the regular ring buffer that operates on bytes you -would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes -instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter -is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. -Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. - -Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is -offset from each other based on the stride. To manage your sub-buffers you can use -`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and -`ma_pcm_rb_get_subbuffer_ptr()`. - -Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section -of the ring buffer. You specify the number of frames you need, and on output it will set to what -was actually acquired. If the read or write pointer is positioned such that the number of frames -requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number -of frames you're given may be less than the number you requested. - -After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the -buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is -where the read/write pointers are updated. When you commit you need to pass in the buffer that was -returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is -only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and -`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was -originally requested. - -If you want to correct for drift between the write pointer and the read pointer you can use a -combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and -`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only -move the read pointer forward via the consumer thread, and the write pointer forward by the -producer thread. If there is too much space between the pointers, move the read pointer forward. If -there is too little space between the pointers, move the write pointer forward. - -You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` -API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and -instead of frame counts you will pass around byte counts. - -The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most -significant bit being used to encode a loop flag and the internally managed buffers always being -aligned to `MA_SIMD_ALIGNMENT`. - -Note that the ring buffer is only thread safe when used by a single consumer thread and single -producer thread. - - - -15. Backends -============ -The following backends are supported by miniaudio. - - +-------------+-----------------------+--------------------------------------------------------+ - | Name | Enum Name | Supported Operating Systems | - +-------------+-----------------------+--------------------------------------------------------+ - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | ALSA | ma_backend_alsa | Linux | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Custom | ma_backend_custom | Cross Platform | - | Null | ma_backend_null | Cross Platform (not used on Web) | - +-------------+-----------------------+--------------------------------------------------------+ - -Some backends have some nuance details you may want to be aware of. - -15.1. WASAPI ------------- -- Low-latency shared mode will be disabled when using an application-defined sample rate which is - different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC` - to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing - when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC - will result in miniaudio's internal resampler being used instead which will in turn enable the - use of low-latency shared mode. - -15.2. PulseAudio ----------------- -- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki: - https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. - Alternatively, consider using a different backend such as ALSA. - -15.3. Android -------------- -- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: - `` -- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a - limitation with OpenSL|ES. -- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration - API (devices are enumerated through Java). You can however perform your own device enumeration - through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it - to ma_device_init(). -- The backend API will perform resampling where possible. The reason for this as opposed to using - miniaudio's built-in resampler is to take advantage of any potential device-specific - optimizations the driver may implement. - -15.4. UWP ---------- -- UWP only supports default playback and capture devices. -- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): - - ``` - - ... - - - - - ``` - -15.5. Web Audio / Emscripten ----------------------------- -- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build. -- The first time a context is initialized it will create a global object called "miniaudio" whose - primary purpose is to act as a factory for device objects. -- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as - they've been deprecated. -- Google has implemented a policy in their browsers that prevent automatic media output without - first receiving some kind of user input. The following web page has additional details: - https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device - may fail if you try to start playback without first handling some kind of user input. - - - -16. Optimization Tips -===================== - -16.1. High Level API --------------------- -- If a sound does not require doppler or pitch shifting, consider disabling pitching by - initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. -- If a sound does not require spatialization, disable it by initialzing the sound with the - `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be renabled again post-initialization with - `ma_sound_set_spatialization_enabled()`. - - - -17. Miscellaneous Notes -======================= -- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for - WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though - not all have been tested. -- The contents of the output buffer passed into the data callback will always be pre-initialized to - silence unless the `noPreSilencedOutputBuffer` config variable in `ma_device_config` is set to - true, in which case it'll be undefined which will require you to write something to the entire - buffer. -- By default miniaudio will automatically clip samples. This only applies when the playback sample - format is configured as `ma_format_f32`. If you are doing clipping yourself, you can disable this - overhead by setting `noClip` to true in the device config. -- Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations. -- The sndio backend is currently only enabled on OpenBSD builds. -- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can - use it. -- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This - is due to 64-bit file APIs not being available. -*/ - -#ifndef miniaudio_h -#define miniaudio_h - -#ifdef __cplusplus -extern "C" { -#endif - -#define MA_STRINGIFY(x) #x -#define MA_XSTRINGIFY(x) MA_STRINGIFY(x) - -#define MA_VERSION_MAJOR 0 -#define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 9 -#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ - #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */ - #pragma warning(disable:4324) /* structure was padded due to alignment specifier */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif - - - -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) - #define MA_SIZEOF_PTR 8 -#else - #define MA_SIZEOF_PTR 4 -#endif - -#include /* For size_t. */ - -/* Sized types. */ -#if defined(MA_USE_STDINT) - #include - typedef int8_t ma_int8; - typedef uint8_t ma_uint8; - typedef int16_t ma_int16; - typedef uint16_t ma_uint16; - typedef int32_t ma_int32; - typedef uint32_t ma_uint32; - typedef int64_t ma_int64; - typedef uint64_t ma_uint64; -#else - typedef signed char ma_int8; - typedef unsigned char ma_uint8; - typedef signed short ma_int16; - typedef unsigned short ma_uint16; - typedef signed int ma_int32; - typedef unsigned int ma_uint32; - #if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 ma_int64; - typedef unsigned __int64 ma_uint64; - #else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long ma_int64; - typedef unsigned long long ma_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif - #endif -#endif /* MA_USE_STDINT */ - -#if MA_SIZEOF_PTR == 8 - typedef ma_uint64 ma_uintptr; -#else - typedef ma_uint32 ma_uintptr; -#endif - -typedef ma_uint8 ma_bool8; -typedef ma_uint32 ma_bool32; -#define MA_TRUE 1 -#define MA_FALSE 0 - -typedef void* ma_handle; -typedef void* ma_ptr; -typedef void (* ma_proc)(void); - -#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) -typedef ma_uint16 wchar_t; -#endif - -/* Define NULL for some compilers. */ -#ifndef NULL -#define NULL 0 -#endif - -#if defined(SIZE_MAX) - #define MA_SIZE_MAX SIZE_MAX -#else - #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ -#endif - - -/* Platform/backend detection. */ -#ifdef _WIN32 - #define MA_WIN32 - #if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)) - #define MA_WIN32_UWP - #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) - #define MA_WIN32_GDK - #else - #define MA_WIN32_DESKTOP - #endif -#else - #define MA_POSIX - - /* - Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. - You can use this to avoid including pthread.h in the header section. The downside is that it - results in some fixed sized structures being declared for the various types that are used in - miniaudio. The risk here is that these types might be too small for a given platform. This - risk is yours to take and no support will be offered if you enable this option. - */ - #ifndef MA_NO_PTHREAD_IN_HEADER - #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ - typedef pthread_t ma_pthread_t; - typedef pthread_mutex_t ma_pthread_mutex_t; - typedef pthread_cond_t ma_pthread_cond_t; - #else - typedef ma_uintptr ma_pthread_t; - typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; - typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; - #endif - - #ifdef __unix__ - #define MA_UNIX - #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif - #endif - #ifdef __linux__ - #define MA_LINUX - #endif - #ifdef __APPLE__ - #define MA_APPLE - #endif - #ifdef __ANDROID__ - #define MA_ANDROID - #endif - #ifdef __EMSCRIPTEN__ - #define MA_EMSCRIPTEN - #endif -#endif - - -#ifdef _MSC_VER - #define MA_INLINE __forceinline -#elif defined(__GNUC__) - /* - I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when - the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some - case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the - command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue - I am using "__inline__" only when we're compiling in strict ANSI mode. - */ - #if defined(__STRICT_ANSI__) - #define MA_GNUC_INLINE_HINT __inline__ - #else - #define MA_GNUC_INLINE_HINT inline - #endif - - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) - #else - #define MA_INLINE MA_GNUC_INLINE_HINT - #endif -#elif defined(__WATCOMC__) - #define MA_INLINE __inline -#else - #define MA_INLINE -#endif - -#if !defined(MA_API) - #if defined(MA_DLL) - #if defined(_WIN32) - #define MA_DLL_IMPORT __declspec(dllimport) - #define MA_DLL_EXPORT __declspec(dllexport) - #define MA_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define MA_DLL_IMPORT __attribute__((visibility("default"))) - #define MA_DLL_EXPORT __attribute__((visibility("default"))) - #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define MA_DLL_IMPORT - #define MA_DLL_EXPORT - #define MA_DLL_PRIVATE static - #endif - #endif - - #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) - #define MA_API MA_DLL_EXPORT - #else - #define MA_API MA_DLL_IMPORT - #endif - #define MA_PRIVATE MA_DLL_PRIVATE - #else - #define MA_API extern - #define MA_PRIVATE static - #endif -#endif - -/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ -#define MA_SIMD_ALIGNMENT 32 - - -/* -Logging Levels -============== -Log levels are only used to give logging callbacks some context as to the severity of a log message -so they can do filtering. All log levels will be posted to registered logging callbacks. If you -don't want to output a certain log level you can discriminate against the log level in the callback. - -MA_LOG_LEVEL_DEBUG - Used for debugging. Useful for debug and test builds, but should be disabled in release builds. - -MA_LOG_LEVEL_INFO - Informational logging. Useful for debugging. This will never be called from within the data - callback. - -MA_LOG_LEVEL_WARNING - Warnings. You should enable this in you development builds and action them when encounted. These - logs usually indicate a potential problem or misconfiguration, but still allow you to keep - running. This will never be called from within the data callback. - -MA_LOG_LEVEL_ERROR - Error logging. This will be fired when an operation fails and is subsequently aborted. This can - be fired from within the data callback, in which case the device will be stopped. You should - always have this log level enabled. -*/ -typedef enum -{ - MA_LOG_LEVEL_DEBUG = 4, - MA_LOG_LEVEL_INFO = 3, - MA_LOG_LEVEL_WARNING = 2, - MA_LOG_LEVEL_ERROR = 1 -} ma_log_level; - -/* -Variables needing to be accessed atomically should be declared with this macro for two reasons: - - 1) It allows people who read the code to identify a variable as such; and - 2) It forces alignment on platforms where it's required or optimal. - -Note that for x86/64, alignment is not strictly necessary, but does have some performance -implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU -architecture does not require it, it will simply leave it unaligned. This is the case with old -versions of Visual Studio, which I've confirmed with at least VC6. -*/ -#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) - #include - #define MA_ATOMIC(alignment, type) alignas(alignment) type -#else - #if defined(__GNUC__) - /* GCC-style compilers. */ - #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment))) - #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */ - /* MSVC. */ - #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type - #else - /* Other compilers. */ - #define MA_ATOMIC(alignment, type) type - #endif -#endif - -typedef struct ma_context ma_context; -typedef struct ma_device ma_device; - -typedef ma_uint8 ma_channel; -typedef enum -{ - MA_CHANNEL_NONE = 0, - MA_CHANNEL_MONO = 1, - MA_CHANNEL_FRONT_LEFT = 2, - MA_CHANNEL_FRONT_RIGHT = 3, - MA_CHANNEL_FRONT_CENTER = 4, - MA_CHANNEL_LFE = 5, - MA_CHANNEL_BACK_LEFT = 6, - MA_CHANNEL_BACK_RIGHT = 7, - MA_CHANNEL_FRONT_LEFT_CENTER = 8, - MA_CHANNEL_FRONT_RIGHT_CENTER = 9, - MA_CHANNEL_BACK_CENTER = 10, - MA_CHANNEL_SIDE_LEFT = 11, - MA_CHANNEL_SIDE_RIGHT = 12, - MA_CHANNEL_TOP_CENTER = 13, - MA_CHANNEL_TOP_FRONT_LEFT = 14, - MA_CHANNEL_TOP_FRONT_CENTER = 15, - MA_CHANNEL_TOP_FRONT_RIGHT = 16, - MA_CHANNEL_TOP_BACK_LEFT = 17, - MA_CHANNEL_TOP_BACK_CENTER = 18, - MA_CHANNEL_TOP_BACK_RIGHT = 19, - MA_CHANNEL_AUX_0 = 20, - MA_CHANNEL_AUX_1 = 21, - MA_CHANNEL_AUX_2 = 22, - MA_CHANNEL_AUX_3 = 23, - MA_CHANNEL_AUX_4 = 24, - MA_CHANNEL_AUX_5 = 25, - MA_CHANNEL_AUX_6 = 26, - MA_CHANNEL_AUX_7 = 27, - MA_CHANNEL_AUX_8 = 28, - MA_CHANNEL_AUX_9 = 29, - MA_CHANNEL_AUX_10 = 30, - MA_CHANNEL_AUX_11 = 31, - MA_CHANNEL_AUX_12 = 32, - MA_CHANNEL_AUX_13 = 33, - MA_CHANNEL_AUX_14 = 34, - MA_CHANNEL_AUX_15 = 35, - MA_CHANNEL_AUX_16 = 36, - MA_CHANNEL_AUX_17 = 37, - MA_CHANNEL_AUX_18 = 38, - MA_CHANNEL_AUX_19 = 39, - MA_CHANNEL_AUX_20 = 40, - MA_CHANNEL_AUX_21 = 41, - MA_CHANNEL_AUX_22 = 42, - MA_CHANNEL_AUX_23 = 43, - MA_CHANNEL_AUX_24 = 44, - MA_CHANNEL_AUX_25 = 45, - MA_CHANNEL_AUX_26 = 46, - MA_CHANNEL_AUX_27 = 47, - MA_CHANNEL_AUX_28 = 48, - MA_CHANNEL_AUX_29 = 49, - MA_CHANNEL_AUX_30 = 50, - MA_CHANNEL_AUX_31 = 51, - MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, - MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, - MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) -} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ - -typedef enum -{ - MA_SUCCESS = 0, - MA_ERROR = -1, /* A generic error. */ - MA_INVALID_ARGS = -2, - MA_INVALID_OPERATION = -3, - MA_OUT_OF_MEMORY = -4, - MA_OUT_OF_RANGE = -5, - MA_ACCESS_DENIED = -6, - MA_DOES_NOT_EXIST = -7, - MA_ALREADY_EXISTS = -8, - MA_TOO_MANY_OPEN_FILES = -9, - MA_INVALID_FILE = -10, - MA_TOO_BIG = -11, - MA_PATH_TOO_LONG = -12, - MA_NAME_TOO_LONG = -13, - MA_NOT_DIRECTORY = -14, - MA_IS_DIRECTORY = -15, - MA_DIRECTORY_NOT_EMPTY = -16, - MA_AT_END = -17, - MA_NO_SPACE = -18, - MA_BUSY = -19, - MA_IO_ERROR = -20, - MA_INTERRUPT = -21, - MA_UNAVAILABLE = -22, - MA_ALREADY_IN_USE = -23, - MA_BAD_ADDRESS = -24, - MA_BAD_SEEK = -25, - MA_BAD_PIPE = -26, - MA_DEADLOCK = -27, - MA_TOO_MANY_LINKS = -28, - MA_NOT_IMPLEMENTED = -29, - MA_NO_MESSAGE = -30, - MA_BAD_MESSAGE = -31, - MA_NO_DATA_AVAILABLE = -32, - MA_INVALID_DATA = -33, - MA_TIMEOUT = -34, - MA_NO_NETWORK = -35, - MA_NOT_UNIQUE = -36, - MA_NOT_SOCKET = -37, - MA_NO_ADDRESS = -38, - MA_BAD_PROTOCOL = -39, - MA_PROTOCOL_UNAVAILABLE = -40, - MA_PROTOCOL_NOT_SUPPORTED = -41, - MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, - MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, - MA_SOCKET_NOT_SUPPORTED = -44, - MA_CONNECTION_RESET = -45, - MA_ALREADY_CONNECTED = -46, - MA_NOT_CONNECTED = -47, - MA_CONNECTION_REFUSED = -48, - MA_NO_HOST = -49, - MA_IN_PROGRESS = -50, - MA_CANCELLED = -51, - MA_MEMORY_ALREADY_MAPPED = -52, - - /* General miniaudio-specific errors. */ - MA_FORMAT_NOT_SUPPORTED = -100, - MA_DEVICE_TYPE_NOT_SUPPORTED = -101, - MA_SHARE_MODE_NOT_SUPPORTED = -102, - MA_NO_BACKEND = -103, - MA_NO_DEVICE = -104, - MA_API_NOT_FOUND = -105, - MA_INVALID_DEVICE_CONFIG = -106, - MA_LOOP = -107, - - /* State errors. */ - MA_DEVICE_NOT_INITIALIZED = -200, - MA_DEVICE_ALREADY_INITIALIZED = -201, - MA_DEVICE_NOT_STARTED = -202, - MA_DEVICE_NOT_STOPPED = -203, - - /* Operation errors. */ - MA_FAILED_TO_INIT_BACKEND = -300, - MA_FAILED_TO_OPEN_BACKEND_DEVICE = -301, - MA_FAILED_TO_START_BACKEND_DEVICE = -302, - MA_FAILED_TO_STOP_BACKEND_DEVICE = -303 -} ma_result; - - -#define MA_MIN_CHANNELS 1 -#ifndef MA_MAX_CHANNELS -#define MA_MAX_CHANNELS 254 -#endif - -#ifndef MA_MAX_FILTER_ORDER -#define MA_MAX_FILTER_ORDER 8 -#endif - -typedef enum -{ - ma_stream_format_pcm = 0 -} ma_stream_format; - -typedef enum -{ - ma_stream_layout_interleaved = 0, - ma_stream_layout_deinterleaved -} ma_stream_layout; - -typedef enum -{ - ma_dither_mode_none = 0, - ma_dither_mode_rectangle, - ma_dither_mode_triangle -} ma_dither_mode; - -typedef enum -{ - /* - I like to keep these explicitly defined because they're used as a key into a lookup table. When items are - added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample(). - */ - ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */ - ma_format_u8 = 1, - ma_format_s16 = 2, /* Seems to be the most widely supported format. */ - ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */ - ma_format_s32 = 4, - ma_format_f32 = 5, - ma_format_count -} ma_format; - -typedef enum -{ - /* Standard rates need to be in priority order. */ - ma_standard_sample_rate_48000 = 48000, /* Most common */ - ma_standard_sample_rate_44100 = 44100, - - ma_standard_sample_rate_32000 = 32000, /* Lows */ - ma_standard_sample_rate_24000 = 24000, - ma_standard_sample_rate_22050 = 22050, - - ma_standard_sample_rate_88200 = 88200, /* Highs */ - ma_standard_sample_rate_96000 = 96000, - ma_standard_sample_rate_176400 = 176400, - ma_standard_sample_rate_192000 = 192000, - - ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ - ma_standard_sample_rate_11025 = 11250, - ma_standard_sample_rate_8000 = 8000, - - ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ - ma_standard_sample_rate_384000 = 384000, - - ma_standard_sample_rate_min = ma_standard_sample_rate_8000, - ma_standard_sample_rate_max = ma_standard_sample_rate_384000, - ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */ -} ma_standard_sample_rate; - - -typedef enum -{ - ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ - ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ - ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */ - ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular -} ma_channel_mix_mode; - -typedef enum -{ - ma_standard_channel_map_microsoft, - ma_standard_channel_map_alsa, - ma_standard_channel_map_rfc3551, /* Based off AIFF. */ - ma_standard_channel_map_flac, - ma_standard_channel_map_vorbis, - ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */ - ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */ - ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */ - ma_standard_channel_map_default = ma_standard_channel_map_microsoft -} ma_standard_channel_map; - -typedef enum -{ - ma_performance_profile_low_latency = 0, - ma_performance_profile_conservative -} ma_performance_profile; - - -typedef struct -{ - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} ma_allocation_callbacks; - -typedef struct -{ - ma_int32 state; -} ma_lcg; - - -/* Spinlocks are 32-bit for compatibility reasons. */ -typedef ma_uint32 ma_spinlock; - -#ifndef MA_NO_THREADING -/* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ -typedef enum -{ - ma_thread_priority_idle = -5, - ma_thread_priority_lowest = -4, - ma_thread_priority_low = -3, - ma_thread_priority_normal = -2, - ma_thread_priority_high = -1, - ma_thread_priority_highest = 0, - ma_thread_priority_realtime = 1, - ma_thread_priority_default = 0 -} ma_thread_priority; - -#if defined(MA_WIN32) -typedef ma_handle ma_thread; -#endif -#if defined(MA_POSIX) -typedef ma_pthread_t ma_thread; -#endif - -#if defined(MA_WIN32) -typedef ma_handle ma_mutex; -#endif -#if defined(MA_POSIX) -typedef ma_pthread_mutex_t ma_mutex; -#endif - -#if defined(MA_WIN32) -typedef ma_handle ma_event; -#endif -#if defined(MA_POSIX) -typedef struct -{ - ma_uint32 value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; -} ma_event; -#endif /* MA_POSIX */ - -#if defined(MA_WIN32) -typedef ma_handle ma_semaphore; -#endif -#if defined(MA_POSIX) -typedef struct -{ - int value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; -} ma_semaphore; -#endif /* MA_POSIX */ -#else -/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ -#ifndef MA_NO_DEVICE_IO -#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; -#endif -#endif /* MA_NO_THREADING */ - - -/* -Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required. -*/ -MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); - -/* -Retrieves the version of miniaudio as a string which can be useful for logging purposes. -*/ -MA_API const char* ma_version_string(void); - - -/************************************************************************************************************************************************************** - -Logging - -**************************************************************************************************************************************************************/ -#include /* For va_list. */ - -#if defined(__has_attribute) - #if __has_attribute(format) - #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) - #endif -#endif -#ifndef MA_ATTRIBUTE_FORMAT -#define MA_ATTRIBUTE_FORMAT(fmt,va) -#endif - -#ifndef MA_MAX_LOG_CALLBACKS -#define MA_MAX_LOG_CALLBACKS 4 -#endif - - -/* -The callback for handling log messages. - - -Parameters ----------- -pUserData (in) - The user data pointer that was passed into ma_log_register_callback(). - -logLevel (in) - The log level. This can be one of the following: - - +----------------------+ - | Log Level | - +----------------------+ - | MA_LOG_LEVEL_DEBUG | - | MA_LOG_LEVEL_INFO | - | MA_LOG_LEVEL_WARNING | - | MA_LOG_LEVEL_ERROR | - +----------------------+ - -pMessage (in) - The log message. - - -Remarks -------- -Do not modify the state of the device from inside the callback. -*/ -typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); - -typedef struct -{ - ma_log_callback_proc onLog; - void* pUserData; -} ma_log_callback; - -MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData); - - -typedef struct -{ - ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]; - ma_uint32 callbackCount; - ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */ -#ifndef MA_NO_THREADING - ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */ -#endif -} ma_log; - -MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog); -MA_API void ma_log_uninit(ma_log* pLog); -MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback); -MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback); -MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage); -MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args); -MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4); - - -/************************************************************************************************************************************************************** - -Biquad Filtering - -**************************************************************************************************************************************************************/ -typedef union -{ - float f32; - ma_int32 s32; -} ma_biquad_coefficient; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - double b0; - double b1; - double b2; - double a0; - double a1; - double a2; -} ma_biquad_config; - -MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient b0; - ma_biquad_coefficient b1; - ma_biquad_coefficient b2; - ma_biquad_coefficient a1; - ma_biquad_coefficient a2; - ma_biquad_coefficient* pR1; - ma_biquad_coefficient* pR2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_biquad; - -MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ); -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ); -MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); -MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ); -MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ); - - -/************************************************************************************************************************************************************** - -Low-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_lpf1_config, ma_lpf2_config; - -MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); -MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient a; - ma_biquad_coefficient* pR1; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_lpf1; - -MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF); -MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF); - -typedef struct -{ - ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */ -} ma_lpf2; - -MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF); -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF); -MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); -MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF); -MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_lpf_config; - -MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_lpf1* pLPF1; - ma_lpf2* pLPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_lpf; - -MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF); -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF); -MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); -MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF); -MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF); - - -/************************************************************************************************************************************************************** - -High-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_hpf1_config, ma_hpf2_config; - -MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); -MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient a; - ma_biquad_coefficient* pR1; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_hpf1; - -MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF); -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF); -MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); -MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF); - -typedef struct -{ - ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */ -} ma_hpf2; - -MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF); -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF); -MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); -MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_hpf_config; - -MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_hpf1* pHPF1; - ma_hpf2* pHPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_hpf; - -MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF); -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF); -MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF); -MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF); - - -/************************************************************************************************************************************************************** - -Band-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_bpf2_config; - -MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */ -} ma_bpf2; - -MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF); -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF); -MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); -MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_bpf_config; - -MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 bpf2Count; - ma_bpf2* pBPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_bpf; - -MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF); -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF); -MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF); -MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF); - - -/************************************************************************************************************************************************************** - -Notching Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double q; - double frequency; -} ma_notch2_config, ma_notch_config; - -MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_notch2; - -MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter); -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter); -MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter); -MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter); - - -/************************************************************************************************************************************************************** - -Peaking EQ Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double q; - double frequency; -} ma_peak2_config, ma_peak_config; - -MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_peak2; - -MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter); -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter); -MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter); -MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter); - - -/************************************************************************************************************************************************************** - -Low Shelf Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double shelfSlope; - double frequency; -} ma_loshelf2_config, ma_loshelf_config; - -MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_loshelf2; - -MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter); -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter); -MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); -MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter); - - -/************************************************************************************************************************************************************** - -High Shelf Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double shelfSlope; - double frequency; -} ma_hishelf2_config, ma_hishelf_config; - -MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_hishelf2; - -MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter); -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter); -MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); -MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter); - - - -/* -Delay -*/ -typedef struct -{ - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 delayInFrames; - ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */ - float wet; /* 0..1. Default = 1. */ - float dry; /* 0..1. Default = 1. */ - float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ -} ma_delay_config; - -MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); - - -typedef struct -{ - ma_delay_config config; - ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ - ma_uint32 bufferSizeInFrames; /* The maximum of config.startDelayInFrames and config.feedbackDelayInFrames. */ - float* pBuffer; -} ma_delay; - -MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay); -MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount); -MA_API void ma_delay_set_wet(ma_delay* pDelay, float value); -MA_API float ma_delay_get_wet(const ma_delay* pDelay); -MA_API void ma_delay_set_dry(ma_delay* pDelay, float value); -MA_API float ma_delay_get_dry(const ma_delay* pDelay); -MA_API void ma_delay_set_decay(ma_delay* pDelay, float value); -MA_API float ma_delay_get_decay(const ma_delay* pDelay); - - -/* Gainer for smooth volume changes. */ -typedef struct -{ - ma_uint32 channels; - ma_uint32 smoothTimeInFrames; -} ma_gainer_config; - -MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames); - - -typedef struct -{ - ma_gainer_config config; - ma_uint32 t; - float* pOldGains; - float* pNewGains; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_gainer; - -MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer); -MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer); -MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); -MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); - - - -/* Stereo panner. */ -typedef enum -{ - ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ - ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */ -} ma_pan_mode; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_pan_mode mode; - float pan; -} ma_panner_config; - -MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_pan_mode mode; - float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ -} ma_panner; - -MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner); -MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode); -MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner); -MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan); -MA_API float ma_panner_get_pan(const ma_panner* pPanner); - - - -/* Fader. */ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; -} ma_fader_config; - -MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate); - -typedef struct -{ - ma_fader_config config; - float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ - float volumeEnd; - ma_uint64 lengthInFrames; /* The total length of the fade. */ - ma_uint64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */ -} ma_fader; - -MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); -MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); -MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); -MA_API float ma_fader_get_current_volume(ma_fader* pFader); - - - -/* Spatializer. */ -typedef struct -{ - float x; - float y; - float z; -} ma_vec3f; - -typedef enum -{ - ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ - ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ - ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ - ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ -} ma_attenuation_model; - -typedef enum -{ - ma_positioning_absolute, - ma_positioning_relative -} ma_positioning; - -typedef enum -{ - ma_handedness_right, - ma_handedness_left -} ma_handedness; - - -typedef struct -{ - ma_uint32 channelsOut; - ma_channel* pChannelMapOut; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float speedOfSound; - ma_vec3f worldUp; -} ma_spatializer_listener_config; - -MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut); - - -typedef struct -{ - ma_spatializer_listener_config config; - ma_vec3f position; /* The absolute position of the listener. */ - ma_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ - ma_vec3f velocity; - ma_bool32 isEnabled; - - /* Memory management. */ - ma_bool32 _ownsHeap; - void* _pHeap; -} ma_spatializer_listener; - -MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener); -MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound); -MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled); -MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener); - - -typedef struct -{ - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel* pChannelMapIn; - ma_attenuation_model attenuationModel; - ma_positioning positioning; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float minGain; - float maxGain; - float minDistance; - float maxDistance; - float rolloff; - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. */ - float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ - ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ -} ma_spatializer_config; - -MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut); - - -typedef struct -{ - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel* pChannelMapIn; - ma_attenuation_model attenuationModel; - ma_positioning positioning; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float minGain; - float maxGain; - float minDistance; - float maxDistance; - float rolloff; - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. */ - float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ - ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ - ma_vec3f position; - ma_vec3f direction; - ma_vec3f velocity; /* For doppler effect. */ - float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ - ma_gainer gainer; /* For smooth gain transitions. */ - float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_spatializer; - -MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer); -MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); -MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); -MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning); -MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff); -MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain); -MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain); -MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance); -MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance); -MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); -MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); -MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DATA CONVERSION -=============== - -This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ - -/************************************************************************************************************************************************************** - -Resampling - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */ - double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */ -} ma_linear_resampler_config; - -MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - -typedef struct -{ - ma_linear_resampler_config config; - ma_uint32 inAdvanceInt; - ma_uint32 inAdvanceFrac; - ma_uint32 inTimeInt; - ma_uint32 inTimeFrac; - union - { - float* f32; - ma_int16* s16; - } x0; /* The previous input frame. */ - union - { - float* f32; - ma_int16* s16; - } x1; /* The next input frame. */ - ma_lpf lpf; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_linear_resampler; - -MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler); -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler); -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); -MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); -MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); -MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); -MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); -MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); - - -typedef struct ma_resampler_config ma_resampler_config; - -typedef void ma_resampling_backend; -typedef struct -{ - ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); - ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); - void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); - ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); - ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ - ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ - ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ - ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */ - ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */ - ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); -} ma_resampling_backend_vtable; - -typedef enum -{ - ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ - ma_resample_algorithm_custom, -} ma_resample_algorithm; - -struct ma_resampler_config -{ - ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */ - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - struct - { - ma_uint32 lpfOrder; - } linear; -}; - -MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); - -typedef struct -{ - ma_resampling_backend* pBackend; - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - union - { - ma_linear_resampler linear; - } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_resampler; - -MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler); - -/* -Initializes a new resampler object from a config. -*/ -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler); - -/* -Uninitializes a resampler. -*/ -MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Converts the given input data. - -Both the input and output frames must be in the format specified in the config when the resampler was initilized. - -On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that -were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use -ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames. - -On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole -input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames -you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead. - -If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of -output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input -frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be -processed. In this case, any internal filter state will be updated as if zeroes were passed in. - -It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL. - -It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL. -*/ -MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); - - -/* -Sets the input and output sample sample rate. -*/ -MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - -/* -Sets the input and output sample rate as a ratio. - -The ration is in/out. -*/ -MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio); - -/* -Retrieves the latency introduced by the resampler in input frames. -*/ -MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler); - -/* -Retrieves the latency introduced by the resampler in output frames. -*/ -MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); - -/* -Calculates the number of whole input frames that would need to be read from the client in order to output the specified -number of output frames. - -The returned value does not include cached input frames. It only returns the number of extra frames that would need to be -read from the input buffer in order to output the specified number of output frames. -*/ -MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); - -/* -Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of -input frames. -*/ -MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); - -/* -Resets the resampler's timer and clears it's internal cache. -*/ -MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); - - -/************************************************************************************************************************************************************** - -Channel Conversion - -**************************************************************************************************************************************************************/ -typedef enum -{ - ma_channel_conversion_path_unknown, - ma_channel_conversion_path_passthrough, - ma_channel_conversion_path_mono_out, /* Converting to mono. */ - ma_channel_conversion_path_mono_in, /* Converting from mono. */ - ma_channel_conversion_path_shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ - ma_channel_conversion_path_weights /* Blended based on weights. */ -} ma_channel_conversion_path; - -typedef enum -{ - ma_mono_expansion_mode_duplicate = 0, /* The default. */ - ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */ - ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ - ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate -} ma_mono_expansion_mode; - -typedef struct -{ - ma_format format; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - const ma_channel* pChannelMapIn; - const ma_channel* pChannelMapOut; - ma_channel_mix_mode mixingMode; - float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ -} ma_channel_converter_config; - -MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode); - -typedef struct -{ - ma_format format; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel_mix_mode mixingMode; - ma_channel_conversion_path conversionPath; - ma_channel* pChannelMapIn; - ma_channel* pChannelMapOut; - ma_uint8* pShuffleTable; /* Indexed by output channel index. */ - union - { - float** f32; - ma_int32** s16; - } weights; /* [in][out] */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_channel_converter; - -MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter); -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter); -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); - - -/************************************************************************************************************************************************************** - -Data Conversion - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format formatIn; - ma_format formatOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_channel* pChannelMapIn; - ma_channel* pChannelMapOut; - ma_dither_mode ditherMode; - ma_channel_mix_mode channelMixMode; - float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ - ma_bool32 allowDynamicSampleRate; - ma_resampler_config resampling; -} ma_data_converter_config; - -MA_API ma_data_converter_config ma_data_converter_config_init_default(void); -MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - - -typedef enum -{ - ma_data_converter_execution_path_passthrough, /* No conversion. */ - ma_data_converter_execution_path_format_only, /* Only format conversion. */ - ma_data_converter_execution_path_channels_only, /* Only channel conversion. */ - ma_data_converter_execution_path_resample_only, /* Only resampling. */ - ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */ - ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */ -} ma_data_converter_execution_path; - -typedef struct -{ - ma_format formatIn; - ma_format formatOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_dither_mode ditherMode; - ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */ - ma_channel_converter channelConverter; - ma_resampler resampler; - ma_bool8 hasPreFormatConversion; - ma_bool8 hasPostFormatConversion; - ma_bool8 hasChannelConverter; - ma_bool8 hasResampler; - ma_bool8 isPassthrough; - - /* Memory management. */ - ma_bool8 _ownsHeap; - void* _pHeap; -} ma_data_converter; - -MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter); -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter); -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); -MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); -MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); -MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); -MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); -MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); -MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); -MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); - - -/************************************************************************************************************************************************************ - -Format Conversion - -************************************************************************************************************************************************************/ -MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode); -MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode); - -/* -Deinterleaves an interleaved buffer. -*/ -MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); - -/* -Interleaves a group of deinterleaved buffers. -*/ -MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); - - -/************************************************************************************************************************************************************ - -Channel Maps - -************************************************************************************************************************************************************/ -/* -This is used in the shuffle table to indicate that the channel index is undefined and should be ignored. -*/ -#define MA_CHANNEL_INDEX_NULL 255 - -/* -Retrieves the channel position of the specified channel in the given channel map. - -The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed. -*/ -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); - -/* -Initializes a blank channel map. - -When a blank channel map is specified anywhere it indicates that the native channel map should be used. -*/ -MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for retrieving a standard channel map. - -The output channel map buffer must have a capacity of at least `channelMapCap`. -*/ -MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels); - -/* -Copies a channel map. - -Both input and output channel map buffers must have a capacity of at at least `channels`. -*/ -MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); - -/* -Copies a channel map if one is specified, otherwise copies the default channel map. - -The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. -*/ -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels); - - -/* -Determines whether or not a channel map is valid. - -A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but -is usually treated as a passthrough. - -Invalid channel maps: - - A channel map with no channels - - A channel map with more than one channel and a mono channel - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for comparing two channel maps for equality. - -This assumes the channel count is the same between the two. - -Both channels map buffers must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels); - -/* -Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for determining whether or not a channel is present in the given channel map. - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); - - -/************************************************************************************************************************************************************ - -Conversion Helpers - -************************************************************************************************************************************************************/ - -/* -High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to -determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is -ignored. - -A return value of 0 indicates an error. - -This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead. -*/ -MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn); -MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); - - -/************************************************************************************************************************************************************ - -Ring Buffer - -************************************************************************************************************************************************************/ -typedef struct -{ - void* pBuffer; - ma_uint32 subbufferSizeInBytes; - ma_uint32 subbufferCount; - ma_uint32 subbufferStrideInBytes; - MA_ATOMIC(4, ma_uint32) encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ - ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ - ma_allocation_callbacks allocationCallbacks; -} ma_rb; - -MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); -MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); -MA_API void ma_rb_uninit(ma_rb* pRB); -MA_API void ma_rb_reset(ma_rb* pRB); -MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes); -MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes); -MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes); -MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes); -MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ -MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB); -MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex); -MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer); - - -typedef struct -{ - ma_rb rb; - ma_format format; - ma_uint32 channels; -} ma_pcm_rb; - -MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); -MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB); -MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); -MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); -MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); -MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); -MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */ -MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); -MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); - - -/* -The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The -capture device writes to it, and then a playback device reads from it. - -At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly -handle desyncs. Note that the API is work in progress and may change at any time in any version. - -The size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size -in frames. The internal sample rate of the capture device is also needed in order to calculate the size. -*/ -typedef struct -{ - ma_pcm_rb rb; -} ma_duplex_rb; - -MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB); -MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB); - - -/************************************************************************************************************************************************************ - -Miscellaneous Helpers - -************************************************************************************************************************************************************/ -/* -Retrieves a human readable description of the given result code. -*/ -MA_API const char* ma_result_description(ma_result result); - -/* -malloc() -*/ -MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -calloc() -*/ -MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -realloc() -*/ -MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -free() -*/ -MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Performs an aligned malloc, with the assumption that the alignment is a power of 2. -*/ -MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Free's an aligned malloc'd buffer. -*/ -MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Retrieves a friendly name for a format. -*/ -MA_API const char* ma_get_format_name(ma_format format); - -/* -Blends two frames in floating point format. -*/ -MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels); - -/* -Retrieves the size of a sample in bytes for the given format. - -This API is efficient and is implemented using a lookup table. - -Thread Safety: SAFE - This API is pure. -*/ -MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format); -static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; } - -/* -Converts a log level to a string. -*/ -MA_API const char* ma_log_level_to_string(ma_uint32 logLevel); - - - - -/************************************************************************************************************************************************************ - -Synchronization - -************************************************************************************************************************************************************/ -/* -Locks a spinlock. -*/ -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); - -/* -Locks a spinlock, but does not yield() when looping. -*/ -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); - -/* -Unlocks a spinlock. -*/ -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); - - -#ifndef MA_NO_THREADING - -/* -Creates a mutex. - -A mutex must be created from a valid context. A mutex is initially unlocked. -*/ -MA_API ma_result ma_mutex_init(ma_mutex* pMutex); - -/* -Deletes a mutex. -*/ -MA_API void ma_mutex_uninit(ma_mutex* pMutex); - -/* -Locks a mutex with an infinite timeout. -*/ -MA_API void ma_mutex_lock(ma_mutex* pMutex); - -/* -Unlocks a mutex. -*/ -MA_API void ma_mutex_unlock(ma_mutex* pMutex); - - -/* -Initializes an auto-reset event. -*/ -MA_API ma_result ma_event_init(ma_event* pEvent); - -/* -Uninitializes an auto-reset event. -*/ -MA_API void ma_event_uninit(ma_event* pEvent); - -/* -Waits for the specified auto-reset event to become signalled. -*/ -MA_API ma_result ma_event_wait(ma_event* pEvent); - -/* -Signals the specified auto-reset event. -*/ -MA_API ma_result ma_event_signal(ma_event* pEvent); -#endif /* MA_NO_THREADING */ - - -/* -Fence -===== -This locks while the counter is larger than 0. Counter can be incremented and decremented by any -thread, but care needs to be taken when waiting. It is possible for one thread to acquire the -fence just as another thread returns from ma_fence_wait(). - -The idea behind a fence is to allow you to wait for a group of operations to complete. When an -operation starts, the counter is incremented which locks the fence. When the operation completes, -the fence will be released which decrements the counter. ma_fence_wait() will block until the -counter hits zero. - -If threading is disabled, ma_fence_wait() will spin on the counter. -*/ -typedef struct -{ -#ifndef MA_NO_THREADING - ma_event e; -#endif - ma_uint32 counter; -} ma_fence; - -MA_API ma_result ma_fence_init(ma_fence* pFence); -MA_API void ma_fence_uninit(ma_fence* pFence); -MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */ -MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */ -MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */ - - - -/* -Notification callback for asynchronous operations. -*/ -typedef void ma_async_notification; - -typedef struct -{ - void (* onSignal)(ma_async_notification* pNotification); -} ma_async_notification_callbacks; - -MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification); - - -/* -Simple polling notification. - -This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() -*/ -typedef struct -{ - ma_async_notification_callbacks cb; - ma_bool32 signalled; -} ma_async_notification_poll; - -MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll); -MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll); - - -/* -Event Notification - -This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. -*/ -typedef struct -{ - ma_async_notification_callbacks cb; -#ifndef MA_NO_THREADING - ma_event e; -#endif -} ma_async_notification_event; - -MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); - - - - -/************************************************************************************************************************************************************ - -Job Queue - -************************************************************************************************************************************************************/ - -/* -Slot Allocator --------------- -The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used -as the insertion point for an object. - -Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. - -The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: - - +-----------------+-----------------+ - | 32 Bits | 32 Bits | - +-----------------+-----------------+ - | Reference Count | Slot Index | - +-----------------+-----------------+ -*/ -typedef struct -{ - ma_uint32 capacity; /* The number of slots to make available. */ -} ma_slot_allocator_config; - -MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity); - - -typedef struct -{ - MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ -} ma_slot_allocator_group; - -typedef struct -{ - ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */ - ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */ - ma_uint32 count; /* Allocation count. */ - ma_uint32 capacity; - - /* Memory management. */ - ma_bool32 _ownsHeap; - void* _pHeap; -} ma_slot_allocator; - -MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator); -MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator); -MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot); -MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot); - - -typedef struct ma_job ma_job; - -/* -Callback for processing a job. Each job type will have their own processing callback which will be -called by ma_job_process(). -*/ -typedef ma_result (* ma_job_proc)(ma_job* pJob); - -/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ -typedef enum -{ - /* Miscellaneous. */ - MA_JOB_TYPE_QUIT = 0, - MA_JOB_TYPE_CUSTOM, - - /* Resource Manager. */ - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER, - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM, - - /* Device. */ - MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE, - - /* Count. Must always be last. */ - MA_JOB_TYPE_COUNT -} ma_job_type; - -struct ma_job -{ - union - { - struct - { - ma_uint16 code; /* Job type. */ - ma_uint16 slot; /* Index into a ma_slot_allocator. */ - ma_uint32 refcount; - } breakup; - ma_uint64 allocation; - } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */ - MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */ - ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ - - union - { - /* Miscellaneous. */ - struct - { - ma_job_proc proc; - ma_uintptr data0; - ma_uintptr data1; - } custom; - - /* Resource Manager */ - union - { - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - char* pFilePath; - wchar_t* pFilePathW; - ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */ - ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ - ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */ - ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ - } loadDataBufferNode; - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataBufferNode; - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - /*ma_decoder**/ void* pDecoder; - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ - ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ - } pageDataBufferNode; - - struct - { - /*ma_resource_manager_data_buffer**/ void* pDataBuffer; - ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ - ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */ - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_uint32 isLooping; - } loadDataBuffer; - struct - { - /*ma_resource_manager_data_buffer**/ void* pDataBuffer; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataBuffer; - - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ - wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ - ma_uint64 initialSeekPoint; - ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ - ma_fence* pInitFence; - } loadDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_uint32 pageIndex; /* The index of the page to decode into. */ - } pageDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_uint64 frameIndex; - } seekDataStream; - } resourceManager; - - /* Device. */ - union - { - union - { - struct - { - /*ma_device**/ void* pDevice; - /*ma_device_type*/ ma_uint32 deviceType; - } reroute; - } aaudio; - } device; - } data; -}; - -MA_API ma_job ma_job_init(ma_uint16 code); -MA_API ma_result ma_job_process(ma_job* pJob); - - -/* -When set, ma_job_queue_next() will not wait and no semaphore will be signaled in -ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. - -This flag should always be used for platforms that do not support multithreading. -*/ -typedef enum -{ - MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 -} ma_job_queue_flags; - -typedef struct -{ - ma_uint32 flags; - ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */ -} ma_job_queue_config; - -MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity); - - -typedef struct -{ - ma_uint32 flags; /* Flags passed in at initialization time. */ - ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ - MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */ - MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */ -#ifndef MA_NO_THREADING - ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ -#endif - ma_slot_allocator allocator; - ma_job* pJobs; -#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock lock; -#endif - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_job_queue; - -MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue); -MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue); -MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob); -MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */ - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DEVICE I/O -========== - -This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc. - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#ifndef MA_NO_DEVICE_IO -/* Some backends are only supported on certain platforms. */ -#if defined(MA_WIN32) - #define MA_SUPPORT_WASAPI - #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ - #define MA_SUPPORT_DSOUND - #define MA_SUPPORT_WINMM - #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ - #endif -#endif -#if defined(MA_UNIX) - #if defined(MA_LINUX) - #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */ - #define MA_SUPPORT_ALSA - #endif - #endif - #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN) - #define MA_SUPPORT_PULSEAUDIO - #define MA_SUPPORT_JACK - #endif - #if defined(MA_ANDROID) - #define MA_SUPPORT_AAUDIO - #define MA_SUPPORT_OPENSL - #endif - #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ - #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */ - #endif - #if defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */ - #endif - #if defined(__FreeBSD__) || defined(__DragonFly__) - #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ - #endif -#endif -#if defined(MA_APPLE) - #define MA_SUPPORT_COREAUDIO -#endif -#if defined(MA_EMSCRIPTEN) - #define MA_SUPPORT_WEBAUDIO -#endif - -/* All platforms should support custom backends. */ -#define MA_SUPPORT_CUSTOM - -/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */ -#if !defined(MA_EMSCRIPTEN) -#define MA_SUPPORT_NULL -#endif - - -#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI)) - #define MA_HAS_WASAPI -#endif -#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND)) - #define MA_HAS_DSOUND -#endif -#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM)) - #define MA_HAS_WINMM -#endif -#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) - #define MA_HAS_ALSA -#endif -#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) - #define MA_HAS_PULSEAUDIO -#endif -#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) - #define MA_HAS_JACK -#endif -#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO)) - #define MA_HAS_COREAUDIO -#endif -#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO)) - #define MA_HAS_SNDIO -#endif -#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4)) - #define MA_HAS_AUDIO4 -#endif -#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS)) - #define MA_HAS_OSS -#endif -#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO)) - #define MA_HAS_AAUDIO -#endif -#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL)) - #define MA_HAS_OPENSL -#endif -#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) - #define MA_HAS_WEBAUDIO -#endif -#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) - #define MA_HAS_CUSTOM -#endif -#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL)) - #define MA_HAS_NULL -#endif - -typedef enum -{ - ma_device_state_uninitialized = 0, - ma_device_state_stopped = 1, /* The device's default state after initialization. */ - ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */ - ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */ - ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ -} ma_device_state; - -#ifdef MA_SUPPORT_WASAPI -/* We need a IMMNotificationClient object for WASAPI. */ -typedef struct -{ - void* lpVtbl; - ma_uint32 counter; - ma_device* pDevice; -} ma_IMMNotificationClient; -#endif - -/* Backend enums must be in priority order. */ -typedef enum -{ - ma_backend_wasapi, - ma_backend_dsound, - ma_backend_winmm, - ma_backend_coreaudio, - ma_backend_sndio, - ma_backend_audio4, - ma_backend_oss, - ma_backend_pulseaudio, - ma_backend_alsa, - ma_backend_jack, - ma_backend_aaudio, - ma_backend_opensl, - ma_backend_webaudio, - ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */ - ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */ -} ma_backend; - -#define MA_BACKEND_COUNT (ma_backend_null+1) - - -/* -Device job thread. This is used by backends that require asynchronous processing of certain -operations. It is not used by all backends. - -The device job thread is made up of a thread and a job queue. You can post a job to the thread with -ma_device_job_thread_post(). The thread will do the processing of the job. -*/ -typedef struct -{ - ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */ - ma_uint32 jobQueueCapacity; - ma_uint32 jobQueueFlags; -} ma_device_job_thread_config; - -MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void); - -typedef struct -{ - ma_thread thread; - ma_job_queue jobQueue; - ma_bool32 _hasThread; -} ma_device_job_thread; - -MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread); -MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob); -MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob); - - - -/* Device notification types. */ -typedef enum -{ - ma_device_notification_type_started, - ma_device_notification_type_stopped, - ma_device_notification_type_rerouted, - ma_device_notification_type_interruption_began, - ma_device_notification_type_interruption_ended -} ma_device_notification_type; - -typedef struct -{ - ma_device* pDevice; - ma_device_notification_type type; - union - { - struct - { - int _unused; - } started; - struct - { - int _unused; - } stopped; - struct - { - int _unused; - } rerouted; - struct - { - int _unused; - } interruption; - } data; -} ma_device_notification; - -/* -The notification callback for when the application should be notified of a change to the device. - -This callback is used for notifying the application of changes such as when the device has started, -stopped, rerouted or an interruption has occurred. Note that not all backends will post all -notification types. For example, some backends will perform automatic stream routing without any -kind of notification to the host program which means miniaudio will never know about it and will -never be able to fire the rerouted notification. You should keep this in mind when designing your -program. - -The stopped notification will *not* get fired when a device is rerouted. - - -Parameters ----------- -pNotification (in) - A pointer to a structure containing information about the event. Use the `pDevice` member of - this object to retrieve the relevant device. The `type` member can be used to discriminate - against each of the notification types. - - -Remarks -------- -Do not restart or uninitialize the device from the callback. - -Not all notifications will be triggered by all backends, however the started and stopped events -should be reliable for all backends. Some backends do not have a good way to detect device -stoppages due to unplugging the device which may result in the stopped callback not getting -fired. This has been observed with at least one BSD variant. - -The rerouted notification is fired *after* the reroute has occurred. The stopped notification will -*not* get fired when a device is rerouted. The following backends are known to do automatic stream -rerouting, but do not have a way to be notified of the change: - - * DirectSound - -The interruption notifications are used on mobile platforms for detecting when audio is interrupted -due to things like an incoming phone call. Currently this is only implemented on iOS. None of the -Android backends will report this notification. -*/ -typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification); - - -/* -The callback for processing audio data from the device. - -The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data -available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the -callback will be fired with a consistent frame count. - - -Parameters ----------- -pDevice (in) - A pointer to the relevant device. - -pOutput (out) - A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or - full-duplex device and null for a capture and loopback device. - -pInput (in) - A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a - playback device. - -frameCount (in) - The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The - `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must - not assume this will always be the same value each time the callback is fired. - - -Remarks -------- -You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the -callback. The following APIs cannot be called from inside the callback: - - ma_device_init() - ma_device_init_ex() - ma_device_uninit() - ma_device_start() - ma_device_stop() - -The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. -*/ -typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - - - -/* -DEPRECATED. Use ma_device_notification_proc instead. - -The callback for when the device has been stopped. - -This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces -such as being unplugged or an internal error occuring. - - -Parameters ----------- -pDevice (in) - A pointer to the device that has just stopped. - - -Remarks -------- -Do not restart or uninitialize the device from the callback. -*/ -typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */ - -typedef enum -{ - ma_device_type_playback = 1, - ma_device_type_capture = 2, - ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */ - ma_device_type_loopback = 4 -} ma_device_type; - -typedef enum -{ - ma_share_mode_shared = 0, - ma_share_mode_exclusive -} ma_share_mode; - -/* iOS/tvOS/watchOS session categories. */ -typedef enum -{ - ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord with AVAudioSessionCategoryOptionDefaultToSpeaker. */ - ma_ios_session_category_none, /* Leave the session category unchanged. */ - ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ - ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ - ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ - ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ - ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ - ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ -} ma_ios_session_category; - -/* iOS/tvOS/watchOS session category options */ -typedef enum -{ - ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ - ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ - ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ - ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ - ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ - ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ - ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ -} ma_ios_session_category_option; - -/* OpenSL stream types. */ -typedef enum -{ - ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ - ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ - ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ - ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ - ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ - ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ - ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ -} ma_opensl_stream_type; - -/* OpenSL recording presets. */ -typedef enum -{ - ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ - ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ - ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ - ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ - ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ - ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ -} ma_opensl_recording_preset; - -/* AAudio usage types. */ -typedef enum -{ - ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ - ma_aaudio_usage_announcement, /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ - ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ - ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ - ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ - ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ - ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ - ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ - ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ - ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ - ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ - ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ - ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ - ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ - ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ - ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ - ma_aaudio_usage_voice_communication_signalling /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ -} ma_aaudio_usage; - -/* AAudio content types. */ -typedef enum -{ - ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ - ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ - ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ - ma_aaudio_content_type_sonification, /* AAUDIO_CONTENT_TYPE_SONIFICATION */ - ma_aaudio_content_type_speech /* AAUDIO_CONTENT_TYPE_SPEECH */ -} ma_aaudio_content_type; - -/* AAudio input presets. */ -typedef enum -{ - ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ - ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ - ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ - ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ - ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ - ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ - ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ -} ma_aaudio_input_preset; - - -typedef union -{ - ma_int64 counter; - double counterD; -} ma_timer; - -typedef union -{ - wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ - ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ - /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ - char alsa[256]; /* ALSA uses a name string for identification. */ - char pulse[256]; /* PulseAudio uses a name string for identification. */ - int jack; /* JACK always uses default devices. */ - char coreaudio[256]; /* Core Audio uses a string for identification. */ - char sndio[256]; /* "snd/0", etc. */ - char audio4[256]; /* "/dev/audio", etc. */ - char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */ - ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ - ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ - char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ - union - { - int i; - char s[256]; - void* p; - } custom; /* The custom backend could be anything. Give them a few options. */ - int nullbackend; /* The null backend uses an integer for device IDs. */ -} ma_device_id; - - -typedef struct ma_context_config ma_context_config; -typedef struct ma_device_config ma_device_config; -typedef struct ma_backend_callbacks ma_backend_callbacks; - -#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ - -#ifndef MA_MAX_DEVICE_NAME_LENGTH -#define MA_MAX_DEVICE_NAME_LENGTH 255 -#endif - -typedef struct -{ - /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ - ma_device_id id; - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ - ma_bool32 isDefault; - - ma_uint32 nativeDataFormatCount; - struct - { - ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ - ma_uint32 channels; /* If set to 0, all channels are supported. */ - ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */ - ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */ - } nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64]; /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */ -} ma_device_info; - -struct ma_device_config -{ - ma_device_type deviceType; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periods; - ma_performance_profile performanceProfile; - ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ - ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */ - ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ - ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ - ma_device_data_proc dataCallback; - ma_device_notification_proc notificationCallback; - ma_stop_proc stopCallback; - void* pUserData; - ma_resampler_config resampling; - struct - { - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_share_mode shareMode; - } playback; - struct - { - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_share_mode shareMode; - } capture; - - struct - { - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ - ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ - } wasapi; - struct - { - ma_bool32 noMMap; /* Disables MMap mode. */ - ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ - ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ - ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ - } alsa; - struct - { - const char* pStreamNamePlayback; - const char* pStreamNameCapture; - } pulse; - struct - { - ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ - } coreaudio; - struct - { - ma_opensl_stream_type streamType; - ma_opensl_recording_preset recordingPreset; - } opensl; - struct - { - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_bool32 noAutoStartAfterReroute; - } aaudio; -}; - - -/* -The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -deviceType (in) - The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`. - -pInfo (in) - A pointer to a `ma_device_info` containing the ID and name of the enumerated device. Note that this will not include detailed information about the device, - only basic information (ID and name). The reason for this is that it would otherwise require opening the backend device to probe for the information which - is too inefficient. - -pUserData (in) - The user data pointer passed into `ma_context_enumerate_devices()`. -*/ -typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData); - - -/* -Describes some basic details about a playback or capture device. -*/ -typedef struct -{ - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periodCount; -} ma_device_descriptor; - -/* -These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context -to many devices. A device is created from a context. - -The general flow goes like this: - - 1) A context is created with `onContextInit()` - 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. - 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. - 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was - selected from device enumeration via `onContextEnumerateDevices()`. - 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()` - 4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call - to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by - miniaudio internally. - -Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the -callbacks defined in this structure. - -Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which -physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the -given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration -needs to stop and the `onContextEnumerateDevices()` function returns with a success code. - -Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, -and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the -case when the device ID is NULL, in which case information about the default device needs to be retrieved. - -Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. -This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a -device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, -the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to -the requested format. The conversion between the format requested by the application and the device's native format will be handled -internally by miniaudio. - -On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's -supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for -sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to -`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should -inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period -size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the -sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_data_format` -object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). - -Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses -asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented. - -The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit -easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and -`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the -backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback. -This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. - -If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback -which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. - -The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been -encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. - -The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this -callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated -which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, -look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to -wake up the audio thread. - -If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the -`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. -*/ -struct ma_backend_callbacks -{ - ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks); - ma_result (* onContextUninit)(ma_context* pContext); - ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); - ma_result (* onDeviceUninit)(ma_device* pDevice); - ma_result (* onDeviceStart)(ma_device* pDevice); - ma_result (* onDeviceStop)(ma_device* pDevice); - ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); - ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); - ma_result (* onDeviceDataLoop)(ma_device* pDevice); - ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); - ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); -}; - -struct ma_context_config -{ - ma_log* pLog; - ma_thread_priority threadPriority; - size_t threadStackSize; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - struct - { - ma_bool32 useVerboseDeviceEnumeration; - } alsa; - struct - { - const char* pApplicationName; - const char* pServerName; - ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ - } pulse; - struct - { - ma_ios_session_category sessionCategory; - ma_uint32 sessionCategoryOptions; - ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ - ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ - } coreaudio; - struct - { - const char* pClientName; - ma_bool32 tryStartServer; - } jack; - ma_backend_callbacks custom; -}; - -/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ -typedef struct -{ - int code; - ma_event* pEvent; /* This will be signalled when the event is complete. */ - union - { - struct - { - int _unused; - } quit; - struct - { - ma_device_type deviceType; - void* pAudioClient; - void** ppAudioClientService; - ma_result* pResult; /* The result from creating the audio client service. */ - } createAudioClient; - struct - { - ma_device* pDevice; - ma_device_type deviceType; - } releaseAudioClient; - } data; -} ma_context_command__wasapi; - -struct ma_context -{ - ma_backend_callbacks callbacks; - ma_backend backend; /* DirectSound, ALSA, etc. */ - ma_log* pLog; - ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ - ma_thread_priority threadPriority; - size_t threadStackSize; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ - ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ - ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ - ma_uint32 playbackDeviceInfoCount; - ma_uint32 captureDeviceInfoCount; - ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - ma_thread commandThread; - ma_mutex commandLock; - ma_semaphore commandSem; - ma_uint32 commandIndex; - ma_uint32 commandCount; - ma_context_command__wasapi commands[4]; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - ma_handle hDSoundDLL; - ma_proc DirectSoundCreate; - ma_proc DirectSoundEnumerateA; - ma_proc DirectSoundCaptureCreate; - ma_proc DirectSoundCaptureEnumerateA; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - ma_handle hWinMM; - ma_proc waveOutGetNumDevs; - ma_proc waveOutGetDevCapsA; - ma_proc waveOutOpen; - ma_proc waveOutClose; - ma_proc waveOutPrepareHeader; - ma_proc waveOutUnprepareHeader; - ma_proc waveOutWrite; - ma_proc waveOutReset; - ma_proc waveInGetNumDevs; - ma_proc waveInGetDevCapsA; - ma_proc waveInOpen; - ma_proc waveInClose; - ma_proc waveInPrepareHeader; - ma_proc waveInUnprepareHeader; - ma_proc waveInAddBuffer; - ma_proc waveInStart; - ma_proc waveInReset; - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - ma_handle asoundSO; - ma_proc snd_pcm_open; - ma_proc snd_pcm_close; - ma_proc snd_pcm_hw_params_sizeof; - ma_proc snd_pcm_hw_params_any; - ma_proc snd_pcm_hw_params_set_format; - ma_proc snd_pcm_hw_params_set_format_first; - ma_proc snd_pcm_hw_params_get_format_mask; - ma_proc snd_pcm_hw_params_set_channels; - ma_proc snd_pcm_hw_params_set_channels_near; - ma_proc snd_pcm_hw_params_set_channels_minmax; - ma_proc snd_pcm_hw_params_set_rate_resample; - ma_proc snd_pcm_hw_params_set_rate; - ma_proc snd_pcm_hw_params_set_rate_near; - ma_proc snd_pcm_hw_params_set_buffer_size_near; - ma_proc snd_pcm_hw_params_set_periods_near; - ma_proc snd_pcm_hw_params_set_access; - ma_proc snd_pcm_hw_params_get_format; - ma_proc snd_pcm_hw_params_get_channels; - ma_proc snd_pcm_hw_params_get_channels_min; - ma_proc snd_pcm_hw_params_get_channels_max; - ma_proc snd_pcm_hw_params_get_rate; - ma_proc snd_pcm_hw_params_get_rate_min; - ma_proc snd_pcm_hw_params_get_rate_max; - ma_proc snd_pcm_hw_params_get_buffer_size; - ma_proc snd_pcm_hw_params_get_periods; - ma_proc snd_pcm_hw_params_get_access; - ma_proc snd_pcm_hw_params_test_format; - ma_proc snd_pcm_hw_params_test_channels; - ma_proc snd_pcm_hw_params_test_rate; - ma_proc snd_pcm_hw_params; - ma_proc snd_pcm_sw_params_sizeof; - ma_proc snd_pcm_sw_params_current; - ma_proc snd_pcm_sw_params_get_boundary; - ma_proc snd_pcm_sw_params_set_avail_min; - ma_proc snd_pcm_sw_params_set_start_threshold; - ma_proc snd_pcm_sw_params_set_stop_threshold; - ma_proc snd_pcm_sw_params; - ma_proc snd_pcm_format_mask_sizeof; - ma_proc snd_pcm_format_mask_test; - ma_proc snd_pcm_get_chmap; - ma_proc snd_pcm_state; - ma_proc snd_pcm_prepare; - ma_proc snd_pcm_start; - ma_proc snd_pcm_drop; - ma_proc snd_pcm_drain; - ma_proc snd_pcm_reset; - ma_proc snd_device_name_hint; - ma_proc snd_device_name_get_hint; - ma_proc snd_card_get_index; - ma_proc snd_device_name_free_hint; - ma_proc snd_pcm_mmap_begin; - ma_proc snd_pcm_mmap_commit; - ma_proc snd_pcm_recover; - ma_proc snd_pcm_readi; - ma_proc snd_pcm_writei; - ma_proc snd_pcm_avail; - ma_proc snd_pcm_avail_update; - ma_proc snd_pcm_wait; - ma_proc snd_pcm_nonblock; - ma_proc snd_pcm_info; - ma_proc snd_pcm_info_sizeof; - ma_proc snd_pcm_info_get_name; - ma_proc snd_pcm_poll_descriptors; - ma_proc snd_pcm_poll_descriptors_count; - ma_proc snd_pcm_poll_descriptors_revents; - ma_proc snd_config_update_free_global; - - ma_mutex internalDeviceEnumLock; - ma_bool32 useVerboseDeviceEnumeration; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - ma_handle pulseSO; - ma_proc pa_mainloop_new; - ma_proc pa_mainloop_free; - ma_proc pa_mainloop_quit; - ma_proc pa_mainloop_get_api; - ma_proc pa_mainloop_iterate; - ma_proc pa_mainloop_wakeup; - ma_proc pa_threaded_mainloop_new; - ma_proc pa_threaded_mainloop_free; - ma_proc pa_threaded_mainloop_start; - ma_proc pa_threaded_mainloop_stop; - ma_proc pa_threaded_mainloop_lock; - ma_proc pa_threaded_mainloop_unlock; - ma_proc pa_threaded_mainloop_wait; - ma_proc pa_threaded_mainloop_signal; - ma_proc pa_threaded_mainloop_accept; - ma_proc pa_threaded_mainloop_get_retval; - ma_proc pa_threaded_mainloop_get_api; - ma_proc pa_threaded_mainloop_in_thread; - ma_proc pa_threaded_mainloop_set_name; - ma_proc pa_context_new; - ma_proc pa_context_unref; - ma_proc pa_context_connect; - ma_proc pa_context_disconnect; - ma_proc pa_context_set_state_callback; - ma_proc pa_context_get_state; - ma_proc pa_context_get_sink_info_list; - ma_proc pa_context_get_source_info_list; - ma_proc pa_context_get_sink_info_by_name; - ma_proc pa_context_get_source_info_by_name; - ma_proc pa_operation_unref; - ma_proc pa_operation_get_state; - ma_proc pa_channel_map_init_extend; - ma_proc pa_channel_map_valid; - ma_proc pa_channel_map_compatible; - ma_proc pa_stream_new; - ma_proc pa_stream_unref; - ma_proc pa_stream_connect_playback; - ma_proc pa_stream_connect_record; - ma_proc pa_stream_disconnect; - ma_proc pa_stream_get_state; - ma_proc pa_stream_get_sample_spec; - ma_proc pa_stream_get_channel_map; - ma_proc pa_stream_get_buffer_attr; - ma_proc pa_stream_set_buffer_attr; - ma_proc pa_stream_get_device_name; - ma_proc pa_stream_set_write_callback; - ma_proc pa_stream_set_read_callback; - ma_proc pa_stream_set_suspended_callback; - ma_proc pa_stream_set_moved_callback; - ma_proc pa_stream_is_suspended; - ma_proc pa_stream_flush; - ma_proc pa_stream_drain; - ma_proc pa_stream_is_corked; - ma_proc pa_stream_cork; - ma_proc pa_stream_trigger; - ma_proc pa_stream_begin_write; - ma_proc pa_stream_write; - ma_proc pa_stream_peek; - ma_proc pa_stream_drop; - ma_proc pa_stream_writable_size; - ma_proc pa_stream_readable_size; - - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - ma_handle jackSO; - ma_proc jack_client_open; - ma_proc jack_client_close; - ma_proc jack_client_name_size; - ma_proc jack_set_process_callback; - ma_proc jack_set_buffer_size_callback; - ma_proc jack_on_shutdown; - ma_proc jack_get_sample_rate; - ma_proc jack_get_buffer_size; - ma_proc jack_get_ports; - ma_proc jack_activate; - ma_proc jack_deactivate; - ma_proc jack_connect; - ma_proc jack_port_register; - ma_proc jack_port_name; - ma_proc jack_port_get_buffer; - ma_proc jack_free; - - char* pClientName; - ma_bool32 tryStartServer; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_handle hCoreFoundation; - ma_proc CFStringGetCString; - ma_proc CFRelease; - - ma_handle hCoreAudio; - ma_proc AudioObjectGetPropertyData; - ma_proc AudioObjectGetPropertyDataSize; - ma_proc AudioObjectSetPropertyData; - ma_proc AudioObjectAddPropertyListener; - ma_proc AudioObjectRemovePropertyListener; - - ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ - ma_proc AudioComponentFindNext; - ma_proc AudioComponentInstanceDispose; - ma_proc AudioComponentInstanceNew; - ma_proc AudioOutputUnitStart; - ma_proc AudioOutputUnitStop; - ma_proc AudioUnitAddPropertyListener; - ma_proc AudioUnitGetPropertyInfo; - ma_proc AudioUnitGetProperty; - ma_proc AudioUnitSetProperty; - ma_proc AudioUnitInitialize; - ma_proc AudioUnitRender; - - /*AudioComponent*/ ma_ptr component; - ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_handle sndioSO; - ma_proc sio_open; - ma_proc sio_close; - ma_proc sio_setpar; - ma_proc sio_getpar; - ma_proc sio_getcap; - ma_proc sio_start; - ma_proc sio_stop; - ma_proc sio_read; - ma_proc sio_write; - ma_proc sio_onmove; - ma_proc sio_nfds; - ma_proc sio_pollfd; - ma_proc sio_revents; - ma_proc sio_eof; - ma_proc sio_setvol; - ma_proc sio_onvol; - ma_proc sio_initpar; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int _unused; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int versionMajor; - int versionMinor; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - ma_handle hAAudio; /* libaaudio.so */ - ma_proc AAudio_createStreamBuilder; - ma_proc AAudioStreamBuilder_delete; - ma_proc AAudioStreamBuilder_setDeviceId; - ma_proc AAudioStreamBuilder_setDirection; - ma_proc AAudioStreamBuilder_setSharingMode; - ma_proc AAudioStreamBuilder_setFormat; - ma_proc AAudioStreamBuilder_setChannelCount; - ma_proc AAudioStreamBuilder_setSampleRate; - ma_proc AAudioStreamBuilder_setBufferCapacityInFrames; - ma_proc AAudioStreamBuilder_setFramesPerDataCallback; - ma_proc AAudioStreamBuilder_setDataCallback; - ma_proc AAudioStreamBuilder_setErrorCallback; - ma_proc AAudioStreamBuilder_setPerformanceMode; - ma_proc AAudioStreamBuilder_setUsage; - ma_proc AAudioStreamBuilder_setContentType; - ma_proc AAudioStreamBuilder_setInputPreset; - ma_proc AAudioStreamBuilder_openStream; - ma_proc AAudioStream_close; - ma_proc AAudioStream_getState; - ma_proc AAudioStream_waitForStateChange; - ma_proc AAudioStream_getFormat; - ma_proc AAudioStream_getChannelCount; - ma_proc AAudioStream_getSampleRate; - ma_proc AAudioStream_getBufferCapacityInFrames; - ma_proc AAudioStream_getFramesPerDataCallback; - ma_proc AAudioStream_getFramesPerBurst; - ma_proc AAudioStream_requestStart; - ma_proc AAudioStream_requestStop; - ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - ma_handle libOpenSLES; - ma_handle SL_IID_ENGINE; - ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; - ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - ma_handle SL_IID_RECORD; - ma_handle SL_IID_PLAY; - ma_handle SL_IID_OUTPUTMIX; - ma_handle SL_IID_ANDROIDCONFIGURATION; - ma_proc slCreateEngine; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - int _unused; - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - int _unused; - } null_backend; -#endif - }; - - union - { -#ifdef MA_WIN32 - struct - { - /*HMODULE*/ ma_handle hOle32DLL; - ma_proc CoInitializeEx; - ma_proc CoUninitialize; - ma_proc CoCreateInstance; - ma_proc CoTaskMemFree; - ma_proc PropVariantClear; - ma_proc StringFromGUID2; - - /*HMODULE*/ ma_handle hUser32DLL; - ma_proc GetForegroundWindow; - ma_proc GetDesktopWindow; - - /*HMODULE*/ ma_handle hAdvapi32DLL; - ma_proc RegOpenKeyExA; - ma_proc RegCloseKey; - ma_proc RegQueryValueExA; - } win32; -#endif -#ifdef MA_POSIX - struct - { - ma_handle pthreadSO; - ma_proc pthread_create; - ma_proc pthread_join; - ma_proc pthread_mutex_init; - ma_proc pthread_mutex_destroy; - ma_proc pthread_mutex_lock; - ma_proc pthread_mutex_unlock; - ma_proc pthread_cond_init; - ma_proc pthread_cond_destroy; - ma_proc pthread_cond_wait; - ma_proc pthread_cond_signal; - ma_proc pthread_attr_init; - ma_proc pthread_attr_destroy; - ma_proc pthread_attr_setschedpolicy; - ma_proc pthread_attr_getschedparam; - ma_proc pthread_attr_setschedparam; - } posix; -#endif - int _unused; - }; -}; - -struct ma_device -{ - ma_context* pContext; - ma_device_type type; - ma_uint32 sampleRate; - MA_ATOMIC(4, ma_device_state) state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ - ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ - ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ - ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ - void* pUserData; /* Application defined data. */ - ma_mutex startStopLock; - ma_event wakeupEvent; - ma_event startEvent; - ma_event stopEvent; - ma_thread thread; - ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ - ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ - ma_bool8 noPreSilencedOutputBuffer; - ma_bool8 noClip; - ma_bool8 noDisableDenormals; - ma_bool8 noFixedSizedCallback; - MA_ATOMIC(4, float) masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ - ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ - struct - { - ma_resample_algorithm algorithm; - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - struct - { - ma_uint32 lpfOrder; - } linear; - } resampling; - struct - { - ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ - ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ - ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_format format; - ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - ma_channel_mix_mode channelMixMode; - ma_data_converter converter; - void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ - ma_uint32 intermediaryBufferCap; - ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ - void* pInputCache; /* In external format. Can be null. */ - ma_uint64 inputCacheCap; - ma_uint64 inputCacheConsumed; - ma_uint64 inputCacheRemaining; - } playback; - struct - { - ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ - ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ - ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_format format; - ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - ma_channel_mix_mode channelMixMode; - ma_data_converter converter; - void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ - ma_uint32 intermediaryBufferCap; - ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ - } capture; - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - /*IAudioClient**/ ma_ptr pAudioClientPlayback; - /*IAudioClient**/ ma_ptr pAudioClientCapture; - /*IAudioRenderClient**/ ma_ptr pRenderClient; - /*IAudioCaptureClient**/ ma_ptr pCaptureClient; - /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ - ma_IMMNotificationClient notificationClient; - /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ - /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ - ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ - ma_uint32 actualBufferSizeInFramesCapture; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_uint32 periodSizeInFramesPlayback; - ma_uint32 periodSizeInFramesCapture; - void* pMappedBufferCapture; - ma_uint32 mappedBufferCaptureCap; - ma_uint32 mappedBufferCaptureLen; - void* pMappedBufferPlayback; - ma_uint32 mappedBufferPlaybackCap; - ma_uint32 mappedBufferPlaybackLen; - MA_ATOMIC(4, ma_bool32) isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - MA_ATOMIC(4, ma_bool32) isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noHardwareOffloading; - ma_bool8 allowCaptureAutoStreamRouting; - ma_bool8 allowPlaybackAutoStreamRouting; - ma_bool8 isDetachedPlayback; - ma_bool8 isDetachedCapture; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - /*LPDIRECTSOUND*/ ma_ptr pPlayback; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer; - /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture; - /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - /*HWAVEOUT*/ ma_handle hDevicePlayback; - /*HWAVEIN*/ ma_handle hDeviceCapture; - /*HANDLE*/ ma_handle hEventPlayback; - /*HANDLE*/ ma_handle hEventCapture; - ma_uint32 fragmentSizeInFrames; - ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ - ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ - ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ - ma_uint32 headerFramesConsumedCapture; /* ^^^ */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */ - ma_uint8* pIntermediaryBufferPlayback; - ma_uint8* pIntermediaryBufferCapture; - ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - /*snd_pcm_t**/ ma_ptr pPCMPlayback; - /*snd_pcm_t**/ ma_ptr pPCMCapture; - /*struct pollfd**/ void* pPollDescriptorsPlayback; - /*struct pollfd**/ void* pPollDescriptorsCapture; - int pollDescriptorCountPlayback; - int pollDescriptorCountCapture; - int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ - int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ - ma_bool8 isUsingMMapPlayback; - ma_bool8 isUsingMMapCapture; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - /*pa_stream**/ ma_ptr pStreamPlayback; - /*pa_stream**/ ma_ptr pStreamCapture; - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - /*jack_client_t**/ ma_ptr pClient; - /*jack_port_t**/ ma_ptr* ppPortsPlayback; - /*jack_port_t**/ ma_ptr* ppPortsCapture; - float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ - float* pIntermediaryBufferCapture; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_uint32 deviceObjectIDPlayback; - ma_uint32 deviceObjectIDCapture; - /*AudioUnit*/ ma_ptr audioUnitPlayback; - /*AudioUnit*/ ma_ptr audioUnitCapture; - /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */ - ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ - ma_event stopEvent; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_bool32 isDefaultPlaybackDevice; - ma_bool32 isDefaultCaptureDevice; - ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_ptr handlePlayback; - ma_ptr handleCapture; - ma_bool32 isStartedPlayback; - ma_bool32 isStartedCapture; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int fdPlayback; - int fdCapture; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int fdPlayback; - int fdCapture; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - /*AAudioStream**/ ma_ptr pStreamPlayback; - /*AAudioStream**/ ma_ptr pStreamCapture; - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_bool32 noAutoStartAfterReroute; - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - /*SLObjectItf*/ ma_ptr pOutputMixObj; - /*SLOutputMixItf*/ ma_ptr pOutputMix; - /*SLObjectItf*/ ma_ptr pAudioPlayerObj; - /*SLPlayItf*/ ma_ptr pAudioPlayer; - /*SLObjectItf*/ ma_ptr pAudioRecorderObj; - /*SLRecordItf*/ ma_ptr pAudioRecorder; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture; - ma_bool32 isDrainingCapture; - ma_bool32 isDrainingPlayback; - ma_uint32 currentBufferIndexPlayback; - ma_uint32 currentBufferIndexCapture; - ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ - ma_uint8* pBufferCapture; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */ - int indexCapture; - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - ma_thread deviceThread; - ma_event operationEvent; - ma_event operationCompletionEvent; - ma_semaphore operationSemaphore; - ma_uint32 operation; - ma_result operationResult; - ma_timer timer; - double priorRunTime; - ma_uint32 currentPeriodFramesRemainingPlayback; - ma_uint32 currentPeriodFramesRemainingCapture; - ma_uint64 lastProcessedFramePlayback; - ma_uint64 lastProcessedFrameCapture; - MA_ATOMIC(4, ma_bool32) isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ - } null_device; -#endif - }; -}; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ -#endif - -/* -Initializes a `ma_context_config` object. - - -Return Value ------------- -A `ma_context_config` initialized to defaults. - - -Remarks -------- -You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio -is updated and new members are added to `ma_context_config`. It also sets logical defaults. - -You can override members of the returned object by changing it's members directly. - - -See Also --------- -ma_context_init() -*/ -MA_API ma_context_config ma_context_config_init(void); - -/* -Initializes a context. - -The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual -device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices. - - -Parameters ----------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. - -backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. - -pConfig (in, optional) - The context configuration. - -pContext (in) - A pointer to the context object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Do not call this function across multiple threads as some backends read and write to global state. - - -Remarks -------- -When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: - - |-------------|-----------------------|--------------------------------------------------------| - | Name | Enum Name | Supported Operating Systems | - |-------------|-----------------------|--------------------------------------------------------| - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | ALSA | ma_backend_alsa | Linux | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Null | ma_backend_null | Cross Platform (not used on Web) | - |-------------|-----------------------|--------------------------------------------------------| - -The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings -can then be set directly on the structure. Below are the members of the `ma_context_config` object. - - pLog - A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not - require logging. See the `ma_log` API for details on how to use the logging system. - - threadPriority - The desired priority to use for the audio thread. Allowable values include the following: - - |--------------------------------------| - | Thread Priority | - |--------------------------------------| - | ma_thread_priority_idle | - | ma_thread_priority_lowest | - | ma_thread_priority_low | - | ma_thread_priority_normal | - | ma_thread_priority_high | - | ma_thread_priority_highest (default) | - | ma_thread_priority_realtime | - | ma_thread_priority_default | - |--------------------------------------| - - threadStackSize - The desired size of the stack for the audio thread. Defaults to the operating system's default. - - pUserData - A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. - - allocationCallbacks - Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation - callbacks will be used for anything tied to the context, including devices. - - alsa.useVerboseDeviceEnumeration - ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique - card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes - it so the ALSA backend includes all devices. Defaults to false. - - pulse.pApplicationName - PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. - - pulse.pServerName - PulseAudio only. The name of the server to connect to with `pa_context_connect()`. - - pulse.tryAutoSpawn - PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that - miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be - intrusive for the end user. - - coreaudio.sessionCategory - iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - - |-----------------------------------------|-------------------------------------| - | miniaudio Token | Core Audio Token | - |-----------------------------------------|-------------------------------------| - | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | - | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | - | ma_ios_session_category_record | AVAudioSessionCategoryRecord | - | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | - | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | - | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | - |-----------------------------------------|-------------------------------------| - - coreaudio.sessionCategoryOptions - iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | miniaudio Token | Core Audio Token | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | - | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | - | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | - | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | - | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | - | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | - | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - - coreaudio.noAudioSessionActivate - iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. - - coreaudio.noAudioSessionDeactivate - iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. - - jack.pClientName - The name of the client to pass to `jack_client_open()`. - - jack.tryStartServer - Whether or not to try auto-starting the JACK server. Defaults to false. - - -It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the -relevant backends every time it's initialized. - -The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The -reason for this is that a pointer to the context is stored in the `ma_device` structure. - - -Example 1 - Default Initialization ----------------------------------- -The example below shows how to initialize the context using the default configuration. - -```c -ma_context context; -ma_result result = ma_context_init(NULL, 0, NULL, &context); -if (result != MA_SUCCESS) { - // Error. -} -``` - - -Example 2 - Custom Configuration --------------------------------- -The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program -wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also -want an error to be returned if no valid backend is available which they achieve by excluding the Null backend. - -For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface. - -```c -ma_backend backends[] = { - ma_backend_alsa, - ma_backend_pulseaudio, - ma_backend_wasapi, - ma_backend_dsound -}; - -ma_log log; -ma_log_init(&log); -ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); - -ma_context_config config = ma_context_config_init(); -config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. - -ma_context context; -ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); -if (result != MA_SUCCESS) { - // Error. - if (result == MA_NO_BACKEND) { - // Couldn't find an appropriate backend. - } -} - -// You could also attach a log callback post-initialization: -ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); -``` - - -See Also --------- -ma_context_config_init() -ma_context_uninit() -*/ -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); - -/* -Uninitializes a context. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Do not call this function across multiple threads as some backends read and write to global state. - - -Remarks -------- -Results are undefined if you call this while any device created by this context is still active. - - -See Also --------- -ma_context_init() -*/ -MA_API ma_result ma_context_uninit(ma_context* pContext); - -/* -Retrieves the size of the ma_context object. - -This is mainly for the purpose of bindings to know how much memory to allocate. -*/ -MA_API size_t ma_context_sizeof(void); - -/* -Retrieves a pointer to the log object associated with this context. - - -Remarks -------- -Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log -message. - -You can attach your own logging callback to the log with `ma_log_register_callback()` - - -Return Value ------------- -A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs, -NULL will be returned. -*/ -MA_API ma_log* ma_context_get_log(ma_context* pContext); - -/* -Enumerates over every device (both playback and capture). - -This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur -an internal heap allocation, or it simply suits your code better. - -Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require -opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, -but don't call it from within the enumeration callback. - -Returning false from the callback will stop enumeration. Returning true will continue enumeration. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -callback (in) - The callback to fire for each enumerated device. - -pUserData (in) - A pointer to application-defined data passed to the callback. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. This is guarded using a simple mutex lock. - - -Remarks -------- -Do _not_ assume the first enumerated device of a given type is the default device. - -Some backends and platforms may only support default playback and capture devices. - -In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also, -do not try to call `ma_context_get_device_info()` from within the callback. - -Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation. - - -Example 1 - Simple Enumeration ------------------------------- -ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -{ - printf("Device Name: %s\n", pInfo->name); - return MA_TRUE; -} - -ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData); -if (result != MA_SUCCESS) { - // Error. -} - - -See Also --------- -ma_context_get_devices() -*/ -MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - -/* -Retrieves basic information about every active playback and/or capture device. - -This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` -parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -ppPlaybackDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. - -pPlaybackDeviceCount (out) - A pointer to an unsigned integer that will receive the number of playback devices. - -ppCaptureDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. - -pCaptureDeviceCount (out) - A pointer to an unsigned integer that will receive the number of capture devices. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple -threads. Instead, you need to make a copy of the returned data with your own higher level synchronization. - - -Remarks -------- -It is _not_ safe to assume the first device in the list is the default device. - -You can pass in NULL for the playback or capture lists in which case they'll be ignored. - -The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers. - - -See Also --------- -ma_context_get_devices() -*/ -MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); - -/* -Retrieves information about a device of the given type, with the specified ID and share mode. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the query. - -deviceType (in) - The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. - -pDeviceID (in) - The ID of the device being queried. - -pDeviceInfo (out) - A pointer to the `ma_device_info` structure that will receive the device information. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. This is guarded using a simple mutex lock. - - -Remarks -------- -Do _not_ call this from within the `ma_context_enumerate_devices()` callback. - -It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in -shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify -which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if -the requested share mode is unsupported. - -This leaves pDeviceInfo unmodified in the result of an error. -*/ -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - -/* -Determines if the given context supports loopback mode. - - -Parameters ----------- -pContext (in) - A pointer to the context getting queried. - - -Return Value ------------- -MA_TRUE if the context supports loopback mode; MA_FALSE otherwise. -*/ -MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext); - - - -/* -Initializes a device config with default settings. - - -Parameters ----------- -deviceType (in) - The type of the device this config is being initialized for. This must set to one of the following: - - |-------------------------| - | Device Type | - |-------------------------| - | ma_device_type_playback | - | ma_device_type_capture | - | ma_device_type_duplex | - | ma_device_type_loopback | - |-------------------------| - - -Return Value ------------- -A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks. - - -Thread Safety -------------- -Safe. - - -Callback Safety ---------------- -Safe, but don't try initializing a device in a callback. - - -Remarks -------- -The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a -typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change -before initializing the device. - -See `ma_device_init()` for details on specific configuration options. - - -Example 1 - Simple Configuration --------------------------------- -The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and -then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added -to the `ma_device_config` structure. - -```c -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pUserData = pMyUserData; -``` - - -See Also --------- -ma_device_init() -ma_device_init_ex() -*/ -MA_API ma_device_config ma_device_config_init(ma_device_type deviceType); - - -/* -Initializes a device. - -A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it -from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be -playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the -device is done via a callback which is fired by miniaudio at periodic time intervals. - -The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames -or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and -increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but -miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple -media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the -backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for. - -When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the -format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you -can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline. - - -Parameters ----------- -pContext (in, optional) - A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. - -pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. - -pDevice (out) - A pointer to the device object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to -calling this at the same time as `ma_device_uninit()`. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so: - - ```c - ma_context_init(NULL, 0, NULL, &context); - ``` - -Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use -device.pContext for the initialization of other devices. - -The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can -then be set directly on the structure. Below are the members of the `ma_device_config` object. - - deviceType - Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. - - sampleRate - The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. - - periodSizeInFrames - The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will - be used depending on the selected performance profile. This value affects latency. See below for details. - - periodSizeInMilliseconds - The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be - used depending on the selected performance profile. The value affects latency. See below for details. - - periods - The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by - this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. - - performanceProfile - A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or - `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value. - - noPreSilencedOutputBuffer - When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of - the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data - callback will write to every sample in the output buffer, or if you are doing your own clearing. - - noClip - When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. When set to false (default), the - contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or not the clip. This only - applies when the playback sample format is f32. - - noDisableDenormals - By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. - - noFixedSizedCallback - Allows miniaudio to fire the data callback with any frame count. When this is set to true, the data callback will be fired with a consistent frame - count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to false, miniaudio will fire the callback with whatever the - backend requests, which could be anything. - - dataCallback - The callback to fire whenever data is ready to be delivered to or from the device. - - notificationCallback - The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. - - pUserData - The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. - - resampling.algorithm - The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The - default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. - - resampling.pBackendVTable - A pointer to an optional vtable that can be used for plugging in a custom resampler. - - resampling.pBackendUserData - A pointer that will passed to callbacks in pBackendVTable. - - resampling.linear.lpfOrder - The linear resampler applies a low-pass filter as part of it's procesing for anti-aliasing. This setting controls the order of the filter. The higher - the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is - `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. - - playback.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's - default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - - playback.format - The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.playback.format`. - - playback.channels - The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.playback.channels`. - - playback.pChannelMap - The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. - - playback.shareMode - The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. - - capture.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's - default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - - capture.format - The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.capture.format`. - - capture.channels - The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.capture.channels`. - - capture.pChannelMap - The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. - - capture.shareMode - The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. - - wasapi.noAutoConvertSRC - WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. - - wasapi.noDefaultQualitySRC - WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. - You should usually leave this set to false, which is the default. - - wasapi.noAutoStreamRouting - WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. - - wasapi.noHardwareOffloading - WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. - - alsa.noMMap - ALSA only. When set to true, disables MMap mode. Defaults to false. - - alsa.noAutoFormat - ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. - - alsa.noAutoChannels - ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. - - alsa.noAutoResample - ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. - - pulse.pStreamNamePlayback - PulseAudio only. Sets the stream name for playback. - - pulse.pStreamNameCapture - PulseAudio only. Sets the stream name for capture. - - coreaudio.allowNominalSampleRateChange - Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This - is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate - that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will - find the closest match between the sample rate requested in the device config and the sample rates natively supported by the - hardware. When set to false, the sample rate currently set by the operating system will always be used. - - opensl.streamType - OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the - stream type will be left unset. Think of this as the type of audio you're playing. - - opensl.recordingPreset - OpenSL only. Explicitly sets the type of recording your program will be doing. When left - unset, the recording preset will be left unchanged. - - aaudio.usage - AAudio only. Explicitly sets the nature of the audio the program will be consuming. When - left unset, the usage will be left unchanged. - - aaudio.contentType - AAudio only. Sets the content type. When left unset, the content type will be left unchanged. - - aaudio.inputPreset - AAudio only. Explicitly sets the type of recording your program will be doing. When left - unset, the input preset will be left unchanged. - - aaudio.noAutoStartAfterReroute - AAudio only. Controls whether or not the device should be automatically restarted after a - stream reroute. When set to false (default) the device will be restarted automatically; - otherwise the device will be stopped. - - -Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. - -After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`. - -If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or -`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or -`ma_performance_profile_conservative`. - -If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device -in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the -config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA, -for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user. -Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary. - -When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config -and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run -on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`, -`playback/capture.channels` and `sampleRate` members of the device object. - -When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message -asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information. - -ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture. -If these fail it will try falling back to the "hw" device. - - -Example 1 - Simple Initialization ---------------------------------- -This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default -playback device this is usually all you need. - -```c -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pMyUserData = pMyUserData; - -ma_device device; -ma_result result = ma_device_init(NULL, &config, &device); -if (result != MA_SUCCESS) { - // Error -} -``` - - -Example 2 - Advanced Initialization ------------------------------------ -This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size -and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device -enumeration. - -```c -ma_context context; -ma_result result = ma_context_init(NULL, 0, NULL, &context); -if (result != MA_SUCCESS) { - // Error -} - -ma_device_info* pPlaybackDeviceInfos; -ma_uint32 playbackDeviceCount; -result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); -if (result != MA_SUCCESS) { - // Error -} - -// ... choose a device from pPlaybackDeviceInfos ... - -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices(). -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pUserData = pMyUserData; -config.periodSizeInMilliseconds = 10; -config.periods = 3; - -ma_device device; -result = ma_device_init(&context, &config, &device); -if (result != MA_SUCCESS) { - // Error -} -``` - - -See Also --------- -ma_device_config_init() -ma_device_uninit() -ma_device_start() -ma_context_init() -ma_context_get_devices() -ma_context_enumerate_devices() -*/ -MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice); - -/* -Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context. - -This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function -allows you to configure the internally created context. - - -Parameters ----------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. - -backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. - -pContextConfig (in, optional) - The context configuration. - -pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. - -pDevice (out) - A pointer to the device object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to -calling this at the same time as `ma_device_uninit()`. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage -your own context. - -See the documentation for `ma_context_init()` for information on the different context configuration options. - - -See Also --------- -ma_device_init() -ma_device_uninit() -ma_device_config_init() -ma_context_init() -*/ -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); - -/* -Uninitializes a device. - -This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do. - - -Parameters ----------- -pDevice (in) - A pointer to the device to stop. - - -Return Value ------------- -Nothing - - -Thread Safety -------------- -Unsafe. As soon as this API is called the device should be considered undefined. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. - - -See Also --------- -ma_device_init() -ma_device_stop() -*/ -MA_API void ma_device_uninit(ma_device* pDevice); - - -/* -Retrieves a pointer to the context that owns the given device. -*/ -MA_API ma_context* ma_device_get_context(ma_device* pDevice); - -/* -Helper function for retrieving the log object associated with the context that owns this device. -*/ -MA_API ma_log* ma_device_get_log(ma_device* pDevice); - - -/* -Retrieves information about the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose information is being retrieved. - -type (in) - The device type. This parameter is required for duplex devices. When retrieving device - information, you are doing so for an individual playback or capture device. - -pDeviceInfo (out) - A pointer to the `ma_device_info` that will receive the device information. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. This should be considered unsafe because it may be calling into the backend which may or -may not be safe. - - -Callback Safety ---------------- -Unsafe. You should avoid calling this in the data callback because it may call into the backend -which may or may not be safe. -*/ -MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); - - -/* -Retrieves the name of the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose information is being retrieved. - -type (in) - The device type. This parameter is required for duplex devices. When retrieving device - information, you are doing so for an individual playback or capture device. - -pName (out) - A pointer to the buffer that will receive the name. - -nameCap (in) - The capacity of the output buffer, including space for the null terminator. - -pLengthNotIncludingNullTerminator (out, optional) - A pointer to the variable that will receive the length of the name, not including the null - terminator. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. This should be considered unsafe because it may be calling into the backend which may or -may not be safe. - - -Callback Safety ---------------- -Unsafe. You should avoid calling this in the data callback because it may call into the backend -which may or may not be safe. - - -Remarks -------- -If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to -`pName` if you want to first get the length of the name for the purpose of memory allocation of the -output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for -most cases and will avoid the need for the inefficiency of calling this function twice. - -This is implemented in terms of `ma_device_get_info()`. -*/ -MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); - - -/* -Starts the device. For playback devices this begins playback. For capture devices it begins recording. - -Use `ma_device_stop()` to stop the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device to start. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. It's safe to call this from any thread with the exception of the callback thread. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid -audio data in the buffer, which needs to be done before the device begins playback. - -This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety. - -Do not call this in any callback. - - -See Also --------- -ma_device_stop() -*/ -MA_API ma_result ma_device_start(ma_device* pDevice); - -/* -Stops the device. For playback devices this stops playback. For capture devices it stops recording. - -Use `ma_device_start()` to start the device again. - - -Parameters ----------- -pDevice (in) - A pointer to the device to stop. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. It's safe to call this from any thread with the exception of the callback thread. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. - - -Remarks -------- -This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some -backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size -that was specified at initialization time). - -Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and -the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the -speakers or received from the microphone which can in turn result in de-syncs. - -Do not call this in any callback. - -This will be called implicitly by `ma_device_uninit()`. - - -See Also --------- -ma_device_start() -*/ -MA_API ma_result ma_device_stop(ma_device* pDevice); - -/* -Determines whether or not the device is started. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose start state is being retrieved. - - -Return Value ------------- -True if the device is started, false otherwise. - - -Thread Safety -------------- -Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return -value will be out of sync. - - -Callback Safety ---------------- -Safe. This is implemented as a simple accessor. - - -See Also --------- -ma_device_start() -ma_device_stop() -*/ -MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice); - - -/* -Retrieves the state of the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose state is being retrieved. - - -Return Value ------------- -The current state of the device. The return value will be one of the following: - - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_started | The device started and requesting and/or delivering audio data. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_starting | The device is in the process of starting. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_stopping | The device is in the process of stopping. | - +-------------------------------+------------------------------------------------------------------------------+ - - -Thread Safety -------------- -Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called, -there's a possibility the return value could be out of sync. See remarks. - - -Callback Safety ---------------- -Safe. This is implemented as a simple accessor. - - -Remarks -------- -The general flow of a devices state goes like this: - - ``` - ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped - ma_device_start() -> ma_device_state_starting -> ma_device_state_started - ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped - ``` - -When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the -value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own -synchronization. -*/ -MA_API ma_device_state ma_device_get_state(const ma_device* pDevice); - - -/* -Performs post backend initialization routines for setting up internal data conversion. - -This should be called whenever the backend is initialized. The only time this should be called from -outside of miniaudio is if you're implementing a custom backend, and you would only do it if you -are reinitializing the backend due to rerouting or reinitializing for some reason. - - -Parameters ----------- -pDevice [in] - A pointer to the device. - -deviceType [in] - The type of the device that was just reinitialized. - -pPlaybackDescriptor [in] - The descriptor of the playback device containing the internal data format and buffer sizes. - -pPlaybackDescriptor [in] - The descriptor of the capture device containing the internal data format and buffer sizes. - - -Return Value ------------- -MA_SUCCESS if successful; any other error otherwise. - - -Thread Safety -------------- -Unsafe. This will be reinitializing internal data converters which may be in use by another thread. - - -Callback Safety ---------------- -Unsafe. This will be reinitializing internal data converters which may be in use by the callback. - - -Remarks -------- -For a duplex device, you can call this for only one side of the system. This is why the deviceType -is specified as a parameter rather than deriving it from the device. - -You do not need to call this manually unless you are doing a custom backend, in which case you need -only do it if you're manually performing rerouting or reinitialization. -*/ -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); - - -/* -Sets the master volume factor for the device. - -The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and -values less than 0 decreases the volume. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose volume is being set. - -volume (in) - The new volume factor. Must be >= 0. - - -Return Value ------------- -MA_SUCCESS if the volume was set successfully. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if volume is negative. - - -Thread Safety -------------- -Safe. This just sets a local member of the device object. - - -Callback Safety ---------------- -Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. - - -Remarks -------- -This applies the volume factor across all channels. - -This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. - - -See Also --------- -ma_device_get_master_volume() -ma_device_set_master_volume_db() -ma_device_get_master_volume_db() -*/ -MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume); - -/* -Retrieves the master volume factor for the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose volume factor is being retrieved. - -pVolume (in) - A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if pVolume is NULL. - - -Thread Safety -------------- -Safe. This just a simple member retrieval. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If an error occurs, `*pVolume` will be set to 0. - - -See Also --------- -ma_device_set_master_volume() -ma_device_set_master_volume_gain_db() -ma_device_get_master_volume_gain_db() -*/ -MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume); - -/* -Sets the master volume for the device as gain in decibels. - -A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose gain is being set. - -gainDB (in) - The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. - - -Return Value ------------- -MA_SUCCESS if the volume was set successfully. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if the gain is > 0. - - -Thread Safety -------------- -Safe. This just sets a local member of the device object. - - -Callback Safety ---------------- -Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. - - -Remarks -------- -This applies the gain across all channels. - -This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. - - -See Also --------- -ma_device_get_master_volume_gain_db() -ma_device_set_master_volume() -ma_device_get_master_volume() -*/ -MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB); - -/* -Retrieves the master gain in decibels. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose gain is being retrieved. - -pGainDB (in) - A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if pGainDB is NULL. - - -Thread Safety -------------- -Safe. This just a simple member retrieval. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If an error occurs, `*pGainDB` will be set to 0. - - -See Also --------- -ma_device_set_master_volume_db() -ma_device_set_master_volume() -ma_device_get_master_volume() -*/ -MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB); - - -/* -Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback. - - -Parameters ----------- -pDevice (in) - A pointer to device whose processing the data callback. - -pOutput (out) - A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device - this can be NULL, in which case pInput must not be NULL. - -pInput (in) - A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be - NULL, in which case `pOutput` must not be NULL. - -frameCount (in) - The number of frames being processed. - - -Return Value ------------- -MA_SUCCESS if successful; any other result code otherwise. - - -Thread Safety -------------- -This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a -playback and capture device in duplex setups. - - -Callback Safety ---------------- -Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend. - - -Remarks -------- -If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in -which case `pInput` will be processed first, followed by `pOutput`. - -If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that -callback. -*/ -MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - -/* -Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile. - -This function is used by backends for helping determine an appropriately sized buffer to use with -the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the -`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a -best guess at the device's native sample rate is also required which is where `nativeSampleRate` -comes in. In addition, the performance profile is also needed for cases where both the period size -in frames and milliseconds are both zero. - - -Parameters ----------- -pDescriptor (in) - A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members - will be used for the calculation of the buffer size. - -nativeSampleRate (in) - The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of - `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which - case a sample rate is required to convert to a size in frames. - -performanceProfile (in) - When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are - zero, miniaudio will fall back to a buffer size based on the performance profile. The profile - to use for this calculation is determine by this parameter. - - -Return Value ------------- -The calculated buffer size in frames. - - -Thread Safety -------------- -This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function -should only ever be called from within the backend's device initialization routine and therefore -shouldn't have any multithreading concerns. - - -Callback Safety ---------------- -This is safe to call within the data callback, but there is no reason to ever do this. - - -Remarks -------- -If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that -is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile); - - - -/* -Retrieves a friendly name for a backend. -*/ -MA_API const char* ma_get_backend_name(ma_backend backend); - -/* -Determines whether or not the given backend is available by the compilation environment. -*/ -MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend); - -/* -Retrieves compile-time enabled backends. - - -Parameters ----------- -pBackends (out, optional) - A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting - the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. - -backendCap (in) - The capacity of the `pBackends` buffer. - -pBackendCount (out) - A pointer to the variable that will receive the enabled backend count. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if `pBackendCount` is NULL. -MA_NO_SPACE if the capacity of `pBackends` is not large enough. - -If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values. - - -Thread Safety -------------- -Safe. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call -this function with `pBackends` set to NULL. - -This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null` -when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at -compile time with `MA_NO_NULL`. - -The returned backends are determined based on compile time settings, not the platform it's currently running on. For -example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have -PulseAudio installed. - - -Example 1 ---------- -The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is -given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends. -Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios. - -``` -ma_backend enabledBackends[MA_BACKEND_COUNT]; -size_t enabledBackendCount; - -result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount); -if (result != MA_SUCCESS) { - // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. -} -``` - - -See Also --------- -ma_is_backend_enabled() -*/ -MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount); - -/* -Determines whether or not loopback mode is support by a backend. -*/ -MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); - -#endif /* MA_NO_DEVICE_IO */ - - - -/************************************************************************************************************************************************************ - -Utiltities - -************************************************************************************************************************************************************/ - -/* -Calculates a buffer size in milliseconds from the specified number of frames and sample rate. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); - -/* -Calculates a buffer size in frames from the specified number of milliseconds and sample rate. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate); - -/* -Copies PCM frames from one buffer to another. -*/ -MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - -/* -Copies silent frames into the given buffer. - -Remarks -------- -For all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it -makes more sense for the purpose of mixing to initialize it to the center point. -*/ -MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - - -/* -Offsets a pointer by the specified number of PCM frames. -*/ -MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); -MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); -static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); } -static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); } - - -/* -Clips samples. -*/ -MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count); -MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - -/* -Helper for applying a volume factor to samples. - -Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place. -*/ -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor); - -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); - -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); - -MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains); - - -MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume); - - -/* -Helper for converting a linear factor to gain in decibels. -*/ -MA_API float ma_volume_linear_to_db(float factor); - -/* -Helper for converting gain in decibels to a linear factor. -*/ -MA_API float ma_volume_db_to_linear(float gain); - - - - -/************************************************************************************************** - -Data Source - -**************************************************************************************************/ -typedef void ma_data_source; - -#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 - -typedef struct -{ - ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); - ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); - ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); - ma_uint32 flags; -} ma_data_source_vtable; - -typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); - -typedef struct -{ - const ma_data_source_vtable* vtable; -} ma_data_source_config; - -MA_API ma_data_source_config ma_data_source_config_init(void); - - -typedef struct -{ - const ma_data_source_vtable* vtable; - ma_uint64 rangeBegInFrames; - ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ - ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ - ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ - ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ - ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ - ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ - MA_ATOMIC(4, ma_bool32) isLooping; -} ma_data_source_base; - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); -MA_API void ma_data_source_uninit(ma_data_source* pDataSource); -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); - - -typedef struct -{ - ma_data_source_base ds; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; - ma_uint64 sizeInFrames; - const void* pData; -} ma_audio_buffer_ref; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); - - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 sizeInFrames; - const void* pData; /* If set to NULL, will allocate a block of memory for you. */ - ma_allocation_callbacks allocationCallbacks; -} ma_audio_buffer_config; - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_audio_buffer_ref ref; - ma_allocation_callbacks allocationCallbacks; - ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ - ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ -} ma_audio_buffer; - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); - - -/* -Paged Audio Buffer -================== -A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It -can be used for cases where audio data is streamed in asynchronously while allowing data to be read -at the same time. - -This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across -simultaneously across different threads, however only one thread at a time can append, and only one -thread at a time can read and seek. -*/ -typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; -struct ma_paged_audio_buffer_page -{ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; - ma_uint64 sizeInFrames; - ma_uint8 pAudioData[1]; -}; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ -} ma_paged_audio_buffer_data; - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_paged_audio_buffer_data* pData; /* Must not be null. */ -} ma_paged_audio_buffer_config; - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); - - -typedef struct -{ - ma_data_source_base ds; - ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ - ma_paged_audio_buffer_page* pCurrent; - ma_uint64 relativeCursor; /* Relative to the current page. */ - ma_uint64 absoluteCursor; -} ma_paged_audio_buffer; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); - - - -/************************************************************************************************************************************************************ - -VFS -=== - -The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely -appropriate for a given situation. - -************************************************************************************************************************************************************/ -typedef void ma_vfs; -typedef ma_handle ma_vfs_file; - -typedef enum -{ - MA_OPEN_MODE_READ = 0x00000001, - MA_OPEN_MODE_WRITE = 0x00000002 -} ma_open_mode_flags; - -typedef enum -{ - ma_seek_origin_start, - ma_seek_origin_current, - ma_seek_origin_end /* Not used by decoders. */ -} ma_seek_origin; - -typedef struct -{ - ma_uint64 sizeInBytes; -} ma_file_info; - -typedef struct -{ - ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); - ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); - ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file); - ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); - ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); - ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); - ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); - ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); -} ma_vfs_callbacks; - -MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); -MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); -MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file); -MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); -MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); -MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); -MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); -MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_vfs_callbacks cb; - ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */ -} ma_default_vfs; - -MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks); - - - -typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); -typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin); -typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor); - - - -#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) -typedef enum -{ - ma_encoding_format_unknown = 0, - ma_encoding_format_wav, - ma_encoding_format_flac, - ma_encoding_format_mp3, - ma_encoding_format_vorbis -} ma_encoding_format; -#endif - -/************************************************************************************************************************************************************ - -Decoding -======== - -Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless -you do your own synchronization. - -************************************************************************************************************************************************************/ -#ifndef MA_NO_DECODING -typedef struct ma_decoder ma_decoder; - - -typedef struct -{ - ma_format preferredFormat; - ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */ -} ma_decoding_backend_config; - -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount); - - -typedef struct -{ - ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); - ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); -} ma_decoding_backend_vtable; - - -typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */ -typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); -typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor); - -typedef struct -{ - ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */ - ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */ - ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */ - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_dither_mode ditherMode; - ma_resampler_config resampling; - ma_allocation_callbacks allocationCallbacks; - ma_encoding_format encodingFormat; - ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ - ma_decoding_backend_vtable** ppCustomBackendVTables; - ma_uint32 customBackendCount; - void* pCustomBackendUserData; -} ma_decoder_config; - -struct ma_decoder -{ - ma_data_source_base ds; - ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */ - const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ - void* pBackendUserData; - ma_decoder_read_proc onRead; - ma_decoder_seek_proc onSeek; - ma_decoder_tell_proc onTell; - void* pUserData; - ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ - ma_format outputFormat; - ma_uint32 outputChannels; - ma_uint32 outputSampleRate; - ma_data_converter converter; /* Data conversion is achieved by running frames through this. */ - void* pInputCache; /* In input format. Can be null if it's not needed. */ - ma_uint64 inputCacheCap; /* The capacity of the input cache. */ - ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ - ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */ - ma_allocation_callbacks allocationCallbacks; - union - { - struct - { - ma_vfs* pVFS; - ma_vfs_file file; - } vfs; - struct - { - const ma_uint8* pData; - size_t dataSize; - size_t currentReadPos; - } memory; /* Only used for decoders that were opened against a block of memory. */ - } data; -}; - -MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate); -MA_API ma_decoder_config ma_decoder_config_init_default(void); - -MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); - -/* -Uninitializes a decoder. -*/ -MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); - -/* -Reads PCM frames from the given decoder. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - -/* -Seeks to a PCM frame based on it's absolute index. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); - -/* -Retrieves the decoder's output data format. -*/ -MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - -/* -Retrieves the current position of the read cursor in PCM frames. -*/ -MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor); - -/* -Retrieves the length of the decoder in PCM frames. - -Do not call this on streams of an undefined length, such as internet radio. - -If the length is unknown or an error occurs, 0 will be returned. - -This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio -uses internally. - -For MP3's, this will decode the entire file. Do not call this in time critical scenarios. - -This function is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength); - -/* -Retrieves the number of frames that can be read before reaching the end. - -This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in -particular ensuring you do not call it on streams of an undefined length, such as internet radio. - -If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be -returned. -*/ -MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames); - -/* -Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input, -pConfig should be set to what you want. On output it will be set to what you got. -*/ -MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); -MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); -MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); - -#endif /* MA_NO_DECODING */ - - -/************************************************************************************************************************************************************ - -Encoding -======== - -Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned. - -************************************************************************************************************************************************************/ -#ifndef MA_NO_ENCODING -typedef struct ma_encoder ma_encoder; - -typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten); -typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin); -typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder); -typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder); -typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); - -typedef struct -{ - ma_encoding_format encodingFormat; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_allocation_callbacks allocationCallbacks; -} ma_encoder_config; - -MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); - -struct ma_encoder -{ - ma_encoder_config config; - ma_encoder_write_proc onWrite; - ma_encoder_seek_proc onSeek; - ma_encoder_init_proc onInit; - ma_encoder_uninit_proc onUninit; - ma_encoder_write_pcm_frames_proc onWritePCMFrames; - void* pUserData; - void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */ - union - { - struct - { - ma_vfs* pVFS; - ma_vfs_file file; - } vfs; - } data; -}; - -MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API void ma_encoder_uninit(ma_encoder* pEncoder); -MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); - -#endif /* MA_NO_ENCODING */ - - -/************************************************************************************************************************************************************ - -Generation - -************************************************************************************************************************************************************/ -#ifndef MA_NO_GENERATION -typedef enum -{ - ma_waveform_type_sine, - ma_waveform_type_square, - ma_waveform_type_triangle, - ma_waveform_type_sawtooth -} ma_waveform_type; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_waveform_type type; - double amplitude; - double frequency; -} ma_waveform_config; - -MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency); - -typedef struct -{ - ma_data_source_base ds; - ma_waveform_config config; - double advance; - double time; -} ma_waveform; - -MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform); -MA_API void ma_waveform_uninit(ma_waveform* pWaveform); -MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); -MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); -MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); -MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); -MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); - -typedef enum -{ - ma_noise_type_white, - ma_noise_type_pink, - ma_noise_type_brownian -} ma_noise_type; - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_noise_type type; - ma_int32 seed; - double amplitude; - ma_bool32 duplicateChannels; -} ma_noise_config; - -MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude); - -typedef struct -{ - ma_data_source_vtable ds; - ma_noise_config config; - ma_lcg lcg; - union - { - struct - { - double** bin; - double* accumulation; - ma_uint32* counter; - } pink; - struct - { - double* accumulation; - } brownian; - } state; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_noise; - -MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise); -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise); -MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); -MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); -MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); - -#endif /* MA_NO_GENERATION */ - - - -/************************************************************************************************************************************************************ - -Resource Manager - -************************************************************************************************************************************************************/ -/* The resource manager cannot be enabled if there is no decoder. */ -#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING) -#define MA_NO_RESOURCE_MANAGER -#endif - -#ifndef MA_NO_RESOURCE_MANAGER -typedef struct ma_resource_manager ma_resource_manager; -typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node; -typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer; -typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; -typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; - -typedef enum -{ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010 /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ -} ma_resource_manager_data_source_flags; - - -/* -Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. -*/ -typedef struct -{ - ma_async_notification* pNotification; - ma_fence* pFence; -} ma_resource_manager_pipeline_stage_notification; - -typedef struct -{ - ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */ - ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */ -} ma_resource_manager_pipeline_notifications; - -MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void); - - - -/* BEGIN BACKWARDS COMPATIBILITY */ -/* TODO: Remove this block in version 0.12. */ -#if 1 -#define ma_resource_manager_job ma_job -#define ma_resource_manager_job_init ma_job_init -#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING -#define ma_resource_manager_job_queue_config ma_job_queue_config -#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init -#define ma_resource_manager_job_queue ma_job_queue -#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size -#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated -#define ma_resource_manager_job_queue_init ma_job_queue_init -#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit -#define ma_resource_manager_job_queue_post ma_job_queue_post -#define ma_resource_manager_job_queue_next ma_job_queue_next -#endif -/* END BACKWARDS COMPATIBILITY */ - - - - -/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ -#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT -#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 -#endif - -typedef enum -{ - /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ - MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, - - /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ - MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 -} ma_resource_manager_flags; - -typedef struct -{ - const char* pFilePath; - const wchar_t* pFilePathW; - const ma_resource_manager_pipeline_notifications* pNotifications; - ma_uint64 initialSeekPointInPCMFrames; - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_bool32 isLooping; - ma_uint32 flags; -} ma_resource_manager_data_source_config; - -MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); - - -typedef enum -{ - ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */ - ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ - ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ - ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ -} ma_resource_manager_data_supply_type; - -typedef struct -{ - MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */ - union - { - struct - { - const void* pData; - size_t sizeInBytes; - } encoded; - struct - { - const void* pData; - ma_uint64 totalFrameCount; - ma_uint64 decodedFrameCount; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - } decoded; - struct - { - ma_paged_audio_buffer_data data; - ma_uint64 decodedFrameCount; - ma_uint32 sampleRate; - } decodedPaged; - } backend; -} ma_resource_manager_data_supply; - -struct ma_resource_manager_data_buffer_node -{ - ma_uint32 hashedName32; /* The hashed name. This is the key. */ - ma_uint32 refCount; - MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ - ma_resource_manager_data_supply data; - ma_resource_manager_data_buffer_node* pParent; - ma_resource_manager_data_buffer_node* pChildLo; - ma_resource_manager_data_buffer_node* pChildHi; -}; - -struct ma_resource_manager_data_buffer -{ - ma_data_source_base ds; /* Base data source. A data buffer is a data source. */ - ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */ - ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */ - ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */ - ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ - MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ - MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ - ma_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ - union - { - ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ - ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */ - ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ - } connector; /* Connects this object to the node's data supply. */ -}; - -struct ma_resource_manager_data_stream -{ - ma_data_source_base ds; /* Base data source. A data stream is a data source. */ - ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */ - ma_uint32 flags; /* The flags that were passed used to initialize the stream. */ - ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ - ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ - ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ - ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ - MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */ - ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - - /* Written by the public API, read by the job thread. */ - MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ - - /* Written by the job thread, read by the public API. */ - void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ - MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ - - /* Written and read by both the public API and the job thread. These must be atomic. */ - MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ - MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */ - MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ - MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ -}; - -struct ma_resource_manager_data_source -{ - union - { - ma_resource_manager_data_buffer buffer; - ma_resource_manager_data_stream stream; - } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ - - ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ -}; - -typedef struct -{ - ma_allocation_callbacks allocationCallbacks; - ma_log* pLog; - ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ - ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ - ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ - ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ - ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ - ma_uint32 flags; - ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ - ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - ma_uint32 customDecodingBackendCount; - void* pCustomDecodingBackendUserData; -} ma_resource_manager_config; - -MA_API ma_resource_manager_config ma_resource_manager_config_init(void); - -struct ma_resource_manager -{ - ma_resource_manager_config config; - ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */ -#ifndef MA_NO_THREADING - ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */ - ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */ -#endif - ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ - ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */ - ma_log log; /* Only used if no log was specified in the config. */ -}; - -/* Init. */ -MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager); -MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager); -MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager); - -/* Registration. */ -MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags); -MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags); -MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ -MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); -MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ -MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes); -MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath); -MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath); -MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName); -MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); - -/* Data Buffers. */ -MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); - -/* Data Streams. */ -MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); - -/* Data Sources. */ -MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames); - -/* Job management. */ -MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob); -MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */ -MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob); -MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ -MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ -#endif /* MA_NO_RESOURCE_MANAGER */ - - - -/************************************************************************************************************************************************************ - -Node Graph - -************************************************************************************************************************************************************/ -#ifndef MA_NO_NODE_GRAPH -/* Must never exceed 254. */ -#ifndef MA_MAX_NODE_BUS_COUNT -#define MA_MAX_NODE_BUS_COUNT 254 -#endif - -/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ -#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT -#define MA_MAX_NODE_LOCAL_BUS_COUNT 2 -#endif - -/* Use this when the bus count is determined by the node instance rather than the vtable. */ -#define MA_NODE_BUS_COUNT_UNKNOWN 255 - -typedef struct ma_node_graph ma_node_graph; -typedef void ma_node; - - -/* Node flags. */ -typedef enum -{ - MA_NODE_FLAG_PASSTHROUGH = 0x00000001, - MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, - MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, - MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008, - MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010 -} ma_node_flags; - - -/* The playback state of a node. Either started or stopped. */ -typedef enum -{ - ma_node_state_started = 0, - ma_node_state_stopped = 1 -} ma_node_state; - - -typedef struct -{ - /* - Extended processing callback. This callback is used for effects that process input and output - at different rates (i.e. they perform resampling). This is similar to the simple version, only - they take two seperate frame counts: one for input, and one for output. - - On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas - `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. - - On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set - `pFrameCountIn` to the number of input frames that were consumed. - */ - void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); - - /* - A callback for retrieving the number of a input frames that are required to output the - specified number of output frames. You would only want to implement this when the node performs - resampling. This is optional, even for nodes that perform resampling, but it does offer a - small reduction in latency as it allows miniaudio to calculate the exact number of input frames - to read at a time instead of having to estimate. - */ - ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount); - - /* - The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` - parameters of the callbacks above. - */ - ma_uint8 inputBusCount; - - /* - The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` - parameters of the callbacks above. - */ - ma_uint8 outputBusCount; - - /* - Flags describing characteristics of the node. This is currently just a placeholder for some - ideas for later on. - */ - ma_uint32 flags; -} ma_node_vtable; - -typedef struct -{ - const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */ - ma_node_state initialState; /* Defaults to ma_node_state_started. */ - ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ - ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ - const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ - const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ -} ma_node_config; - -MA_API ma_node_config ma_node_config_init(void); - - -/* -A node has multiple output buses. An output bus is attached to an input bus as an item in a linked -list. Think of the input bus as a linked list, with the output bus being an item in that list. -*/ -typedef struct ma_node_output_bus ma_node_output_bus; -struct ma_node_output_bus -{ - /* Immutable. */ - ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ - ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ - - /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ - MA_ATOMIC(1, ma_uint8) inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. */ - MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ - MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ - MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ - MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ - MA_ATOMIC(4, float) volume; /* Linear. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */ -}; - -/* -A node has multiple input buses. The output buses of a node are connecting to the input busses of -another. An input bus is essentially just a linked list of output buses. -*/ -typedef struct ma_node_input_bus ma_node_input_bus; -struct ma_node_input_bus -{ - /* Mutable via multiple threads. */ - ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */ - MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ - MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ - - /* Set once at startup. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ -}; - - -typedef struct ma_node_base ma_node_base; -struct ma_node_base -{ - /* These variables are set once at startup. */ - ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ - const ma_node_vtable* vtable; - float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ - ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ - - /* These variables are read and written only from the audio thread. */ - ma_uint16 cachedFrameCountOut; - ma_uint16 cachedFrameCountIn; - ma_uint16 consumedFrameCountIn; - - /* These variables are read and written between different threads. */ - MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ - MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ - MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_node_input_bus* pInputBuses; - ma_node_output_bus* pOutputBuses; - - /* Memory management. */ - ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; - ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; - void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ - ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ -}; - -MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode); -MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); -MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex); -MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex); -MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode); -MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume); -MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state); -MA_API ma_node_state ma_node_get_state(const ma_node* pNode); -MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime); -MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state); -MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime); -MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd); -MA_API ma_uint64 ma_node_get_time(const ma_node* pNode); -MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); - - -typedef struct -{ - ma_uint32 channels; - ma_uint16 nodeCacheCapInFrames; -} ma_node_graph_config; - -MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); - - -struct ma_node_graph -{ - /* Immutable. */ - ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ - ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ - ma_uint16 nodeCacheCapInFrames; - - /* Read and written by multiple threads. */ - MA_ATOMIC(4, ma_bool32) isReading; -}; - -MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); -MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph); -MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph); -MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph); -MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime); - - - -/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ -typedef struct -{ - ma_node_config nodeConfig; - ma_data_source* pDataSource; -} ma_data_source_node_config; - -MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource); - - -typedef struct -{ - ma_node_base base; - ma_data_source* pDataSource; -} ma_data_source_node; - -MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode); -MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); - - -/* Splitter Node. 1 input, 2 outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ -typedef struct -{ - ma_node_config nodeConfig; - ma_uint32 channels; -} ma_splitter_node_config; - -MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); - - -typedef struct -{ - ma_node_base base; -} ma_splitter_node; - -MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode); -MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Biquad Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_biquad_config biquad; -} ma_biquad_node_config; - -MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2); - - -typedef struct -{ - ma_node_base baseNode; - ma_biquad biquad; -} ma_biquad_node; - -MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode); -MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode); -MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Low Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_lpf_config lpf; -} ma_lpf_node_config; - -MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_lpf lpf; -} ma_lpf_node; - -MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode); -MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode); -MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -High Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_hpf_config hpf; -} ma_hpf_node_config; - -MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_hpf hpf; -} ma_hpf_node; - -MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode); -MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode); -MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Band Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_bpf_config bpf; -} ma_bpf_node_config; - -MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_bpf bpf; -} ma_bpf_node; - -MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode); -MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode); -MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Notching Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_notch_config notch; -} ma_notch_node_config; - -MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_notch2 notch; -} ma_notch_node; - -MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode); -MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode); -MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Peaking Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_peak_config peak; -} ma_peak_node_config; - -MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_peak2 peak; -} ma_peak_node; - -MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode); -MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode); -MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Low Shelf Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_loshelf_config loshelf; -} ma_loshelf_node_config; - -MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_loshelf2 loshelf; -} ma_loshelf_node; - -MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode); -MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode); -MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -High Shelf Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_hishelf_config hishelf; -} ma_hishelf_node_config; - -MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_hishelf2 hishelf; -} ma_hishelf_node; - -MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode); -MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode); -MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_node_config nodeConfig; - ma_delay_config delay; -} ma_delay_node_config; - -MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); - - -typedef struct -{ - ma_node_base baseNode; - ma_delay delay; -} ma_delay_node; - -MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode); -MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode); -MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode); -MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); -#endif /* MA_NO_NODE_GRAPH */ - - -/************************************************************************************************************************************************************ - -Engine - -************************************************************************************************************************************************************/ -#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) -typedef struct ma_engine ma_engine; -typedef struct ma_sound ma_sound; - - -/* Sound flags. */ -typedef enum -{ - MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ - MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ - MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ - MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ - MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ - MA_SOUND_FLAG_NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ - MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00000040 /* Disable spatialization. */ -} ma_sound_flags; - -#ifndef MA_ENGINE_MAX_LISTENERS -#define MA_ENGINE_MAX_LISTENERS 4 -#endif - -#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1) - -typedef enum -{ - ma_engine_node_type_sound, - ma_engine_node_type_group -} ma_engine_node_type; - -typedef struct -{ - ma_engine* pEngine; - ma_engine_node_type type; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ - ma_bool8 isPitchDisabled; /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ - ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ - ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ -} ma_engine_node_config; - -MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags); - - -/* Base node object for both ma_sound and ma_sound_group. */ -typedef struct -{ - ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ - ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ - ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ - ma_fader fader; - ma_linear_resampler resampler; /* For pitch shift. */ - ma_spatializer spatializer; - ma_panner panner; - MA_ATOMIC(4, float) pitch; - float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ - float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ - MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ - MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ - MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ - - /* Memory management. */ - ma_bool8 _ownsHeap; - void* _pHeap; -} ma_engine_node; - -MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode); -MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode); -MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF - -typedef struct -{ - const char* pFilePath; /* Set this to load from the resource manager. */ - const wchar_t* pFilePathW; /* Set this to load from the resource manager. */ - ma_data_source* pDataSource; /* Set this to load from an existing data source. */ - ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ - ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ - ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ - ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ - ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ - ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_bool32 isLooping; - ma_fence* pDoneFence; /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */ -} ma_sound_config; - -MA_API ma_sound_config ma_sound_config_init(void); - -struct ma_sound -{ - ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */ - ma_data_source* pDataSource; - MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ - MA_ATOMIC(4, ma_bool32) atEnd; - ma_bool8 ownsDataSource; - - /* - We're declaring a resource manager data source object here to save us a malloc when loading a - sound via the resource manager, which I *think* will be the most common scenario. - */ -#ifndef MA_NO_RESOURCE_MANAGER - ma_resource_manager_data_source* pResourceManagerDataSource; -#endif -}; - -/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ -typedef struct ma_sound_inlined ma_sound_inlined; -struct ma_sound_inlined -{ - ma_sound sound; - ma_sound_inlined* pNext; - ma_sound_inlined* pPrev; -}; - -/* A sound group is just a sound. */ -typedef ma_sound_config ma_sound_group_config; -typedef ma_sound ma_sound_group; - -MA_API ma_sound_group_config ma_sound_group_config_init(void); - - -typedef struct -{ -#if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ -#endif -#if !defined(MA_NO_DEVICE_IO) - ma_context* pContext; - ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ - ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ -#endif - ma_log* pLog; /* When set to NULL, will use the context's log. */ - ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ - ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ - ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ - ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ - ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ - ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ - ma_allocation_callbacks allocationCallbacks; - ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ - ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ -} ma_engine_config; - -MA_API ma_engine_config ma_engine_config_init(void); - - -struct ma_engine -{ - ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ -#if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; -#endif -#if !defined(MA_NO_DEVICE_IO) - ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ -#endif - ma_log* pLog; - ma_uint32 sampleRate; - ma_uint32 listenerCount; - ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]; - ma_allocation_callbacks allocationCallbacks; - ma_bool8 ownsResourceManager; - ma_bool8 ownsDevice; - ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */ - ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ - MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ - ma_mono_expansion_mode monoExpansionMode; -}; - -MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); -MA_API void ma_engine_uninit(ma_engine* pEngine); -MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine); -#if !defined(MA_NO_RESOURCE_MANAGER) -MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); -#endif -MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); -MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); -MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); -MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); -MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); - -MA_API ma_result ma_engine_start(ma_engine* pEngine); -MA_API ma_result ma_engine_stop(ma_engine* pEngine); -MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); - -MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); -MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); -MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled); -MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex); - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex); -MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */ -#endif - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); -MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); -MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); -#endif -MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); -MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound); -MA_API void ma_sound_uninit(ma_sound* pSound); -MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); -MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); -MA_API ma_result ma_sound_start(ma_sound* pSound); -MA_API ma_result ma_sound_stop(ma_sound* pSound); -MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); -MA_API float ma_sound_get_volume(const ma_sound* pSound); -MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); -MA_API float ma_sound_get_pan(const ma_sound* pSound); -MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode); -MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound); -MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch); -MA_API float ma_sound_get_pitch(const ma_sound* pSound); -MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled); -MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); -MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); -MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); -MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); -MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); -MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); -MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound); -MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound); -MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound); -MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning); -MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound); -MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff); -MA_API float ma_sound_get_rolloff(const ma_sound* pSound); -MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain); -MA_API float ma_sound_get_min_gain(const ma_sound* pSound); -MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain); -MA_API float ma_sound_get_max_gain(const ma_sound* pSound); -MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance); -MA_API float ma_sound_get_min_distance(const ma_sound* pSound); -MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance); -MA_API float ma_sound_get_max_distance(const ma_sound* pSound); -MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); -MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); -MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); -MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); -MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API float ma_sound_get_current_fade_volume(ma_sound* pSound); -MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); -MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); -MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); -MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); -MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); -MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); - -MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); -MA_API void ma_sound_group_uninit(ma_sound_group* pGroup); -MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup); -MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume); -MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan); -MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode); -MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch); -MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled); -MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); -MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); -MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); -MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning); -MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff); -MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain); -MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain); -MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance); -MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance); -MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); -MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); -MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); -MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); -MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); -#endif /* MA_NO_ENGINE */ - -#ifdef __cplusplus -} -#endif -#endif /* miniaudio_h */ - - -/* -This is for preventing greying out of the implementation section. -*/ -#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__) -#define MINIAUDIO_IMPLEMENTATION -#endif - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -IMPLEMENTATION - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) -#ifndef miniaudio_c -#define miniaudio_c - -#include -#include /* For INT_MAX */ -#include /* sin(), etc. */ - -#include -#include -#if !defined(_MSC_VER) && !defined(__DMC__) - #include /* For strcasecmp(). */ - #include /* For wcslen(), wcsrtombs() */ -#endif -#ifdef _MSC_VER - #include /* For _controlfp_s constants */ -#endif - -#ifdef MA_WIN32 -#include -#else -#include /* For malloc(), free(), wcstombs(). */ -#include /* For memset() */ -#include -#include /* select() (used for ma_sleep()). */ -#include -#endif - -#include /* For fstat(), etc. */ - -#ifdef MA_EMSCRIPTEN -#include -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#ifdef _WIN32 -#ifdef _WIN64 -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#ifdef __GNUC__ -#ifdef __LP64__ -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#include -#if INTPTR_MAX == INT64_MAX -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif - -/* Architecture Detection */ -#if defined(__x86_64__) || defined(_M_X64) -#define MA_X64 -#elif defined(__i386) || defined(_M_IX86) -#define MA_X86 -#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) -#define MA_ARM -#endif - -/* Intrinsics Support */ -#if defined(MA_X64) || defined(MA_X86) - #if defined(_MSC_VER) && !defined(__clang__) - /* MSVC. */ - #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ - #define MA_SUPPORT_SSE2 - #endif - /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */ - #define MA_SUPPORT_AVX2 - #endif - #else - /* Assume GNUC-style. */ - #if defined(__SSE2__) && !defined(MA_NO_SSE2) - #define MA_SUPPORT_SSE2 - #endif - /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if defined(__AVX2__) && !defined(MA_NO_AVX2) - #define MA_SUPPORT_AVX2 - #endif - #endif - - /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include() - #define MA_SUPPORT_SSE2 - #endif - /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include()*/ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include() - #define MA_SUPPORT_AVX2 - #endif - #endif - - #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) - #include - #elif defined(MA_SUPPORT_SSE2) - #include - #endif -#endif - -#if defined(MA_ARM) - #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define MA_SUPPORT_NEON - #include - #endif -#endif - -/* Begin globally disabled warnings. */ -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */ - #pragma warning(disable:4049) /* compiler limit : terminating line number emission */ -#endif - -#if defined(MA_X64) || defined(MA_X86) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 - #include - static MA_INLINE void ma_cpuid(int info[4], int fid) - { - __cpuid(info, fid); - } - #else - #define MA_NO_CPUID - #endif - - #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219) - static MA_INLINE unsigned __int64 ma_xgetbv(int reg) - { - return _xgetbv(reg); - } - #else - #define MA_NO_XGETBV - #endif - #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID) - static MA_INLINE void ma_cpuid(int info[4], int fid) - { - /* - It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the - specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for - supporting different assembly dialects. - - What's basically happening is that we're saving and restoring the ebx register manually. - */ - #if defined(DRFLAC_X86) && defined(__PIC__) - __asm__ __volatile__ ( - "xchg{l} {%%}ebx, %k1;" - "cpuid;" - "xchg{l} {%%}ebx, %k1;" - : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #else - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #endif - } - - static MA_INLINE ma_uint64 ma_xgetbv(int reg) - { - unsigned int hi; - unsigned int lo; - - __asm__ __volatile__ ( - "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg) - ); - - return ((ma_uint64)hi << 32) | (ma_uint64)lo; - } - #else - #define MA_NO_CPUID - #define MA_NO_XGETBV - #endif -#else - #define MA_NO_CPUID - #define MA_NO_XGETBV -#endif - -static MA_INLINE ma_bool32 ma_has_sse2(void) -{ -#if defined(MA_SUPPORT_SSE2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2) - #if defined(MA_X64) - return MA_TRUE; /* 64-bit targets always support SSE2. */ - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ - #else - #if defined(MA_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_cpuid(info, 1); - return (info[3] & (1 << 26)) != 0; - #endif - #endif - #else - return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -#if 0 -static MA_INLINE ma_bool32 ma_has_avx() -{ -#if defined(MA_SUPPORT_AVX) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX) - #if defined(_AVX_) || defined(__AVX__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */ - #else - /* AVX requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info[4]; - ma_cpuid(info, 1); - if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0x06) == 0x06) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} -#endif - -static MA_INLINE ma_bool32 ma_has_avx2(void) -{ -#if defined(MA_SUPPORT_AVX2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2) - #if defined(_AVX2_) || defined(__AVX2__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */ - #else - /* AVX2 requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info1[4]; - int info7[4]; - ma_cpuid(info1, 1); - ma_cpuid(info7, 7); - if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0x06) == 0x06) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -static MA_INLINE ma_bool32 ma_has_neon(void) -{ -#if defined(MA_SUPPORT_NEON) - #if defined(MA_ARM) && !defined(MA_NO_NEON) - #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ - #else - /* TODO: Runtime check. */ - return MA_FALSE; - #endif - #else - return MA_FALSE; /* NEON is only supported on ARM architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -#define MA_SIMD_NONE 0 -#define MA_SIMD_SSE2 1 -#define MA_SIMD_AVX2 2 -#define MA_SIMD_NEON 3 - -#ifndef MA_PREFERRED_SIMD - # if defined(MA_SUPPORT_SSE2) && defined(MA_PREFER_SSE2) - #define MA_PREFERRED_SIMD MA_SIMD_SSE2 - #elif defined(MA_SUPPORT_AVX2) && defined(MA_PREFER_AVX2) - #define MA_PREFERRED_SIMD MA_SIMD_AVX2 - #elif defined(MA_SUPPORT_NEON) && defined(MA_PREFER_NEON) - #define MA_PREFERRED_SIMD MA_SIMD_NEON - #else - #define MA_PREFERRED_SIMD MA_SIMD_NONE - #endif -#endif - -#if defined(__has_builtin) - #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) -#else - #define MA_COMPILER_HAS_BUILTIN(x) 0 -#endif - -#ifndef MA_ASSUME - #if MA_COMPILER_HAS_BUILTIN(__builtin_assume) - #define MA_ASSUME(x) __builtin_assume(x) - #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable) - #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0) - #elif defined(_MSC_VER) - #define MA_ASSUME(x) __assume(x) - #else - #define MA_ASSUME(x) (void)(x) - #endif -#endif - -#ifndef MA_RESTRICT - #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) - #define MA_RESTRICT __restrict - #else - #define MA_RESTRICT - #endif -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define MA_HAS_BYTESWAP16_INTRINSIC - #define MA_HAS_BYTESWAP32_INTRINSIC - #define MA_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16) - #define MA_HAS_BYTESWAP16_INTRINSIC - #endif - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32) - #define MA_HAS_BYTESWAP32_INTRINSIC - #endif - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64) - #define MA_HAS_BYTESWAP64_INTRINSIC - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_HAS_BYTESWAP32_INTRINSIC - #define MA_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_HAS_BYTESWAP16_INTRINSIC - #endif -#endif - - -static MA_INLINE ma_bool32 ma_is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} - -static MA_INLINE ma_bool32 ma_is_big_endian(void) -{ - return !ma_is_little_endian(); -} - - -static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n) -{ -#ifdef MA_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ - /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} - - -#if !defined(MA_EMSCRIPTEN) -#ifdef MA_WIN32 -static void ma_sleep__win32(ma_uint32 milliseconds) -{ - Sleep((DWORD)milliseconds); -} -#endif -#ifdef MA_POSIX -static void ma_sleep__posix(ma_uint32 milliseconds) -{ -#ifdef MA_EMSCRIPTEN - (void)milliseconds; - MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ -#else - #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L - struct timespec ts; - ts.tv_sec = milliseconds / 1000; - ts.tv_nsec = milliseconds % 1000 * 1000000; - nanosleep(&ts, NULL); - #else - struct timeval tv; - tv.tv_sec = milliseconds / 1000; - tv.tv_usec = milliseconds % 1000 * 1000; - select(0, NULL, NULL, NULL, &tv); - #endif -#endif -} -#endif - -static MA_INLINE void ma_sleep(ma_uint32 milliseconds) -{ -#ifdef MA_WIN32 - ma_sleep__win32(milliseconds); -#endif -#ifdef MA_POSIX - ma_sleep__posix(milliseconds); -#endif -} -#endif - -static MA_INLINE void ma_yield() -{ -#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) - /* x86/x64 */ - #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__) - #if _MSC_VER >= 1400 - _mm_pause(); - #else - #if defined(__DMC__) - /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */ - __asm nop; - #else - __asm pause; - #endif - #endif - #else - __asm__ __volatile__ ("pause"); - #endif -#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) - /* ARM */ - #if defined(_MSC_VER) - /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */ - __yield(); - #else - __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */ - #endif -#else - /* Unknown or unsupported architecture. No-op. */ -#endif -} - - -#define MA_MM_DENORMALS_ZERO_MASK 0x0040 -#define MA_MM_FLUSH_ZERO_MASK 0x8000 - -static MA_INLINE unsigned int ma_disable_denormals() -{ - unsigned int prevState; - - #if defined(_MSC_VER) - { - /* - Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't - know which version of Visual Studio first added support for _controlfp_s(), but I do know - that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older - versions of Visual Studio, let me know and I'll make the necessary adjustment. - */ - #if _MSC_VER <= 1200 - { - prevState = _statusfp(); - _controlfp(prevState | _DN_FLUSH, _MCW_DN); - } - #else - { - unsigned int unused; - _controlfp_s(&prevState, 0, 0); - _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN); - } - #endif - } - #elif defined(MA_X86) || defined(MA_X64) - { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ - { - prevState = _mm_getcsr(); - _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); - } - #else - { - /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ - prevState = 0; - } - #endif - } - #else - { - /* Unknown or unsupported architecture. No-op. */ - prevState = 0; - } - #endif - - return prevState; -} - -static MA_INLINE void ma_restore_denormals(unsigned int prevState) -{ - #if defined(_MSC_VER) - { - /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ - #if _MSC_VER <= 1200 - { - _controlfp(prevState, _MCW_DN); - } - #else - { - unsigned int unused; - _controlfp_s(&unused, prevState, _MCW_DN); - } - #endif - } - #elif defined(MA_X86) || defined(MA_X64) - { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ - { - _mm_setcsr(prevState); - } - #else - { - /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ - (void)prevState; - } - #endif - } - #else - { - /* Unknown or unsupported architecture. No-op. */ - (void)prevState; - } - #endif -} - - - -#ifndef MA_COINIT_VALUE -#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ -#endif - - -#ifndef MA_FLT_MAX - #ifdef FLT_MAX - #define MA_FLT_MAX FLT_MAX - #else - #define MA_FLT_MAX 3.402823466e+38F - #endif -#endif - - -#ifndef MA_PI -#define MA_PI 3.14159265358979323846264f -#endif -#ifndef MA_PI_D -#define MA_PI_D 3.14159265358979323846264 -#endif -#ifndef MA_TAU -#define MA_TAU 6.28318530717958647693f -#endif -#ifndef MA_TAU_D -#define MA_TAU_D 6.28318530717958647693 -#endif - - -/* The default format when ma_format_unknown (0) is requested when initializing a device. */ -#ifndef MA_DEFAULT_FORMAT -#define MA_DEFAULT_FORMAT ma_format_f32 -#endif - -/* The default channel count to use when 0 is used when initializing a device. */ -#ifndef MA_DEFAULT_CHANNELS -#define MA_DEFAULT_CHANNELS 2 -#endif - -/* The default sample rate to use when 0 is used when initializing a device. */ -#ifndef MA_DEFAULT_SAMPLE_RATE -#define MA_DEFAULT_SAMPLE_RATE 48000 -#endif - -/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */ -#ifndef MA_DEFAULT_PERIODS -#define MA_DEFAULT_PERIODS 3 -#endif - -/* The default period size in milliseconds for low latency mode. */ -#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY -#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10 -#endif - -/* The default buffer size in milliseconds for conservative mode. */ -#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE -#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100 -#endif - -/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */ -#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER - #if MA_MAX_FILTER_ORDER >= 4 - #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4 - #else - #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER - #endif -#endif - - -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wunused-variable" -#endif - -/* Standard sample rates, in order of priority. */ -static ma_uint32 g_maStandardSampleRatePriorities[] = { - (ma_uint32)ma_standard_sample_rate_48000, - (ma_uint32)ma_standard_sample_rate_44100, - - (ma_uint32)ma_standard_sample_rate_32000, - (ma_uint32)ma_standard_sample_rate_24000, - (ma_uint32)ma_standard_sample_rate_22050, - - (ma_uint32)ma_standard_sample_rate_88200, - (ma_uint32)ma_standard_sample_rate_96000, - (ma_uint32)ma_standard_sample_rate_176400, - (ma_uint32)ma_standard_sample_rate_192000, - - (ma_uint32)ma_standard_sample_rate_16000, - (ma_uint32)ma_standard_sample_rate_11025, - (ma_uint32)ma_standard_sample_rate_8000, - - (ma_uint32)ma_standard_sample_rate_352800, - (ma_uint32)ma_standard_sample_rate_384000 -}; - -static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate) -{ - ma_uint32 iSampleRate; - - for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) { - if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) { - return MA_TRUE; - } - } - - /* Getting here means the sample rate is not supported. */ - return MA_FALSE; -} - - -static ma_format g_maFormatPriorities[] = { - ma_format_s16, /* Most common */ - ma_format_f32, - - /*ma_format_s24_32,*/ /* Clean alignment */ - ma_format_s32, - - ma_format_s24, /* Unclean alignment */ - - ma_format_u8 /* Low quality */ -}; -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif - - -MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_VERSION_MAJOR; - } - - if (pMinor) { - *pMinor = MA_VERSION_MINOR; - } - - if (pRevision) { - *pRevision = MA_VERSION_REVISION; - } -} - -MA_API const char* ma_version_string(void) -{ - return MA_VERSION_STRING; -} - - -/****************************************************************************** - -Standard Library Stuff - -******************************************************************************/ -#ifndef MA_MALLOC -#ifdef MA_WIN32 -#define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz)) -#else -#define MA_MALLOC(sz) malloc((sz)) -#endif -#endif - -#ifndef MA_REALLOC -#ifdef MA_WIN32 -#define MA_REALLOC(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(size_t)(HeapFree(GetProcessHeap(), 0, (p)) & 0))) -#else -#define MA_REALLOC(p, sz) realloc((p), (sz)) -#endif -#endif - -#ifndef MA_FREE -#ifdef MA_WIN32 -#define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p)) -#else -#define MA_FREE(p) free((p)) -#endif -#endif - -#ifndef MA_ZERO_MEMORY -#ifdef MA_WIN32 -#define MA_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz)) -#else -#define MA_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#endif - -#ifndef MA_COPY_MEMORY -#ifdef MA_WIN32 -#define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz)) -#else -#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#endif - -#ifndef MA_MOVE_MEMORY -#ifdef MA_WIN32 -#define MA_MOVE_MEMORY(dst, src, sz) MoveMemory((dst), (src), (sz)) -#else -#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif -#endif - -#ifndef MA_ASSERT -#ifdef MA_WIN32 -#define MA_ASSERT(condition) assert(condition) -#else -#define MA_ASSERT(condition) assert(condition) -#endif -#endif - -#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) - -#define ma_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) -#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) -#define ma_abs(x) (((x) > 0) ? (x) : -(x)) -#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) -#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) ((x + (a-1)) & ~(a-1)) -#define ma_align_64(x) ma_align(x, 8) - -#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) - -static MA_INLINE double ma_sind(double x) -{ - /* TODO: Implement custom sin(x). */ - return sin(x); -} - -static MA_INLINE double ma_expd(double x) -{ - /* TODO: Implement custom exp(x). */ - return exp(x); -} - -static MA_INLINE double ma_logd(double x) -{ - /* TODO: Implement custom log(x). */ - return log(x); -} - -static MA_INLINE double ma_powd(double x, double y) -{ - /* TODO: Implement custom pow(x, y). */ - return pow(x, y); -} - -static MA_INLINE double ma_sqrtd(double x) -{ - /* TODO: Implement custom sqrt(x). */ - return sqrt(x); -} - - -static MA_INLINE float ma_sinf(float x) -{ - return (float)ma_sind((float)x); -} - -static MA_INLINE double ma_cosd(double x) -{ - return ma_sind((MA_PI_D*0.5) - x); -} - -static MA_INLINE float ma_cosf(float x) -{ - return (float)ma_cosd((float)x); -} - -static MA_INLINE double ma_log10d(double x) -{ - return ma_logd(x) * 0.43429448190325182765; -} - -static MA_INLINE float ma_powf(float x, float y) -{ - return (float)ma_powd((double)x, (double)y); -} - -static MA_INLINE float ma_log10f(float x) -{ - return (float)ma_log10d((double)x); -} - - -static MA_INLINE double ma_degrees_to_radians(double degrees) -{ - return degrees * 0.01745329252; -} - -static MA_INLINE double ma_radians_to_degrees(double radians) -{ - return radians * 57.295779512896; -} - -static MA_INLINE float ma_degrees_to_radians_f(float degrees) -{ - return degrees * 0.01745329252f; -} - -static MA_INLINE float ma_radians_to_degrees_f(float radians) -{ - return radians * 57.295779512896f; -} - - -/* -Return Values: - 0: Success - 22: EINVAL - 34: ERANGE - -Not using symbolic constants for errors because I want to avoid #including errno.h -*/ -MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstSizeInBytes) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - -MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstCap == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstCap && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstCap) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - - -MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) -{ - size_t maxcount; - size_t i; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - maxcount = count; - if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */ - maxcount = dstSizeInBytes - 1; - } - - for (i = 0; i < maxcount && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (src[i] == '\0' || i == count || count == ((size_t)-1)) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - -MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) -{ - char* dstorig; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - dstorig = dst; - - while (dstSizeInBytes > 0 && dst[0] != '\0') { - dst += 1; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - return 22; /* Unterminated. */ - } - - - while (dstSizeInBytes > 0 && src[0] != '\0') { - *dst++ = *src++; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes > 0) { - dst[0] = '\0'; - } else { - dstorig[0] = '\0'; - return 34; - } - - return 0; -} - -MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) -{ - char* dstorig; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - return 22; - } - - dstorig = dst; - - while (dstSizeInBytes > 0 && dst[0] != '\0') { - dst += 1; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - return 22; /* Unterminated. */ - } - - - if (count == ((size_t)-1)) { /* _TRUNCATE */ - count = dstSizeInBytes - 1; - } - - while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) { - *dst++ = *src++; - dstSizeInBytes -= 1; - count -= 1; - } - - if (dstSizeInBytes > 0) { - dst[0] = '\0'; - } else { - dstorig[0] = '\0'; - return 34; - } - - return 0; -} - -MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) -{ - int sign; - unsigned int valueU; - char* dstEnd; - - if (dst == NULL || dstSizeInBytes == 0) { - return 22; - } - if (radix < 2 || radix > 36) { - dst[0] = '\0'; - return 22; - } - - sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */ - - if (value < 0) { - valueU = -value; - } else { - valueU = value; - } - - dstEnd = dst; - do - { - int remainder = valueU % radix; - if (remainder > 9) { - *dstEnd = (char)((remainder - 10) + 'a'); - } else { - *dstEnd = (char)(remainder + '0'); - } - - dstEnd += 1; - dstSizeInBytes -= 1; - valueU /= radix; - } while (dstSizeInBytes > 0 && valueU > 0); - - if (dstSizeInBytes == 0) { - dst[0] = '\0'; - return 22; /* Ran out of room in the output buffer. */ - } - - if (sign < 0) { - *dstEnd++ = '-'; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - dst[0] = '\0'; - return 22; /* Ran out of room in the output buffer. */ - } - - *dstEnd = '\0'; - - - /* At this point the string will be reversed. */ - dstEnd -= 1; - while (dst < dstEnd) { - char temp = *dst; - *dst = *dstEnd; - *dstEnd = temp; - - dst += 1; - dstEnd -= 1; - } - - return 0; -} - -MA_API int ma_strcmp(const char* str1, const char* str2) -{ - if (str1 == str2) return 0; - - /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ - if (str1 == NULL) return -1; - if (str2 == NULL) return 1; - - for (;;) { - if (str1[0] == '\0') { - break; - } - if (str1[0] != str2[0]) { - break; - } - - str1 += 1; - str2 += 1; - } - - return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; -} - -MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) -{ - int result; - - result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1); - if (result != 0) { - return result; - } - - result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1); - if (result != 0) { - return result; - } - - return result; -} - -MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (src == NULL) { - return NULL; - } - - size_t sz = strlen(src)+1; - char* dst = (char*)ma_malloc(sz, pAllocationCallbacks); - if (dst == NULL) { - return NULL; - } - - ma_strcpy_s(dst, sz, src); - - return dst; -} - -MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t sz = wcslen(src)+1; - wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); - if (dst == NULL) { - return NULL; - } - - ma_wcscpy_s(dst, sz, src); - - return dst; -} - - -#include -static ma_result ma_result_from_errno(int e) -{ - switch (e) - { - case 0: return MA_SUCCESS; - #ifdef EPERM - case EPERM: return MA_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return MA_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return MA_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return MA_INTERRUPT; - #endif - #ifdef EIO - case EIO: return MA_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return MA_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return MA_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return MA_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return MA_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return MA_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return MA_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return MA_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return MA_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return MA_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return MA_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return MA_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return MA_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return MA_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return MA_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return MA_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return MA_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return MA_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return MA_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return MA_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return MA_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return MA_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return MA_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return MA_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return MA_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return MA_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return MA_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return MA_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return MA_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return MA_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return MA_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return MA_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return MA_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return MA_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return MA_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return MA_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return MA_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return MA_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return MA_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return MA_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return MA_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return MA_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return MA_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return MA_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return MA_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return MA_ERROR; - #endif - #ifdef EBADE - case EBADE: return MA_ERROR; - #endif - #ifdef EBADR - case EBADR: return MA_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return MA_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return MA_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return MA_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return MA_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return MA_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return MA_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return MA_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return MA_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return MA_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return MA_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return MA_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return MA_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return MA_ERROR; - #endif - #ifdef EADV - case EADV: return MA_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return MA_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return MA_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return MA_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return MA_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return MA_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return MA_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return MA_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return MA_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return MA_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return MA_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return MA_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return MA_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return MA_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return MA_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return MA_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return MA_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return MA_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return MA_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return MA_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return MA_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return MA_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return MA_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return MA_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return MA_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return MA_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return MA_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return MA_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return MA_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return MA_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return MA_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return MA_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return MA_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return MA_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return MA_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return MA_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return MA_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return MA_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return MA_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return MA_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return MA_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return MA_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return MA_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return MA_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return MA_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return MA_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return MA_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return MA_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return MA_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return MA_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return MA_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return MA_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return MA_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return MA_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return MA_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return MA_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return MA_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return MA_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return MA_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return MA_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return MA_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return MA_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return MA_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return MA_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return MA_ERROR; - #endif - default: return MA_ERROR; - } -} - -MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - - if (ppFile != NULL) { - *ppFile = NULL; /* Safety. */ - } - - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - ma_result result = ma_result_from_errno(errno); - if (result == MA_SUCCESS) { - result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ - } - - return result; - } -#endif - - return MA_SUCCESS; -} - - - -/* -_wfopen() isn't always available in all compilation environments. - - * Windows only. - * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). - * MinGW-64 (both 32- and 64-bit) seems to support it. - * MinGW wraps it in !defined(__STRICT_ANSI__). - * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). - -This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() -fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. -*/ -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define MA_HAS_WFOPEN - #endif -#endif - -MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; /* Safety. */ - } - - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_HAS_WFOPEN) - { - /* Use _wfopen() on Windows. */ - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return ma_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - /* - Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can - think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for - maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. - */ - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - - /* Get the length first. */ - MA_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return ma_result_from_errno(errno); - } - - pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return MA_OUT_OF_MEMORY; - } - - pFilePathTemp = pFilePath; - MA_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - - /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - - *ppFile = fopen(pFilePathMB, pOpenModeMB); - - ma_free(pFilePathMB, pAllocationCallbacks); - } - - if (*ppFile == NULL) { - return MA_ERROR; - } -#endif - - return MA_SUCCESS; -} - - - -static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) -{ -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX - MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); -#else - while (sizeInBytes > 0) { - ma_uint64 bytesToCopyNow = sizeInBytes; - if (bytesToCopyNow > MA_SIZE_MAX) { - bytesToCopyNow = MA_SIZE_MAX; - } - - MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */ - - sizeInBytes -= bytesToCopyNow; - dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow); - src = (const void*)((const ma_uint8*)src + bytesToCopyNow); - } -#endif -} - -static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) -{ -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX - MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); -#else - while (sizeInBytes > 0) { - ma_uint64 bytesToZeroNow = sizeInBytes; - if (bytesToZeroNow > MA_SIZE_MAX) { - bytesToZeroNow = MA_SIZE_MAX; - } - - MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */ - - sizeInBytes -= bytesToZeroNow; - dst = (void*)((ma_uint8*)dst + bytesToZeroNow); - } -#endif -} - - -/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ -static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x) -{ - x--; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - x++; - - return x; -} - -static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x) -{ - return ma_next_power_of_2(x) >> 1; -} - -static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x) -{ - unsigned int prev = ma_prev_power_of_2(x); - unsigned int next = ma_next_power_of_2(x); - if ((next - x) > (x - prev)) { - return prev; - } else { - return next; - } -} - -static MA_INLINE unsigned int ma_count_set_bits(unsigned int x) -{ - unsigned int count = 0; - while (x != 0) { - if (x & 1) { - count += 1; - } - - x = x >> 1; - } - - return count; -} - - - -/************************************************************************************************************************************************************** - -Allocation Callbacks - -**************************************************************************************************************************************************************/ -static void* ma__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_MALLOC(sz); -} - -static void* ma__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_REALLOC(p, sz); -} - -static void ma__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_FREE(p); -} - -static ma_allocation_callbacks ma_allocation_callbacks_init_default(void) -{ - ma_allocation_callbacks callbacks; - callbacks.pUserData = NULL; - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - - return callbacks; -} - -static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc) -{ - if (pDst == NULL) { - return MA_INVALID_ARGS; - } - - if (pSrc == NULL) { - *pDst = ma_allocation_callbacks_init_default(); - } else { - if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) { - *pDst = ma_allocation_callbacks_init_default(); - } else { - if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) { - return MA_INVALID_ARGS; /* Invalid allocation callbacks. */ - } else { - *pDst = *pSrc; - } - } - } - - return MA_SUCCESS; -} - - - - -/************************************************************************************************************************************************************** - -Logging - -**************************************************************************************************************************************************************/ -MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) -{ - switch (logLevel) - { - case MA_LOG_LEVEL_DEBUG: return "DEBUG"; - case MA_LOG_LEVEL_INFO: return "INFO"; - case MA_LOG_LEVEL_WARNING: return "WARNING"; - case MA_LOG_LEVEL_ERROR: return "ERROR"; - default: return "ERROR"; - } -} - -#if defined(MA_DEBUG_OUTPUT) - -/* Customize this to use a specific tag in __android_log_print() for debug output messages. */ -#ifndef MA_ANDROID_LOG_TAG -#define MA_ANDROID_LOG_TAG "miniaudio" -#endif - -void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage) -{ - (void)pUserData; - - /* Special handling for some platforms. */ - #if defined(MA_ANDROID) - { - /* Android. */ - __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage); - } - #else - { - /* Everything else. */ - printf("%s: %s", ma_log_level_to_string(level), pMessage); - } - #endif -} -#endif - -MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData) -{ - ma_log_callback callback; - - MA_ZERO_OBJECT(&callback); - callback.onLog = onLog; - callback.pUserData = pUserData; - - return callback; -} - - -MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog) -{ - if (pLog == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLog); - ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks); - - /* We need a mutex for thread safety. */ - #ifndef MA_NO_THREADING - { - ma_result result = ma_mutex_init(&pLog->lock); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - - /* If we're using debug output, enable it. */ - #if defined(MA_DEBUG_OUTPUT) - { - ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */ - } - #endif - - return MA_SUCCESS; -} - -MA_API void ma_log_uninit(ma_log* pLog) -{ - if (pLog == NULL) { - return; - } - -#ifndef MA_NO_THREADING - ma_mutex_uninit(&pLog->lock); -#endif -} - -static void ma_log_lock(ma_log* pLog) -{ -#ifndef MA_NO_THREADING - ma_mutex_lock(&pLog->lock); -#else - (void)pLog; -#endif -} - -static void ma_log_unlock(ma_log* pLog) -{ -#ifndef MA_NO_THREADING - ma_mutex_unlock(&pLog->lock); -#else - (void)pLog; -#endif -} - -MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback) -{ - ma_result result = MA_SUCCESS; - - if (pLog == NULL || callback.onLog == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - if (pLog->callbackCount == ma_countof(pLog->callbacks)) { - result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */ - } else { - pLog->callbacks[pLog->callbackCount] = callback; - pLog->callbackCount += 1; - } - } - ma_log_unlock(pLog); - - return result; -} - -MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback) -{ - if (pLog == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - ma_uint32 iLog; - for (iLog = 0; iLog < pLog->callbackCount; ) { - if (pLog->callbacks[iLog].onLog == callback.onLog) { - /* Found. Move everything down a slot. */ - ma_uint32 jLog; - for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) { - pLog->callbacks[jLog] = pLog->callbacks[jLog + 1]; - } - - pLog->callbackCount -= 1; - } else { - /* Not found. */ - iLog += 1; - } - } - } - ma_log_unlock(pLog); - - return MA_SUCCESS; -} - -MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage) -{ - if (pLog == NULL || pMessage == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - ma_uint32 iLog; - for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) { - if (pLog->callbacks[iLog].onLog) { - pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage); - } - } - } - ma_log_unlock(pLog); - - return MA_SUCCESS; -} - - -/* -We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a -logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf(). -*/ -#if defined(_MSC_VER) && _MSC_VER < 1900 -static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args) -{ -#if _MSC_VER > 1200 - return _vscprintf(format, args); -#else - int result; - char* pTempBuffer = NULL; - size_t tempBufferCap = 1024; - - if (format == NULL) { - errno = EINVAL; - return -1; - } - - for (;;) { - char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks); - if (pNewTempBuffer == NULL) { - ma_free(pTempBuffer, pAllocationCallbacks); - errno = ENOMEM; - return -1; /* Out of memory. */ - } - - pTempBuffer = pNewTempBuffer; - - result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); - ma_free(pTempBuffer, NULL); - - if (result != -1) { - break; /* Got it. */ - } - - /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ - tempBufferCap *= 2; - } - - return result; -#endif -} -#endif - -MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args) -{ - if (pLog == NULL || pFormat == NULL) { - return MA_INVALID_ARGS; - } - - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - { - ma_result result; - int length; - char pFormattedMessageStack[1024]; - char* pFormattedMessageHeap = NULL; - - /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ - length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); - if (length < 0) { - return MA_INVALID_OPERATION; /* An error occured when trying to convert the buffer. */ - } - - if ((size_t)length < sizeof(pFormattedMessageStack)) { - /* The string was written to the stack. */ - result = ma_log_post(pLog, level, pFormattedMessageStack); - } else { - /* The stack buffer was too small, try the heap. */ - pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks); - if (pFormattedMessageHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - - length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args); - if (length < 0) { - ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); - return MA_INVALID_OPERATION; - } - - result = ma_log_post(pLog, level, pFormattedMessageHeap); - ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); - } - - return result; - } - #else - { - /* - Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll - need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing - a fixed sized stack allocated buffer. - */ - #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */ - { - ma_result result; - int formattedLen; - char* pFormattedMessage = NULL; - va_list args2; - - #if _MSC_VER >= 1800 - { - va_copy(args2, args); - } - #else - { - args2 = args; - } - #endif - - formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); - va_end(args2); - - if (formattedLen <= 0) { - return MA_INVALID_OPERATION; - } - - pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks); - if (pFormattedMessage == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */ - #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */ - { - vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args); - } - #else - { - vsprintf(pFormattedMessage, pFormat, args); - } - #endif - - result = ma_log_post(pLog, level, pFormattedMessage); - ma_free(pFormattedMessage, &pLog->allocationCallbacks); - - return result; - } - #else - { - /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */ - (void)level; - (void)args; - - return MA_INVALID_OPERATION; - } - #endif - } - #endif -} - -MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) -{ - ma_result result; - va_list args; - - if (pLog == NULL || pFormat == NULL) { - return MA_INVALID_ARGS; - } - - va_start(args, pFormat); - { - result = ma_log_postv(pLog, level, pFormat, args); - } - va_end(args); - - return result; -} - - - -static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x) -{ - return (ma_uint8)(ma_clamp(x, -128, 127) + 128); -} - -static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) -{ - return (ma_int16)ma_clamp(x, -32768, 32767); -} - -static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) -{ - return (ma_int64)ma_clamp(x, -8388608, 8388607); -} - -static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) -{ - /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ - ma_int64 clipMin; - ma_int64 clipMax; - clipMin = -((ma_int64)2147483647 + 1); - clipMax = (ma_int64)2147483647; - - return (ma_int32)ma_clamp(x, clipMin, clipMax); -} - -static MA_INLINE float ma_clip_f32(float x) -{ - if (x < -1) return -1; - if (x > +1) return +1; - return x; -} - - -static MA_INLINE float ma_mix_f32(float x, float y, float a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE float ma_mix_f32_fast(float x, float y, float a) -{ - float r0 = (y - x); - float r1 = r0*a; - return x + r1; - /*return x + (y - x)*a;*/ -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a) -{ - return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a)); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) -{ - return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) -{ - return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a)); -} -#endif - - -static MA_INLINE double ma_mix_f64(double x, double y, double a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE double ma_mix_f64_fast(double x, double y, double a) -{ - return x + (y - x)*a; -} - -static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi) -{ - return lo + x*(hi-lo); -} - - -/* -Greatest common factor using Euclid's algorithm iteratively. -*/ -static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b) -{ - for (;;) { - if (b == 0) { - break; - } else { - ma_uint32 t = a; - a = b; - b = t % a; - } - } - - return a; -} - - -static ma_uint32 ma_ffs_32(ma_uint32 x) -{ - ma_uint32 i; - - /* Just a naive implementation just to get things working for now. Will optimize this later. */ - for (i = 0; i < 32; i += 1) { - if ((x & (1 << i)) != 0) { - return i; - } - } - - return i; -} - -static MA_INLINE ma_int16 ma_float_to_fixed_16(float x) -{ - return (ma_int16)(x * (1 << 8)); -} - - - -/* -Random Number Generation - -miniaudio uses the LCG random number generation algorithm. This is good enough for audio. - -Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across -multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for -miniaudio's purposes. -*/ -#ifndef MA_DEFAULT_LCG_SEED -#define MA_DEFAULT_LCG_SEED 4321 -#endif - -#define MA_LCG_M 2147483647 -#define MA_LCG_A 48271 -#define MA_LCG_C 0 - -static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ - -static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) -{ - MA_ASSERT(pLCG != NULL); - pLCG->state = seed; -} - -static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG) -{ - pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M; - return pLCG->state; -} - -static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG) -{ - return (ma_uint32)ma_lcg_rand_s32(pLCG); -} - -static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG) -{ - return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF); -} - -static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG) -{ - return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF; -} - -static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG) -{ - return (float)ma_lcg_rand_f64(pLCG); -} - -static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi) -{ - return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi); -} - -static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi) -{ - if (lo == hi) { - return lo; - } - - return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1); -} - - - -static MA_INLINE void ma_seed(ma_int32 seed) -{ - ma_lcg_seed(&g_maLCG, seed); -} - -static MA_INLINE ma_int32 ma_rand_s32(void) -{ - return ma_lcg_rand_s32(&g_maLCG); -} - -static MA_INLINE ma_uint32 ma_rand_u32(void) -{ - return ma_lcg_rand_u32(&g_maLCG); -} - -static MA_INLINE double ma_rand_f64(void) -{ - return ma_lcg_rand_f64(&g_maLCG); -} - -static MA_INLINE float ma_rand_f32(void) -{ - return ma_lcg_rand_f32(&g_maLCG); -} - -static MA_INLINE float ma_rand_range_f32(float lo, float hi) -{ - return ma_lcg_rand_range_f32(&g_maLCG, lo, hi); -} - -static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi) -{ - return ma_lcg_rand_range_s32(&g_maLCG, lo, hi); -} - - -static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax) -{ - return ma_rand_range_f32(ditherMin, ditherMax); -} - -static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax) -{ - float a = ma_rand_range_f32(ditherMin, 0); - float b = ma_rand_range_f32(0, ditherMax); - return a + b; -} - -static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax) -{ - if (ditherMode == ma_dither_mode_rectangle) { - return ma_dither_f32_rectangle(ditherMin, ditherMax); - } - if (ditherMode == ma_dither_mode_triangle) { - return ma_dither_f32_triangle(ditherMin, ditherMax); - } - - return 0; -} - -static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax) -{ - if (ditherMode == ma_dither_mode_rectangle) { - ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax); - return a; - } - if (ditherMode == ma_dither_mode_triangle) { - ma_int32 a = ma_rand_range_s32(ditherMin, 0); - ma_int32 b = ma_rand_range_s32(0, ditherMax); - return a + b; - } - - return 0; -} - - -/************************************************************************************************************************************************************** - -Atomics - -**************************************************************************************************************************************************************/ -/* c89atomic.h begin */ -#ifndef c89atomic_h -#define c89atomic_h -#if defined(__cplusplus) -extern "C" { -#endif -typedef signed char c89atomic_int8; -typedef unsigned char c89atomic_uint8; -typedef signed short c89atomic_int16; -typedef unsigned short c89atomic_uint16; -typedef signed int c89atomic_int32; -typedef unsigned int c89atomic_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 c89atomic_int64; - typedef unsigned __int64 c89atomic_uint64; -#else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long c89atomic_int64; - typedef unsigned long long c89atomic_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif -#endif -typedef int c89atomic_memory_order; -typedef unsigned char c89atomic_bool; -#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) -#ifdef _WIN32 -#ifdef _WIN64 -#define C89ATOMIC_64BIT -#else -#define C89ATOMIC_32BIT -#endif -#endif -#endif -#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) -#ifdef __GNUC__ -#ifdef __LP64__ -#define C89ATOMIC_64BIT -#else -#define C89ATOMIC_32BIT -#endif -#endif -#endif -#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) -#include -#if INTPTR_MAX == INT64_MAX -#define C89ATOMIC_64BIT -#else -#define C89ATOMIC_32BIT -#endif -#endif -#if defined(__x86_64__) || defined(_M_X64) -#define C89ATOMIC_X64 -#elif defined(__i386) || defined(_M_IX86) -#define C89ATOMIC_X86 -#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) -#define C89ATOMIC_ARM -#endif -#if defined(_MSC_VER) - #define C89ATOMIC_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define C89ATOMIC_INLINE __inline__ __attribute__((always_inline)) - #else - #define C89ATOMIC_INLINE inline __attribute__((always_inline)) - #endif -#elif defined(__WATCOMC__) || defined(__DMC__) - #define C89ATOMIC_INLINE __inline -#else - #define C89ATOMIC_INLINE -#endif -#define C89ATOMIC_HAS_8 -#define C89ATOMIC_HAS_16 -#define C89ATOMIC_HAS_32 -#define C89ATOMIC_HAS_64 -#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) - #define c89atomic_memory_order_relaxed 0 - #define c89atomic_memory_order_consume 1 - #define c89atomic_memory_order_acquire 2 - #define c89atomic_memory_order_release 3 - #define c89atomic_memory_order_acq_rel 4 - #define c89atomic_memory_order_seq_cst 5 - #if _MSC_VER < 1600 && defined(C89ATOMIC_X86) - #define C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY - #endif - #if _MSC_VER < 1600 - #undef C89ATOMIC_HAS_8 - #undef C89ATOMIC_HAS_16 - #endif - #if !defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #include - #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) - { - c89atomic_uint8 result = 0; - __asm { - mov ecx, dst - mov al, expected - mov dl, desired - lock cmpxchg [ecx], dl - mov result, al - } - return result; - } - #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) - { - c89atomic_uint16 result = 0; - __asm { - mov ecx, dst - mov ax, expected - mov dx, desired - lock cmpxchg [ecx], dx - mov result, ax - } - return result; - } - #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) - { - c89atomic_uint32 result = 0; - __asm { - mov ecx, dst - mov eax, expected - mov edx, desired - lock cmpxchg [ecx], edx - mov result, eax - } - return result; - } - #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) - { - c89atomic_uint32 resultEAX = 0; - c89atomic_uint32 resultEDX = 0; - __asm { - mov esi, dst - mov eax, dword ptr expected - mov edx, dword ptr expected + 4 - mov ebx, dword ptr desired - mov ecx, dword ptr desired + 4 - lock cmpxchg8b qword ptr [esi] - mov resultEAX, eax - mov resultEDX, edx - } - return ((c89atomic_uint64)resultEDX << 32) | resultEAX; - } - #endif - #else - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) - #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) - #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) - #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile c89atomic_int64*)dst, (c89atomic_int64)desired, (c89atomic_int64)expected) - #endif - #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xchg [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xchg [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xchg [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - (void)order; - return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); - } - #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - (void)order; - return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); - } - #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - (void)order; - return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src); - } - #endif - #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - (void)order; - return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); - } - #else - #endif - #endif - #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - do { - oldValue = *dst; - } while (c89atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xadd [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xadd [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xadd [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - (void)order; - return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); - } - #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - (void)order; - return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); - } - #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - (void)order; - return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); - } - #endif - #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - (void)order; - return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); - } - #else - #endif - #endif - #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order) - { - (void)order; - __asm { - lock add [esp], 0 - } - } - #else - #if defined(C89ATOMIC_X64) - #define c89atomic_thread_fence(order) __faststorefence(), (void)order - #else - static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order) - { - volatile c89atomic_uint32 barrier = 0; - c89atomic_fetch_add_explicit_32(&barrier, 0, order); - } - #endif - #endif - #define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst) - #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) - { - (void)order; - return c89atomic_compare_and_swap_8((volatile c89atomic_uint8*)ptr, 0, 0); - } - #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) - { - (void)order; - return c89atomic_compare_and_swap_16((volatile c89atomic_uint16*)ptr, 0, 0); - } - #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) - { - (void)order; - return c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)ptr, 0, 0); - } - #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) - { - (void)order; - return c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)ptr, 0, 0); - } - #endif - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) - #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) - #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) - #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) - #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue - src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue - src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue & src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue & src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue ^ src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue ^ src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue | src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue | src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order) - #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order) - #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order) - #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order) - #endif - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order) - #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order) - #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order) - #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order) - #endif - #if defined(C89ATOMIC_HAS_8) - typedef c89atomic_uint8 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order) - #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) - #else - typedef c89atomic_uint32 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_32(ptr, order) - #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_32(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_32(ptr, order) - #endif -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE - #define C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE - #define c89atomic_memory_order_relaxed __ATOMIC_RELAXED - #define c89atomic_memory_order_consume __ATOMIC_CONSUME - #define c89atomic_memory_order_acquire __ATOMIC_ACQUIRE - #define c89atomic_memory_order_release __ATOMIC_RELEASE - #define c89atomic_memory_order_acq_rel __ATOMIC_ACQ_REL - #define c89atomic_memory_order_seq_cst __ATOMIC_SEQ_CST - #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #define c89atomic_thread_fence(order) __atomic_thread_fence(order) - #define c89atomic_signal_fence(order) __atomic_signal_fence(order) - #define c89atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) - #define c89atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) - #define c89atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) - #define c89atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) - #define c89atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) - #define c89atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) - #define c89atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) - #define c89atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) - #define c89atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_compare_and_swap_8 (dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - typedef c89atomic_uint8 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order) - #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) -#else - #define c89atomic_memory_order_relaxed 1 - #define c89atomic_memory_order_consume 2 - #define c89atomic_memory_order_acquire 3 - #define c89atomic_memory_order_release 4 - #define c89atomic_memory_order_acq_rel 5 - #define c89atomic_memory_order_seq_cst 6 - #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #if defined(__GNUC__) - #define c89atomic_thread_fence(order) __sync_synchronize(), (void)order - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - if (order > c89atomic_memory_order_acquire) { - __sync_synchronize(); - } - return __sync_lock_test_and_set(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #else - #if defined(C89ATOMIC_X86) - #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") - #elif defined(C89ATOMIC_X64) - #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") - #else - #error Unsupported architecture. Please submit a feature request. - #endif - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) - { - c89atomic_uint8 result; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) - { - c89atomic_uint16 result; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) - { - c89atomic_uint32 result; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) - { - volatile c89atomic_uint64 result; - #if defined(C89ATOMIC_X86) - c89atomic_uint32 resultEAX; - c89atomic_uint32 resultEDX; - __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); - result = ((c89atomic_uint64)resultEDX << 32) | resultEAX; - #elif defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 result = 0; - (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 result = 0; - (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 result; - (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 result; - (void)order; - #if defined(C89ATOMIC_X86) - do { - result = *dst; - } while (c89atomic_compare_and_swap_64(dst, result, src) != result); - #elif defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 result; - (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 result; - (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 result; - (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - #if defined(C89ATOMIC_X86) - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; - (void)order; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - return oldValue; - #elif defined(C89ATOMIC_X64) - c89atomic_uint64 result; - (void)order; - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - return result; - #endif - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue - src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue - src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue & src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue & src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue ^ src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue ^ src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue | src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) - { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; - do { - oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue | src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) - { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) - { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) - { - (void)order; - return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0); - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) - { - (void)order; - return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0); - } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) - { - (void)order; - return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0); - } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) - { - (void)order; - return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0); - } - #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) - #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) - #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) - #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) - #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order) - #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order) - #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order) - #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order) - #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order) - #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order) - #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order) - #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order) - typedef c89atomic_uint8 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order) - #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) -#endif -#if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) - #if defined(C89ATOMIC_HAS_8) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) - { - c89atomic_uint8 expectedValue; - c89atomic_uint8 result; - (void)successOrder; - (void)failureOrder; - expectedValue = c89atomic_load_explicit_8(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_8(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - c89atomic_store_explicit_8(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(C89ATOMIC_HAS_16) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) - { - c89atomic_uint16 expectedValue; - c89atomic_uint16 result; - (void)successOrder; - (void)failureOrder; - expectedValue = c89atomic_load_explicit_16(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_16(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - c89atomic_store_explicit_16(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(C89ATOMIC_HAS_32) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) - { - c89atomic_uint32 expectedValue; - c89atomic_uint32 result; - (void)successOrder; - (void)failureOrder; - expectedValue = c89atomic_load_explicit_32(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_32(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - c89atomic_store_explicit_32(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(C89ATOMIC_HAS_64) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) - { - c89atomic_uint64 expectedValue; - c89atomic_uint64 result; - (void)successOrder; - (void)failureOrder; - expectedValue = c89atomic_load_explicit_64(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_64(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - c89atomic_store_explicit_64(expected, result, failureOrder); - return 0; - } - } - #endif - #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) -#endif -#if !defined(C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_8(volatile void* ptr) - { - (void)ptr; - return 1; - } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_16(volatile void* ptr) - { - (void)ptr; - return 1; - } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_32(volatile void* ptr) - { - (void)ptr; - return 1; - } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_64(volatile void* ptr) - { - (void)ptr; - #if defined(C89ATOMIC_64BIT) - return 1; - #else - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - return 1; - #else - return 0; - #endif - #endif - } -#endif -#if defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) - { - return c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr); - } - static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) - { - return (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order); - } - static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) - { - c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); - } - static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) - { - return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); - } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) - { - return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); - } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) - { - return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); - } - static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) - { - return (void*)c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired); - } -#elif defined(C89ATOMIC_32BIT) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) - { - return c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr); - } - static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) - { - return (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order); - } - static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) - { - c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); - } - static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) - { - return (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); - } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) - { - return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); - } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) - { - return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); - } - static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) - { - return (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired); - } -#else - #error Unsupported architecture. -#endif -#define c89atomic_flag_test_and_set(ptr) c89atomic_flag_test_and_set_explicit(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_flag_clear(ptr) c89atomic_flag_clear_explicit(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_64(ptr) c89atomic_test_and_set_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_16(ptr) c89atomic_clear_explicit_16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_32(ptr) c89atomic_clear_explicit_32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_64(ptr) c89atomic_clear_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_8( dst, src) c89atomic_store_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_16(dst, src) c89atomic_store_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_32(dst, src) c89atomic_store_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_64(dst, src) c89atomic_store_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_8( ptr) c89atomic_load_explicit_8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_16(ptr) c89atomic_load_explicit_16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_32(ptr) c89atomic_load_explicit_32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_64(ptr) c89atomic_load_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_16(dst, src) c89atomic_exchange_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_32(dst, src) c89atomic_exchange_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_64(dst, src) c89atomic_exchange_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_16( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_32( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_64( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_16(dst, src) c89atomic_fetch_add_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_32(dst, src) c89atomic_fetch_add_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_64(dst, src) c89atomic_fetch_add_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_16(dst, src) c89atomic_fetch_sub_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_32(dst, src) c89atomic_fetch_sub_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_64(dst, src) c89atomic_fetch_sub_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_16(dst, src) c89atomic_fetch_or_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_32(dst, src) c89atomic_fetch_or_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_64(dst, src) c89atomic_fetch_or_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_16(dst, src) c89atomic_fetch_xor_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_32(dst, src) c89atomic_fetch_xor_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_64(dst, src) c89atomic_fetch_xor_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_8( dst, src) c89atomic_fetch_and_explicit_8 (dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_16(dst, src) c89atomic_fetch_and_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_32(dst, src) c89atomic_fetch_and_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_64(dst, src) c89atomic_fetch_and_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_test_and_set_explicit_8( (c89atomic_uint8* )ptr, order) -#define c89atomic_test_and_set_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_test_and_set_explicit_16((c89atomic_uint16*)ptr, order) -#define c89atomic_test_and_set_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_test_and_set_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_test_and_set_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_test_and_set_explicit_64((c89atomic_uint64*)ptr, order) -#define c89atomic_clear_explicit_i8( ptr, order) c89atomic_clear_explicit_8( (c89atomic_uint8* )ptr, order) -#define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order) -#define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) -#define c89atomic_store_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_store_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_store_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_store_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_load_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order) -#define c89atomic_load_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order) -#define c89atomic_load_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_load_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_load_explicit_64((c89atomic_uint64*)ptr, order) -#define c89atomic_exchange_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_exchange_explicit_8 ((c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_exchange_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_exchange_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_exchange_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_exchange_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_exchange_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_exchange_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) -#define c89atomic_fetch_add_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_add_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_add_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_add_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_add_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_add_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_add_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_add_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_sub_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_sub_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_sub_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_sub_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_sub_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_sub_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_sub_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_sub_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_or_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_or_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_or_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_or_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_or_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_or_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_or_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_or_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_xor_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_xor_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_xor_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_xor_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_xor_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_xor_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_xor_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_xor_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_and_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_and_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_and_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_and_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_and_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_and_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_and_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_and_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_test_and_set_i8( ptr) c89atomic_test_and_set_explicit_i8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_i16(ptr) c89atomic_test_and_set_explicit_i16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_i32(ptr) c89atomic_test_and_set_explicit_i32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_i64(ptr) c89atomic_test_and_set_explicit_i64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i8( ptr) c89atomic_clear_explicit_i8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i16(ptr) c89atomic_clear_explicit_i16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i32(ptr) c89atomic_clear_explicit_i32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i64(ptr) c89atomic_clear_explicit_i64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i8( dst, src) c89atomic_store_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i16(dst, src) c89atomic_store_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i32(dst, src) c89atomic_store_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i64(dst, src) c89atomic_store_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i8( ptr) c89atomic_load_explicit_i8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i16(ptr) c89atomic_load_explicit_i16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i32(ptr) c89atomic_load_explicit_i32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i64(ptr) c89atomic_load_explicit_i64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i8( dst, src) c89atomic_exchange_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i16(dst, src) c89atomic_exchange_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i32(dst, src) c89atomic_exchange_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i64(dst, src) c89atomic_exchange_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i16(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i8( dst, src) c89atomic_fetch_add_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i16(dst, src) c89atomic_fetch_add_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i32(dst, src) c89atomic_fetch_add_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i64(dst, src) c89atomic_fetch_add_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i8( dst, src) c89atomic_fetch_sub_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i16(dst, src) c89atomic_fetch_sub_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i32(dst, src) c89atomic_fetch_sub_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i64(dst, src) c89atomic_fetch_sub_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i8( dst, src) c89atomic_fetch_or_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i16(dst, src) c89atomic_fetch_or_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i32(dst, src) c89atomic_fetch_or_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i64(dst, src) c89atomic_fetch_or_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i8( dst, src) c89atomic_fetch_xor_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i16(dst, src) c89atomic_fetch_xor_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i32(dst, src) c89atomic_fetch_xor_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i64(dst, src) c89atomic_fetch_xor_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i8( dst, src) c89atomic_fetch_and_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i16(dst, src) c89atomic_fetch_and_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i32(dst, src) c89atomic_fetch_and_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i64(dst, src) c89atomic_fetch_and_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_and_swap_i8( dst, expected, dedsired) (c89atomic_int8 )c89atomic_compare_and_swap_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )expected, (c89atomic_uint8 )dedsired) -#define c89atomic_compare_and_swap_i16(dst, expected, dedsired) (c89atomic_int16)c89atomic_compare_and_swap_16((c89atomic_uint16*)dst, (c89atomic_uint16)expected, (c89atomic_uint16)dedsired) -#define c89atomic_compare_and_swap_i32(dst, expected, dedsired) (c89atomic_int32)c89atomic_compare_and_swap_32((c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)dedsired) -#define c89atomic_compare_and_swap_i64(dst, expected, dedsired) (c89atomic_int64)c89atomic_compare_and_swap_64((c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)dedsired) -typedef union -{ - c89atomic_uint32 i; - float f; -} c89atomic_if32; -typedef union -{ - c89atomic_uint64 i; - double f; -} c89atomic_if64; -#define c89atomic_clear_explicit_f32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_clear_explicit_f64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) -static C89ATOMIC_INLINE void c89atomic_store_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) -{ - c89atomic_if32 x; - x.f = src; - c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); -} -static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) -{ - c89atomic_if64 x; - x.f = src; - c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); -} -static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile const float* ptr, c89atomic_memory_order order) -{ - c89atomic_if32 r; - r.i = c89atomic_load_explicit_32((volatile const c89atomic_uint32*)ptr, order); - return r.f; -} -static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile const double* ptr, c89atomic_memory_order order) -{ - c89atomic_if64 r; - r.i = c89atomic_load_explicit_64((volatile const c89atomic_uint64*)ptr, order); - return r.f; -} -static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) -{ - c89atomic_if32 r; - c89atomic_if32 x; - x.f = src; - r.i = c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); - return r.f; -} -static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) -{ - c89atomic_if64 r; - c89atomic_if64 x; - x.f = src; - r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); - return r.f; -} -#define c89atomic_clear_f32(ptr) (float )c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_f64(ptr) (double)c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_f64(dst, src) c89atomic_store_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_f32(ptr) (float )c89atomic_load_explicit_f32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_f64(ptr) (double)c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_f32(dst, src) (float )c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_f64(dst, src) (double)c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -typedef c89atomic_flag c89atomic_spinlock; -static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock) -{ - for (;;) { - if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) { - break; - } - while (c89atoimc_flag_load_explicit(pSpinlock, c89atomic_memory_order_relaxed) == 1) { - } - } -} -static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlock* pSpinlock) -{ - c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release); -} -#if defined(__cplusplus) -} -#endif -#endif -/* c89atomic.h end */ - - - -MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) -{ - /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */ - ma_uint64 outputFrameCount; - ma_uint64 preliminaryInputFrameCountFromFrac; - ma_uint64 preliminaryInputFrameCount; - - if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) { - return 0; - } - - if (sampleRateOut == sampleRateIn) { - return frameCountIn; - } - - outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn; - - preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut; - preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac; - - if (preliminaryInputFrameCount <= frameCountIn) { - outputFrameCount += 1; - } - - return outputFrameCount; -} - -#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE -#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 -#endif - - - -#if defined(MA_WIN32) -static ma_result ma_result_from_GetLastError(DWORD error) -{ - switch (error) - { - case ERROR_SUCCESS: return MA_SUCCESS; - case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST; - case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES; - case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY; - case ERROR_DISK_FULL: return MA_NO_SPACE; - case ERROR_HANDLE_EOF: return MA_AT_END; - case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK; - case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS; - case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED; - case ERROR_SEM_TIMEOUT: return MA_TIMEOUT; - case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST; - default: break; - } - - return MA_ERROR; -} -#endif /* MA_WIN32 */ - - -/******************************************************************************* - -Threading - -*******************************************************************************/ -static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) -{ - if (pSpinlock == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - if (c89atomic_exchange_explicit_32(pSpinlock, 1, c89atomic_memory_order_acquire) == 0) { - break; - } - - while (c89atomic_load_explicit_32(pSpinlock, c89atomic_memory_order_relaxed) == 1) { - if (yield) { - ma_yield(); - } - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock) -{ - return ma_spinlock_lock_ex(pSpinlock, MA_TRUE); -} - -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock) -{ - return ma_spinlock_lock_ex(pSpinlock, MA_FALSE); -} - -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) -{ - if (pSpinlock == NULL) { - return MA_INVALID_ARGS; - } - - c89atomic_store_explicit_32(pSpinlock, 0, c89atomic_memory_order_release); - return MA_SUCCESS; -} - - -#ifndef MA_NO_THREADING -#ifdef MA_WIN32 - #define MA_THREADCALL WINAPI - typedef unsigned long ma_thread_result; -#else - #define MA_THREADCALL - typedef void* ma_thread_result; -#endif -typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); - -#ifdef MA_WIN32 -static int ma_thread_priority_to_win32(ma_thread_priority priority) -{ - switch (priority) { - case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; - case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; - case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; - case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; - case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; - case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; - case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; - default: return THREAD_PRIORITY_NORMAL; - } -} - -static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, NULL); - if (*pThread == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); - - return MA_SUCCESS; -} - -static void ma_thread_wait__win32(ma_thread* pThread) -{ - WaitForSingleObject((HANDLE)*pThread, INFINITE); - CloseHandle((HANDLE)*pThread); -} - - -static ma_result ma_mutex_init__win32(ma_mutex* pMutex) -{ - *pMutex = CreateEventW(NULL, FALSE, TRUE, NULL); - if (*pMutex == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__win32(ma_mutex* pMutex) -{ - CloseHandle((HANDLE)*pMutex); -} - -static void ma_mutex_lock__win32(ma_mutex* pMutex) -{ - WaitForSingleObject((HANDLE)*pMutex, INFINITE); -} - -static void ma_mutex_unlock__win32(ma_mutex* pMutex) -{ - SetEvent((HANDLE)*pMutex); -} - - -static ma_result ma_event_init__win32(ma_event* pEvent) -{ - *pEvent = CreateEventW(NULL, FALSE, FALSE, NULL); - if (*pEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_event_uninit__win32(ma_event* pEvent) -{ - CloseHandle((HANDLE)*pEvent); -} - -static ma_result ma_event_wait__win32(ma_event* pEvent) -{ - DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_event_signal__win32(ma_event* pEvent) -{ - BOOL result = SetEvent((HANDLE)*pEvent); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) -{ - *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); - if (*pSemaphore == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) -{ - CloseHandle((HANDLE)*pSemaphore); -} - -static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) -{ - DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) -{ - BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} -#endif - - -#ifdef MA_POSIX -static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - int result; - pthread_attr_t* pAttr = NULL; - -#if !defined(__EMSCRIPTEN__) - /* Try setting the thread priority. It's not critical if anything fails here. */ - pthread_attr_t attr; - if (pthread_attr_init(&attr) == 0) { - int scheduler = -1; - if (priority == ma_thread_priority_idle) { -#ifdef SCHED_IDLE - if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { - scheduler = SCHED_IDLE; - } -#endif - } else if (priority == ma_thread_priority_realtime) { -#ifdef SCHED_FIFO - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { - scheduler = SCHED_FIFO; - } -#endif -#ifdef MA_LINUX - } else { - scheduler = sched_getscheduler(0); -#endif - } - - if (stackSize > 0) { - pthread_attr_setstacksize(&attr, stackSize); - } - - if (scheduler != -1) { - int priorityMin = sched_get_priority_min(scheduler); - int priorityMax = sched_get_priority_max(scheduler); - int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */ - - struct sched_param sched; - if (pthread_attr_getschedparam(&attr, &sched) == 0) { - if (priority == ma_thread_priority_idle) { - sched.sched_priority = priorityMin; - } else if (priority == ma_thread_priority_realtime) { - sched.sched_priority = priorityMax; - } else { - sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ - if (sched.sched_priority < priorityMin) { - sched.sched_priority = priorityMin; - } - if (sched.sched_priority > priorityMax) { - sched.sched_priority = priorityMax; - } - } - - if (pthread_attr_setschedparam(&attr, &sched) == 0) { - pAttr = &attr; - } - } - } - } -#else - /* It's the emscripten build. We'll have a few unused parameters. */ - (void)priority; - (void)stackSize; -#endif - - result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); - - /* The thread attributes object is no longer required. */ - if (pAttr != NULL) { - pthread_attr_destroy(pAttr); - } - - if (result != 0) { - return ma_result_from_errno(result); - } - - return MA_SUCCESS; -} - -static void ma_thread_wait__posix(ma_thread* pThread) -{ - pthread_join((pthread_t)*pThread, NULL); -} - - -static ma_result ma_mutex_init__posix(ma_mutex* pMutex) -{ - int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); - if (result != 0) { - return ma_result_from_errno(result); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__posix(ma_mutex* pMutex) -{ - pthread_mutex_destroy((pthread_mutex_t*)pMutex); -} - -static void ma_mutex_lock__posix(ma_mutex* pMutex) -{ - pthread_mutex_lock((pthread_mutex_t*)pMutex); -} - -static void ma_mutex_unlock__posix(ma_mutex* pMutex) -{ - pthread_mutex_unlock((pthread_mutex_t*)pMutex); -} - - -static ma_result ma_event_init__posix(ma_event* pEvent) -{ - int result; - - result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); - if (result != 0) { - return ma_result_from_errno(result); - } - - result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); - if (result != 0) { - pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); - return ma_result_from_errno(result); - } - - pEvent->value = 0; - return MA_SUCCESS; -} - -static void ma_event_uninit__posix(ma_event* pEvent) -{ - pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); - pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); -} - -static ma_result ma_event_wait__posix(ma_event* pEvent) -{ - pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); - { - while (pEvent->value == 0) { - pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); - } - pEvent->value = 0; /* Auto-reset. */ - } - pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); - - return MA_SUCCESS; -} - -static ma_result ma_event_signal__posix(ma_event* pEvent) -{ - pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); - { - pEvent->value = 1; - pthread_cond_signal((pthread_cond_t*)&pEvent->cond); - } - pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore) -{ - int result; - - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pSemaphore->value = initialValue; - - result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); - if (result != 0) { - return ma_result_from_errno(result); /* Failed to create mutex. */ - } - - result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); - if (result != 0) { - pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); - return ma_result_from_errno(result); /* Failed to create condition variable. */ - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return; - } - - pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); - pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); -} - -static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); - { - /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ - while (pSemaphore->value == 0) { - pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); - } - - pSemaphore->value -= 1; - } - pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); - - return MA_SUCCESS; -} - -static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); - { - pSemaphore->value += 1; - pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); - } - pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); - - return MA_SUCCESS; -} -#endif - -typedef struct -{ - ma_thread_entry_proc entryProc; - void* pData; - ma_allocation_callbacks allocationCallbacks; -} ma_thread_proxy_data; - -static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData) -{ - ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData; - ma_thread_entry_proc entryProc; - void* pEntryProcData; - ma_thread_result result; - - #if defined(MA_ON_THREAD_ENTRY) - MA_ON_THREAD_ENTRY - #endif - - entryProc = pProxyData->entryProc; - pEntryProcData = pProxyData->pData; - - /* Free the proxy data before getting into the real thread entry proc. */ - ma_free(pProxyData, &pProxyData->allocationCallbacks); - - result = entryProc(pEntryProcData); - - #if defined(MA_ON_THREAD_EXIT) - MA_ON_THREAD_EXIT - #endif - - return result; -} - -static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_thread_proxy_data* pProxyData; - - if (pThread == NULL || entryProc == NULL) { - return MA_INVALID_ARGS; - } - - pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ - if (pProxyData == NULL) { - return MA_OUT_OF_MEMORY; - } - - pProxyData->entryProc = entryProc; - pProxyData->pData = pData; - ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); - -#ifdef MA_WIN32 - result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#endif -#ifdef MA_POSIX - result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#endif - - if (result != MA_SUCCESS) { - ma_free(pProxyData, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; -} - -static void ma_thread_wait(ma_thread* pThread) -{ - if (pThread == NULL) { - return; - } - -#ifdef MA_WIN32 - ma_thread_wait__win32(pThread); -#endif -#ifdef MA_POSIX - ma_thread_wait__posix(pThread); -#endif -} - - -MA_API ma_result ma_mutex_init(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#ifdef MA_WIN32 - return ma_mutex_init__win32(pMutex); -#endif -#ifdef MA_POSIX - return ma_mutex_init__posix(pMutex); -#endif -} - -MA_API void ma_mutex_uninit(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - return; - } - -#ifdef MA_WIN32 - ma_mutex_uninit__win32(pMutex); -#endif -#ifdef MA_POSIX - ma_mutex_uninit__posix(pMutex); -#endif -} - -MA_API void ma_mutex_lock(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#ifdef MA_WIN32 - ma_mutex_lock__win32(pMutex); -#endif -#ifdef MA_POSIX - ma_mutex_lock__posix(pMutex); -#endif -} - -MA_API void ma_mutex_unlock(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; -} - -#ifdef MA_WIN32 - ma_mutex_unlock__win32(pMutex); -#endif -#ifdef MA_POSIX - ma_mutex_unlock__posix(pMutex); -#endif -} - - -MA_API ma_result ma_event_init(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#ifdef MA_WIN32 - return ma_event_init__win32(pEvent); -#endif -#ifdef MA_POSIX - return ma_event_init__posix(pEvent); -#endif -} - -#if 0 -static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_event* pEvent; - - if (ppEvent == NULL) { - return MA_INVALID_ARGS; - } - - *ppEvent = NULL; - - pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks); - if (pEvent == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_event_init(pEvent); - if (result != MA_SUCCESS) { - ma_free(pEvent, pAllocationCallbacks); - return result; - } - - *ppEvent = pEvent; - return result; -} -#endif - -MA_API void ma_event_uninit(ma_event* pEvent) -{ - if (pEvent == NULL) { - return; - } - -#ifdef MA_WIN32 - ma_event_uninit__win32(pEvent); -#endif -#ifdef MA_POSIX - ma_event_uninit__posix(pEvent); -#endif -} - -#if 0 -static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pEvent == NULL) { - return; - } - - ma_event_uninit(pEvent); - ma_free(pEvent, pAllocationCallbacks); -} -#endif - -MA_API ma_result ma_event_wait(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#ifdef MA_WIN32 - return ma_event_wait__win32(pEvent); -#endif -#ifdef MA_POSIX - return ma_event_wait__posix(pEvent); -#endif -} - -MA_API ma_result ma_event_signal(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#ifdef MA_WIN32 - return ma_event_signal__win32(pEvent); -#endif -#ifdef MA_POSIX - return ma_event_signal__posix(pEvent); -#endif -} - - -MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#ifdef MA_WIN32 - return ma_semaphore_init__win32(initialValue, pSemaphore); -#endif -#ifdef MA_POSIX - return ma_semaphore_init__posix(initialValue, pSemaphore); -#endif -} - -MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#ifdef MA_WIN32 - ma_semaphore_uninit__win32(pSemaphore); -#endif -#ifdef MA_POSIX - ma_semaphore_uninit__posix(pSemaphore); -#endif -} - -MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#ifdef MA_WIN32 - return ma_semaphore_wait__win32(pSemaphore); -#endif -#ifdef MA_POSIX - return ma_semaphore_wait__posix(pSemaphore); -#endif -} - -MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#ifdef MA_WIN32 - return ma_semaphore_release__win32(pSemaphore); -#endif -#ifdef MA_POSIX - return ma_semaphore_release__posix(pSemaphore); -#endif -} -#else -/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ -#ifndef MA_NO_DEVICE_IO -#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; -#endif -#endif /* MA_NO_THREADING */ - - - -#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF - -MA_API ma_result ma_fence_init(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFence); - pFence->counter = 0; - - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_init(&pFence->e); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - - return MA_SUCCESS; -} - -MA_API void ma_fence_uninit(ma_fence* pFence) -{ - if (pFence == NULL) { - return; - } - - #ifndef MA_NO_THREADING - { - ma_event_uninit(&pFence->e); - } - #endif - - MA_ZERO_OBJECT(pFence); -} - -MA_API ma_result ma_fence_acquire(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); - ma_uint32 newCounter = oldCounter + 1; - - /* Make sure we're not about to exceed our maximum value. */ - if (newCounter > MA_FENCE_COUNTER_MAX) { - MA_ASSERT(MA_FALSE); - return MA_OUT_OF_RANGE; - } - - if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { - return MA_SUCCESS; - } else { - if (oldCounter == MA_FENCE_COUNTER_MAX) { - MA_ASSERT(MA_FALSE); - return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ - } - } - } - - /* Should never get here. */ - /*return MA_SUCCESS;*/ -} - -MA_API ma_result ma_fence_release(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); - ma_uint32 newCounter = oldCounter - 1; - - if (oldCounter == 0) { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ - } - - if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { - #ifndef MA_NO_THREADING - { - if (newCounter == 0) { - ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */ - } - } - #endif - - return MA_SUCCESS; - } else { - if (oldCounter == 0) { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ - } - } - } - - /* Should never get here. */ - /*return MA_SUCCESS;*/ -} - -MA_API ma_result ma_fence_wait(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 counter; - - counter = c89atomic_load_32(&pFence->counter); - if (counter == 0) { - /* - Counter has hit zero. By the time we get here some other thread may have acquired the - fence again, but that is where the caller needs to take care with how they se the fence. - */ - return MA_SUCCESS; - } - - /* Getting here means the counter is > 0. We'll need to wait for something to happen. */ - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_wait(&pFence->e); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - } - - /* Should never get here. */ - /*return MA_INVALID_OPERATION;*/ -} - - -MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification) -{ - ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification; - - if (pNotification == NULL) { - return MA_INVALID_ARGS; - } - - if (pNotificationCallbacks->onSignal == NULL) { - return MA_NOT_IMPLEMENTED; - } - - pNotificationCallbacks->onSignal(pNotification); - return MA_INVALID_ARGS; -} - - -static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification) -{ - ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE; -} - -MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll) -{ - if (pNotificationPoll == NULL) { - return MA_INVALID_ARGS; - } - - pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal; - pNotificationPoll->signalled = MA_FALSE; - - return MA_SUCCESS; -} - -MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll) -{ - if (pNotificationPoll == NULL) { - return MA_FALSE; - } - - return pNotificationPoll->signalled; -} - - -static void ma_async_notification_event__on_signal(ma_async_notification* pNotification) -{ - ma_async_notification_event_signal((ma_async_notification_event*)pNotification); -} - -MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal; - - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_init(&pNotificationEvent->e); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - ma_event_uninit(&pNotificationEvent->e); - return MA_SUCCESS; - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - return ma_event_wait(&pNotificationEvent->e); - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - return ma_event_signal(&pNotificationEvent->e); - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - - - -/************************************************************************************************************************************************************ - -Job Queue - -************************************************************************************************************************************************************/ -MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity) -{ - ma_slot_allocator_config config; - - MA_ZERO_OBJECT(&config); - config.capacity = capacity; - - return config; -} - - -static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity) -{ - ma_uint32 cap = slotCapacity / 32; - if ((slotCapacity % 32) != 0) { - cap += 1; - } - - return cap; -} - -static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator) -{ - return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity); -} - - -typedef struct -{ - size_t sizeInBytes; - size_t groupsOffset; - size_t slotsOffset; -} ma_slot_allocator_heap_layout; - -static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->capacity == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Groups. */ - pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group)); - - /* Slots. */ - pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_slot_allocator_heap_layout layout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_slot_allocator_get_heap_layout(pConfig, &layout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = layout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator) -{ - ma_result result; - ma_slot_allocator_heap_layout heapLayout; - - if (pAllocator == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pAllocator); - - if (pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pAllocator->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset); - pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset); - pAllocator->capacity = pConfig->capacity; - - return MA_SUCCESS; -} - -MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap allocation. */ - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pAllocator->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocator == NULL) { - return; - } - - if (pAllocator->_ownsHeap) { - ma_free(pAllocator->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot) -{ - ma_uint32 iAttempt; - const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */ - - if (pAllocator == NULL || pSlot == NULL) { - return MA_INVALID_ARGS; - } - - for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) { - /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */ - ma_uint32 iGroup; - for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) { - /* CAS */ - for (;;) { - ma_uint32 oldBitfield; - ma_uint32 newBitfield; - ma_uint32 bitOffset; - - oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ - - /* Fast check to see if anything is available. */ - if (oldBitfield == 0xFFFFFFFF) { - break; /* No available bits in this bitfield. */ - } - - bitOffset = ma_ffs_32(~oldBitfield); - MA_ASSERT(bitOffset < 32); - - newBitfield = oldBitfield | (1 << bitOffset); - - if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - ma_uint32 slotIndex; - - /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ - c89atomic_fetch_add_32(&pAllocator->count, 1); - - /* The slot index is required for constructing the output value. */ - slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ - if (slotIndex >= pAllocator->capacity) { - return MA_OUT_OF_MEMORY; - } - - /* Increment the reference count before constructing the output value. */ - pAllocator->pSlots[slotIndex] += 1; - - /* Construct the output value. */ - *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex); - - return MA_SUCCESS; - } - } - } - - /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */ - if (pAllocator->count < pAllocator->capacity) { - ma_yield(); - } else { - return MA_OUT_OF_MEMORY; - } - } - - /* We couldn't find a slot within the maximum number of attempts. */ - return MA_OUT_OF_MEMORY; -} - -MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot) -{ - ma_uint32 iGroup; - ma_uint32 iBit; - - if (pAllocator == NULL) { - return MA_INVALID_ARGS; - } - - iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */ - iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */ - - if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ - - while (c89atomic_load_32(&pAllocator->count) > 0) { - /* CAS */ - ma_uint32 oldBitfield; - ma_uint32 newBitfield; - - oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ - newBitfield = oldBitfield & ~(1 << iBit); - - /* Debugging for checking for double-frees. */ - #if defined(MA_DEBUG_OUTPUT) - { - if ((oldBitfield & (1 << iBit)) == 0) { - MA_ASSERT(MA_FALSE); /* Double free detected.*/ - } - } - #endif - - if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - c89atomic_fetch_sub_32(&pAllocator->count, 1); - return MA_SUCCESS; - } - } - - /* Getting here means there are no allocations available for freeing. */ - return MA_INVALID_OPERATION; -} - - -#define MA_JOB_ID_NONE ~((ma_uint64)0) -#define MA_JOB_SLOT_NONE (ma_uint16)(~0) - -static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc) -{ - return (ma_uint32)(toc >> 32); -} - -static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) -{ - return (ma_uint16)(toc & 0x0000FFFF); -} - -static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) -{ - return (ma_uint16)((toc & 0xFFFF0000) >> 16); -} - -static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) -{ - return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc); -} - -static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount) -{ - /* Clear the reference count first. */ - toc = toc & ~((ma_uint64)0xFFFFFFFF << 32); - toc = toc | ((ma_uint64)refcount << 32); - - return toc; -} - - -MA_API ma_job ma_job_init(ma_uint16 code) -{ - ma_job job; - - MA_ZERO_OBJECT(&job); - job.toc.breakup.code = code; - job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */ - job.next = MA_JOB_ID_NONE; - - return job; -} - - -static ma_result ma_job_process__noop(ma_job* pJob); -static ma_result ma_job_process__quit(ma_job* pJob); -static ma_result ma_job_process__custom(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob); - -#if !defined(MA_NO_DEVICE_IO) -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob); -#endif - -static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = -{ - /* Miscellaneous. */ - ma_job_process__quit, /* MA_JOB_TYPE_QUIT */ - ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */ - - /* Resource Manager. */ - ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */ - ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */ - ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */ - ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */ - ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */ - ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */ - - /* Device. */ -#if !defined(MA_NO_DEVICE_IO) - ma_job_process__device__aaudio_reroute /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/ -#endif -}; - -MA_API ma_result ma_job_process(ma_job* pJob) -{ - if (pJob == NULL) { - return MA_INVALID_ARGS; - } - - if (pJob->toc.breakup.code > MA_JOB_TYPE_COUNT) { - return MA_INVALID_OPERATION; - } - - return g_jobVTable[pJob->toc.breakup.code](pJob); -} - -static ma_result ma_job_process__noop(ma_job* pJob) -{ - MA_ASSERT(pJob != NULL); - - /* No-op. */ - (void)pJob; - - return MA_SUCCESS; -} - -static ma_result ma_job_process__quit(ma_job* pJob) -{ - return ma_job_process__noop(pJob); -} - -static ma_result ma_job_process__custom(ma_job* pJob) -{ - MA_ASSERT(pJob != NULL); - - /* No-op if there's no callback. */ - if (pJob->data.custom.proc == NULL) { - return MA_SUCCESS; - } - - return pJob->data.custom.proc(pJob); -} - - - -MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity) -{ - ma_job_queue_config config; - - config.flags = flags; - config.capacity = capacity; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t allocatorOffset; - size_t jobsOffset; -} ma_job_queue_heap_layout; - -static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->capacity == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Allocator. */ - { - ma_slot_allocator_config allocatorConfig; - size_t allocatorHeapSizeInBytes; - - allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); - result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes; - } - - /* Jobs. */ - pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_job_queue_heap_layout layout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_job_queue_get_heap_layout(pConfig, &layout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = layout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue) -{ - ma_result result; - ma_job_queue_heap_layout heapLayout; - ma_slot_allocator_config allocatorConfig; - - if (pQueue == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pQueue); - - result = ma_job_queue_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pQueue->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pQueue->flags = pConfig->flags; - pQueue->capacity = pConfig->capacity; - pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset); - - allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); - result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator); - if (result != MA_SUCCESS) { - return result; - } - - /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_init(0, &pQueue->sem); - } - #else - { - /* Threading is disabled and we've requested non-blocking mode. */ - return MA_INVALID_OPERATION; - } - #endif - } - - /* - Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is - just a dummy item for giving us the first item in the list which is stored in the "next" member. - */ - ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */ - pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE; - pQueue->tail = pQueue->head; - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pQueue->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pQueue == NULL) { - return; - } - - /* All we need to do is uninitialize the semaphore. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_uninit(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks); - - if (pQueue->_ownsHeap) { - ma_free(pQueue->_pHeap, pAllocationCallbacks); - } -} - -static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) -{ - /* The new counter is taken from the expected value. */ - return c89atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; -} - -MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) -{ - /* - Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors - */ - ma_result result; - ma_uint64 slot; - ma_uint64 tail; - ma_uint64 next; - - if (pQueue == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - /* We need a new slot. */ - result = ma_slot_allocator_alloc(&pQueue->allocator, &slot); - if (result != MA_SUCCESS) { - return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */ - } - - /* At this point we should have a slot to place the job. */ - MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity); - - /* We need to put the job into memory before we do anything. */ - pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob; - pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */ - pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */ - pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */ - - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_lock(&pQueue->lock); - #endif - { - /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ - for (;;) { - tail = c89atomic_load_64(&pQueue->tail); - next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); - - if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->tail))) { - if (ma_job_extract_slot(next) == 0xFFFF) { - if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { - break; - } - } else { - ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); - } - } - } - ma_job_queue_cas(&pQueue->tail, tail, slot); - } - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - - - /* Signal the semaphore as the last step if we're using synchronous mode. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_release(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) -{ - ma_uint64 head; - ma_uint64 tail; - ma_uint64 next; - - if (pQueue == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - /* If we're running in synchronous mode we'll need to wait on a semaphore. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_wait(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_lock(&pQueue->lock); - #endif - { - /* - BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below - is stored. One thread can fall through to the freeing of this item while another is still using "head" for the - retrieval of the "next" variable. - - The slot allocator might need to make use of some reference counting to ensure it's only truely freed when - there are no more references to the item. This must be fixed before removing these locks. - */ - - /* Now we need to remove the root item from the list. */ - for (;;) { - head = c89atomic_load_64(&pQueue->head); - tail = c89atomic_load_64(&pQueue->tail); - next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); - - if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->head))) { - if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { - if (ma_job_extract_slot(next) == 0xFFFF) { - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - return MA_NO_DATA_AVAILABLE; - } - ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); - } else { - *pJob = pQueue->pJobs[ma_job_extract_slot(next)]; - if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) { - break; - } - } - } - } - } - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - - ma_slot_allocator_free(&pQueue->allocator, head); - - /* - If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We - could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as - possible. - */ - if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) { - ma_job_queue_post(pQueue, pJob); - return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */ - } - - return MA_SUCCESS; -} - - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DEVICE I/O -========== - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#ifndef MA_NO_DEVICE_IO -#ifdef MA_WIN32 - #include - #include - #include -#endif - -#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) - #include /* For mach_absolute_time() */ -#endif - -#ifdef MA_POSIX - #include - #include - #include -#endif - -/* -Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When -using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing -compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply -disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am -not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere. -*/ -/*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/ - -/* Disable run-time linking on certain backends. */ -#ifndef MA_NO_RUNTIME_LINKING - #if defined(MA_EMSCRIPTEN) - #define MA_NO_RUNTIME_LINKING - #endif -#endif - - -MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) -{ - if (pDeviceInfo == NULL) { - return; - } - - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; - } -} - - -MA_API const char* ma_get_backend_name(ma_backend backend) -{ - switch (backend) - { - case ma_backend_wasapi: return "WASAPI"; - case ma_backend_dsound: return "DirectSound"; - case ma_backend_winmm: return "WinMM"; - case ma_backend_coreaudio: return "Core Audio"; - case ma_backend_sndio: return "sndio"; - case ma_backend_audio4: return "audio(4)"; - case ma_backend_oss: return "OSS"; - case ma_backend_pulseaudio: return "PulseAudio"; - case ma_backend_alsa: return "ALSA"; - case ma_backend_jack: return "JACK"; - case ma_backend_aaudio: return "AAudio"; - case ma_backend_opensl: return "OpenSL|ES"; - case ma_backend_webaudio: return "Web Audio"; - case ma_backend_custom: return "Custom"; - case ma_backend_null: return "Null"; - default: return "Unknown"; - } -} - -MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) -{ - /* - This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers - about some enums not being handled by the switch statement. - */ - switch (backend) - { - case ma_backend_wasapi: - #if defined(MA_HAS_WASAPI) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_dsound: - #if defined(MA_HAS_DSOUND) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_winmm: - #if defined(MA_HAS_WINMM) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_coreaudio: - #if defined(MA_HAS_COREAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_sndio: - #if defined(MA_HAS_SNDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_audio4: - #if defined(MA_HAS_AUDIO4) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_oss: - #if defined(MA_HAS_OSS) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_pulseaudio: - #if defined(MA_HAS_PULSEAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_alsa: - #if defined(MA_HAS_ALSA) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_jack: - #if defined(MA_HAS_JACK) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_aaudio: - #if defined(MA_HAS_AAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_opensl: - #if defined(MA_HAS_OPENSL) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_webaudio: - #if defined(MA_HAS_WEBAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_custom: - #if defined(MA_HAS_CUSTOM) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_null: - #if defined(MA_HAS_NULL) - return MA_TRUE; - #else - return MA_FALSE; - #endif - - default: return MA_FALSE; - } -} - -MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount) -{ - size_t backendCount; - size_t iBackend; - ma_result result = MA_SUCCESS; - - if (pBackendCount == NULL) { - return MA_INVALID_ARGS; - } - - backendCount = 0; - - for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) { - ma_backend backend = (ma_backend)iBackend; - - if (ma_is_backend_enabled(backend)) { - /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */ - if (backendCount == backendCap) { - result = MA_NO_SPACE; - break; - } else { - pBackends[backendCount] = backend; - backendCount += 1; - } - } - } - - if (pBackendCount != NULL) { - *pBackendCount = backendCount; - } - - return result; -} - -MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) -{ - switch (backend) - { - case ma_backend_wasapi: return MA_TRUE; - case ma_backend_dsound: return MA_FALSE; - case ma_backend_winmm: return MA_FALSE; - case ma_backend_coreaudio: return MA_FALSE; - case ma_backend_sndio: return MA_FALSE; - case ma_backend_audio4: return MA_FALSE; - case ma_backend_oss: return MA_FALSE; - case ma_backend_pulseaudio: return MA_FALSE; - case ma_backend_alsa: return MA_FALSE; - case ma_backend_jack: return MA_FALSE; - case ma_backend_aaudio: return MA_FALSE; - case ma_backend_opensl: return MA_FALSE; - case ma_backend_webaudio: return MA_FALSE; - case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */ - case ma_backend_null: return MA_FALSE; - default: return MA_FALSE; - } -} - - - -#ifdef MA_WIN32 -/* WASAPI error codes. */ -#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) -#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) -#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003) -#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004) -#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005) -#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006) -#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007) -#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008) -#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009) -#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A) -#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B) -#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C) -#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D) -#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E) -#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F) -#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010) -#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011) -#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012) -#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013) -#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014) -#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015) -#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016) -#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017) -#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018) -#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019) -#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020) -#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021) -#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022) -#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023) -#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024) -#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025) -#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026) -#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027) -#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028) -#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029) -#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030) -#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040) -#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001) -#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002) -#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003) - -#define MA_DS_OK ((HRESULT)0) -#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A) -#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A) -#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E) -#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/ -#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032) -#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/ -#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046) -#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/ -#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064) -#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/ -#define MA_DSERR_NODRIVER ((HRESULT)0x88780078) -#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082) -#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/ -#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096) -#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0) -#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA) -#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/ -#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/ -#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4) -#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE) -#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8) -#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2) -#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161) -#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC) - -static ma_result ma_result_from_HRESULT(HRESULT hr) -{ - switch (hr) - { - case NOERROR: return MA_SUCCESS; - /*case S_OK: return MA_SUCCESS;*/ - - case E_POINTER: return MA_INVALID_ARGS; - case E_UNEXPECTED: return MA_ERROR; - case E_NOTIMPL: return MA_NOT_IMPLEMENTED; - case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY; - case E_INVALIDARG: return MA_INVALID_ARGS; - case E_NOINTERFACE: return MA_API_NOT_FOUND; - case E_HANDLE: return MA_INVALID_ARGS; - case E_ABORT: return MA_ERROR; - case E_FAIL: return MA_ERROR; - case E_ACCESSDENIED: return MA_ACCESS_DENIED; - - /* WASAPI */ - case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED; - case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; - case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE; - case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED; - case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG; - case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED; - case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY; - case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST; - case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED; - case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED; - case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED; - case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR; - case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR; - case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY; - case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA; - case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE; - case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS; - case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR; - - /* DirectSound */ - /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */ - case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS; - case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE; - case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION; - /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */ - case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION; - /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */ - case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION; - /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */ - case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED; - /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */ - case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND; - case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; - case MA_DSERR_NOAGGREGATION: return MA_ERROR; - case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE; - case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED; - case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED; - /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */ - /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */ - case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE; - case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION; - case MA_DSERR_SENDLOOP: return MA_DEADLOCK; - case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS; - case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE; - case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE; - - default: return MA_ERROR; - } -} - -typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); -typedef void (WINAPI * MA_PFN_CoUninitialize)(void); -typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); -typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv); -typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar); -typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax); - -typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); -typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); - -#if defined(MA_WIN32_DESKTOP) -/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */ -typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult); -typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); -typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); -#endif /* MA_WIN32_DESKTOP */ -#endif /* MA_WIN32 */ - - -#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" -#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" - - - - -/******************************************************************************* - -Timing - -*******************************************************************************/ -#ifdef MA_WIN32 - static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - void ma_timer_init(ma_timer* pTimer) - { - LARGE_INTEGER counter; - - if (g_ma_TimerFrequency.QuadPart == 0) { - QueryPerformanceFrequency(&g_ma_TimerFrequency); - } - - QueryPerformanceCounter(&counter); - pTimer->counter = counter.QuadPart; - } - - double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - LARGE_INTEGER counter; - if (!QueryPerformanceCounter(&counter)) { - return 0; - } - - return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; - } -#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) - static ma_uint64 g_ma_TimerFrequency = 0; - static void ma_timer_init(ma_timer* pTimer) - { - mach_timebase_info_data_t baseTime; - mach_timebase_info(&baseTime); - g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer; - - pTimer->counter = mach_absolute_time(); - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter = mach_absolute_time(); - ma_uint64 oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency; - } -#elif defined(MA_EMSCRIPTEN) - static MA_INLINE void ma_timer_init(ma_timer* pTimer) - { - pTimer->counterD = emscripten_get_now(); - } - - static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ - } -#else - #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L - #if defined(CLOCK_MONOTONIC) - #define MA_CLOCK_ID CLOCK_MONOTONIC - #else - #define MA_CLOCK_ID CLOCK_REALTIME - #endif - - static void ma_timer_init(ma_timer* pTimer) - { - struct timespec newTime; - clock_gettime(MA_CLOCK_ID, &newTime); - - pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter; - ma_uint64 oldTimeCounter; - - struct timespec newTime; - clock_gettime(MA_CLOCK_ID, &newTime); - - newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / 1000000000.0; - } - #else - static void ma_timer_init(ma_timer* pTimer) - { - struct timeval newTime; - gettimeofday(&newTime, NULL); - - pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter; - ma_uint64 oldTimeCounter; - - struct timeval newTime; - gettimeofday(&newTime, NULL); - - newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec; - oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / 1000000.0; - } - #endif -#endif - - -/******************************************************************************* - -Dynamic Linking - -*******************************************************************************/ -MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) -{ - ma_handle handle; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); - -#ifdef _WIN32 -#ifdef MA_WIN32_DESKTOP - handle = (ma_handle)LoadLibraryA(filename); -#else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } -#endif -#else - handle = (ma_handle)dlopen(filename, RTLD_NOW); -#endif - - /* - I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority - backend is a deliberate design choice. Instead I'm logging it as an informational message. - */ - if (handle == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); - } - - (void)pContext; /* It's possible for pContext to be unused. */ - return handle; -} - -MA_API void ma_dlclose(ma_context* pContext, ma_handle handle) -{ -#ifdef _WIN32 - FreeLibrary((HMODULE)handle); -#else - dlclose((void*)handle); -#endif - - (void)pContext; -} - -MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol) -{ - ma_proc proc; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); - -#ifdef _WIN32 - proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); -#else -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" -#endif - proc = (ma_proc)dlsym((void*)handle, symbol); -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #pragma GCC diagnostic pop -#endif -#endif - - if (proc == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); - } - - (void)pContext; /* It's possible for pContext to be unused. */ - return proc; -} - - -#if 0 -static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) -{ - ma_uint32 closestRate = 0; - ma_uint32 closestDiff = 0xFFFFFFFF; - size_t iStandardRate; - - for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; - ma_uint32 diff; - - if (sampleRateIn > standardRate) { - diff = sampleRateIn - standardRate; - } else { - diff = standardRate - sampleRateIn; - } - - if (diff == 0) { - return standardRate; /* The input sample rate is a standard rate. */ - } - - if (closestDiff > diff) { - closestDiff = diff; - closestRate = standardRate; - } - } - - return closestRate; -} -#endif - - -static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (!pDevice->noDisableDenormals) { - return ma_disable_denormals(); - } else { - return 0; - } -} - -static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState) -{ - MA_ASSERT(pDevice != NULL); - - if (!pDevice->noDisableDenormals) { - ma_restore_denormals(prevState); - } else { - /* Do nothing. */ - (void)prevState; - } -} - -static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) -{ - ma_device_notification notification; - - MA_ZERO_OBJECT(¬ification); - notification.pDevice = pDevice; - notification.type = type; - - return notification; -} - -static void ma_device__on_notification(ma_device_notification notification) -{ - MA_ASSERT(notification.pDevice != NULL); - - if (notification.pDevice->onNotification != NULL) { - notification.pDevice->onNotification(¬ification); - } - - /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */ - if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) { - notification.pDevice->onStop(notification.pDevice); - } -} - -void ma_device__on_notification_started(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); -} - -void ma_device__on_notification_stopped(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); -} - -void ma_device__on_notification_rerouted(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); -} - -void ma_device__on_notification_interruption_began(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); -} - -void ma_device__on_notification_interruption_ended(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); -} - - -static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pDevice->onData != NULL); - - if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } - - pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); -} - -static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->noFixedSizedCallback) { - /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ - ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); - } else { - /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */ - ma_uint32 totalFramesProcessed = 0; - - while (totalFramesProcessed < frameCount) { - ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed; - ma_uint32 framesToProcessThisIteration = 0; - - if (pFramesIn != NULL) { - /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */ - if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) { - /* There's some room left in the intermediary buffer. Write to it without firing the callback. */ - framesToProcessThisIteration = totalFramesRemaining; - if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) { - framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen; - } - - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels), - ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels), - framesToProcessThisIteration, - pDevice->capture.format, pDevice->capture.channels); - - pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration; - } - - if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { - /* No room left in the intermediary buffer. Fire the data callback. */ - if (pDevice->type == ma_device_type_duplex) { - /* We'll do the duplex data callback later after we've processed the playback data. */ - } else { - ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); - - /* The intermediary buffer has just been drained. */ - pDevice->capture.intermediaryBufferLen = 0; - } - } - } - - if (pFramesOut != NULL) { - /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */ - if (pDevice->playback.intermediaryBufferLen > 0) { - /* There's some content in the intermediary buffer. Read from that without firing the callback. */ - if (pDevice->type == ma_device_type_duplex) { - /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */ - } else { - framesToProcessThisIteration = totalFramesRemaining; - if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) { - framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen; - } - } - - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels), - ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels), - framesToProcessThisIteration, - pDevice->playback.format, pDevice->playback.channels); - - pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration; - } - - if (pDevice->playback.intermediaryBufferLen == 0) { - /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */ - if (pDevice->type == ma_device_type_duplex) { - /* In duplex mode, the data callback will be fired later. Nothing to do here. */ - } else { - ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap); - - /* The intermediary buffer has just been filled. */ - pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; - } - } - } - - /* If we're in duplex mode we might need to do a refill of the data. */ - if (pDevice->type == ma_device_type_duplex) { - if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { - ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); - - pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */ - pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */ - } - } - - /* Make sure this is only incremented once in the duplex case. */ - totalFramesProcessed += framesToProcessThisIteration; - } - } -} - -static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - float masterVolumeFactor; - - ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ - - if (pDevice->onData) { - unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); - { - /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ - if (pFramesIn != NULL && masterVolumeFactor < 1) { - ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; - if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { - framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; - } - - ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); - - ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); - - totalFramesProcessed += framesToProcessThisIteration; - } - } else { - ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount); - } - - /* Volume control and clipping for playback devices. */ - if (pFramesOut != NULL) { - if (masterVolumeFactor < 1) { - if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ - ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); - } - } - - if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { - ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels); /* Intentionally specifying the same pointer for both input and output for in-place processing. */ - } - } - } - ma_device_restore_denormals(pDevice, prevDenormalState); - } -} - - - -/* A helper function for reading sample data from the client. */ -static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCount > 0); - MA_ASSERT(pFramesOut != NULL); - - if (pDevice->playback.converter.isPassthrough) { - ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount); - } else { - ma_result result; - ma_uint64 totalFramesReadOut; - void* pRunningFramesOut; - - totalFramesReadOut = 0; - pRunningFramesOut = pFramesOut; - - /* - We run slightly different logic depending on whether or not we're using a heap-allocated - buffer for caching input data. This will be the case if the data converter does not have - the ability to retrieve the required input frame count for a given output frame count. - */ - if (pDevice->playback.pInputCache != NULL) { - while (totalFramesReadOut < frameCount) { - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - - /* If there's any data available in the cache, that needs to get processed first. */ - if (pDevice->playback.inputCacheRemaining > 0) { - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) { - framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining; - } - - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn; - pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn; - - totalFramesReadOut += framesToReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - - if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - - /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */ - if (pDevice->playback.inputCacheRemaining == 0) { - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap); - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap; - } - } - } else { - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - if (framesToReadThisIterationIn > 0) { - ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); - } - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationIn = framesToReadThisIterationIn; - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } -} - -/* A helper for sending sample data to the client. */ -static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCountInDeviceFormat > 0); - MA_ASSERT(pFramesInDeviceFormat != NULL); - - if (pDevice->capture.converter.isPassthrough) { - ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); - } else { - ma_result result; - ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint64 totalDeviceFramesProcessed = 0; - ma_uint64 totalClientFramesProcessed = 0; - const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - - /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */ - for (;;) { - ma_uint64 deviceFramesProcessedThisIteration; - ma_uint64 clientFramesProcessedThisIteration; - - deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed); - clientFramesProcessedThisIteration = framesInClientFormatCap; - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration); - if (result != MA_SUCCESS) { - break; - } - - if (clientFramesProcessedThisIteration > 0) { - ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ - } - - pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; - totalClientFramesProcessed += clientFramesProcessedThisIteration; - - if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { - break; /* We're done. */ - } - } - } -} - -static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB) -{ - ma_result result; - ma_uint32 totalDeviceFramesProcessed = 0; - const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCountInDeviceFormat > 0); - MA_ASSERT(pFramesInDeviceFormat != NULL); - MA_ASSERT(pRB != NULL); - - /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */ - for (;;) { - ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed); - ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint64 framesProcessedInDeviceFormat; - ma_uint64 framesProcessedInClientFormat; - void* pFramesInClientFormat; - - result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer."); - break; - } - - if (framesToProcessInClientFormat == 0) { - if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) { - break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */ - } - } - - /* Convert. */ - framesProcessedInDeviceFormat = framesToProcessInDeviceFormat; - framesProcessedInClientFormat = framesToProcessInClientFormat; - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat); - if (result != MA_SUCCESS) { - break; - } - - result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */ - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer."); - break; - } - - pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */ - - /* We're done when we're unable to process any client nor device frames. */ - if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) { - break; /* Done. */ - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB) -{ - ma_result result; - ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 totalFramesReadOut = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCount > 0); - MA_ASSERT(pFramesInInternalFormat != NULL); - MA_ASSERT(pRB != NULL); - MA_ASSERT(pDevice->playback.pInputCache != NULL); - - /* - Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for - the whole frameCount frames we just use silence instead for the input data. - */ - MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames)); - - while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) { - /* - We should have a buffer allocated on the heap. Any playback frames still sitting in there - need to be sent to the internal device before we process any more data from the client. - */ - if (pDevice->playback.inputCacheRemaining > 0) { - ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining; - ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut); - ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); - - pDevice->playback.inputCacheConsumed += framesConvertedIn; - pDevice->playback.inputCacheRemaining -= framesConvertedIn; - - totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */ - pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - } - - /* If there's no more data in the cache we'll need to fill it with some. */ - if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) { - ma_uint32 inputFrameCount; - void* pInputFrames; - - inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap; - result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); - if (result == MA_SUCCESS) { - if (inputFrameCount > 0) { - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount); - } else { - if (ma_pcm_rb_pointer_distance(pRB) == 0) { - break; /* Underrun. */ - } - } - } else { - /* No capture data available. Feed in silence. */ - inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount); - } - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = inputFrameCount; - - result = ma_pcm_rb_commit_read(pRB, inputFrameCount); - if (result != MA_SUCCESS) { - return result; /* Should never happen. */ - } - } - } - - return MA_SUCCESS; -} - -/* A helper for changing the state of the device. */ -static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) -{ - c89atomic_exchange_i32((ma_int32*)&pDevice->state, (ma_int32)newState); -} - - -#ifdef MA_WIN32 - GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ -#endif - - - -MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ -{ - ma_uint32 i; - for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) { - if (g_maFormatPriorities[i] == format) { - return i; - } - } - - /* Getting here means the format could not be found or is equal to ma_format_unknown. */ - return (ma_uint32)-1; -} - -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); - -static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) -{ - if (pDeviceDescriptor == NULL) { - return MA_FALSE; - } - - if (pDeviceDescriptor->format == ma_format_unknown) { - return MA_FALSE; - } - - if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { - return MA_FALSE; - } - - if (pDeviceDescriptor->sampleRate == 0) { - return MA_FALSE; - } - - return MA_TRUE; -} - - -static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) -{ - ma_result result = MA_SUCCESS; - ma_bool32 exitLoop = MA_FALSE; - ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 capturedDeviceDataCapInFrames = 0; - ma_uint32 playbackDeviceDataCapInFrames = 0; - - MA_ASSERT(pDevice != NULL); - - /* Just some quick validation on the device type and the available callbacks. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - if (pDevice->pContext->callbacks.onDeviceRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->pContext->callbacks.onDeviceWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } - - playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - /* NOTE: The device was started outside of this function, in the worker thread. */ - - while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) { - switch (pDevice->type) { - case ma_device_type_duplex: - { - /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */ - ma_uint32 totalCapturedDeviceFramesProcessed = 0; - ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - - while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { - ma_uint32 capturedDeviceFramesRemaining; - ma_uint32 capturedDeviceFramesProcessed; - ma_uint32 capturedDeviceFramesToProcess; - ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed; - if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) { - capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames; - } - - result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - capturedDeviceFramesRemaining = capturedDeviceFramesToProcess; - capturedDeviceFramesProcessed = 0; - - /* At this point we have our captured data in device format and we now need to convert it to client format. */ - for (;;) { - ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames); - ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining; - ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - - /* Convert capture data from device format to client format. */ - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration); - if (result != MA_SUCCESS) { - break; - } - - /* - If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small - which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. - */ - if (capturedClientFramesToProcessThisIteration == 0) { - break; - } - - ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ - - capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ - capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ - - /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */ - for (;;) { - ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration; - ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount); - if (result != MA_SUCCESS) { - break; - } - - result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */ - if (capturedClientFramesToProcessThisIteration == 0) { - break; - } - } - - /* In case an error happened from ma_device_write__null()... */ - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - } - - /* Make sure we don't get stuck in the inner loop. */ - if (capturedDeviceFramesProcessed == 0) { - break; - } - - totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed; - } - } break; - - case ma_device_type_capture: - case ma_device_type_loopback: - { - ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - ma_uint32 framesReadThisPeriod = 0; - while (framesReadThisPeriod < periodSizeInFrames) { - ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod; - ma_uint32 framesProcessed; - ma_uint32 framesToReadThisIteration = framesRemainingInPeriod; - if (framesToReadThisIteration > capturedDeviceDataCapInFrames) { - framesToReadThisIteration = capturedDeviceDataCapInFrames; - } - - result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Make sure we don't get stuck in the inner loop. */ - if (framesProcessed == 0) { - break; - } - - ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData); - - framesReadThisPeriod += framesProcessed; - } - } break; - - case ma_device_type_playback: - { - /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */ - ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - ma_uint32 framesWrittenThisPeriod = 0; - while (framesWrittenThisPeriod < periodSizeInFrames) { - ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod; - ma_uint32 framesProcessed; - ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod; - if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) { - framesToWriteThisIteration = playbackDeviceDataCapInFrames; - } - - ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData); - - result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Make sure we don't get stuck in the inner loop. */ - if (framesProcessed == 0) { - break; - } - - framesWrittenThisPeriod += framesProcessed; - } - } break; - - /* Should never get here. */ - default: break; - } - } - - return result; -} - - - -/******************************************************************************* - -Null Backend - -*******************************************************************************/ -#ifdef MA_HAS_NULL - -#define MA_DEVICE_OP_NONE__NULL 0 -#define MA_DEVICE_OP_START__NULL 1 -#define MA_DEVICE_OP_SUSPEND__NULL 2 -#define MA_DEVICE_OP_KILL__NULL 3 - -static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) -{ - ma_device* pDevice = (ma_device*)pData; - MA_ASSERT(pDevice != NULL); - - for (;;) { /* Keep the thread alive until the device is uninitialized. */ - ma_uint32 operation; - - /* Wait for an operation to be requested. */ - ma_event_wait(&pDevice->null_device.operationEvent); - - /* At this point an event should have been triggered. */ - operation = pDevice->null_device.operation; - - /* Starting the device needs to put the thread into a loop. */ - if (operation == MA_DEVICE_OP_START__NULL) { - /* Reset the timer just in case. */ - ma_timer_init(&pDevice->null_device.timer); - - /* Getting here means a suspend or kill operation has been requested. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; - } - - /* Suspending the device means we need to stop the timer and just continue the loop. */ - if (operation == MA_DEVICE_OP_SUSPEND__NULL) { - /* We need to add the current run time to the prior run time, then reset the timer. */ - pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); - ma_timer_init(&pDevice->null_device.timer); - - /* We're done. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; - } - - /* Killing the device means we need to get out of this loop so that this thread can terminate. */ - if (operation == MA_DEVICE_OP_KILL__NULL) { - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - break; - } - - /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ - if (operation == MA_DEVICE_OP_NONE__NULL) { - MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ - pDevice->null_device.operationResult = MA_INVALID_OPERATION; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; /* Continue the loop. Don't terminate. */ - } - } - - return (ma_thread_result)0; -} - -static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) -{ - ma_result result; - - /* - TODO: Need to review this and consider just using mutual exclusion. I think the original motivation - for this was to just post the event to a queue and return immediately, but that has since changed - and now this function is synchronous. I think this can be simplified to just use a mutex. - */ - - /* - The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later - to support queing of operations. - */ - result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); - if (result != MA_SUCCESS) { - return result; /* Failed to wait for the event. */ - } - - /* - When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to - signal an event to the worker thread to let it know that it can start work. - */ - pDevice->null_device.operation = operation; - - /* Once the operation code has been set, the worker thread can start work. */ - if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { - return MA_ERROR; - } - - /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ - if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { - return MA_ERROR; - } - - return pDevice->null_device.operationResult; -} - -static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) -{ - ma_uint32 internalSampleRate; - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - internalSampleRate = pDevice->capture.internalSampleRate; - } else { - internalSampleRate = pDevice->playback.internalSampleRate; - } - - return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); -} - -static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - (void)cbResult; /* Silence a static analysis warning. */ - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - - /* Support everything on the null backend. */ - pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[0].channels = 0; - pDeviceInfo->nativeDataFormats[0].sampleRate = 0; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Keep it clean and wait for the device thread to finish before returning. */ - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); - - /* Wait for the thread to finish before continuing. */ - ma_thread_wait(&pDevice->null_device.deviceThread); - - /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ - ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); - ma_event_uninit(&pDevice->null_device.operationCompletionEvent); - ma_event_uninit(&pDevice->null_device.operationEvent); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->null_device); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* The null backend supports everything exactly as we specify it. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; - pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; - pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - } - - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; - pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; - pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); - } - - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - } - - /* - In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the - first period is "written" to it, and then stopped in ma_device_stop__null(). - */ - result = ma_event_init(&pDevice->null_device.operationEvent); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_event_init(&pDevice->null_device.operationCompletionEvent); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ - if (result != MA_SUCCESS) { - return result; - } - - result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); - - c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE); - return MA_SUCCESS; -} - -static ma_result ma_device_stop__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); - - c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE); - return MA_SUCCESS; -} - -static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalPCMFramesProcessed; - ma_bool32 wasStartedOnEntry; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - wasStartedOnEntry = c89atomic_load_32(&pDevice->null_device.isStarted); - - /* Keep going until everything has been read. */ - totalPCMFramesProcessed = 0; - while (totalPCMFramesProcessed < frameCount) { - ma_uint64 targetFrame; - - /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) { - ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback; - if (framesToProcess > framesRemaining) { - framesToProcess = framesRemaining; - } - - /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */ - (void)pPCMFrames; - - pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess; - totalPCMFramesProcessed += framesToProcess; - } - - /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { - pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; - - if (!c89atomic_load_32(&pDevice->null_device.isStarted) && !wasStartedOnEntry) { - result = ma_device_start__null(pDevice); - if (result != MA_SUCCESS) { - break; - } - } - } - - /* If we've consumed the whole buffer we can return now. */ - MA_ASSERT(totalPCMFramesProcessed <= frameCount); - if (totalPCMFramesProcessed == frameCount) { - break; - } - - /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFramePlayback; - for (;;) { - ma_uint64 currentFrame; - - /* Stop waiting if the device has been stopped. */ - if (!c89atomic_load_32(&pDevice->null_device.isStarted)) { - break; - } - - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); - if (currentFrame >= targetFrame) { - break; - } - - /* Getting here means we haven't yet reached the target sample, so continue waiting. */ - ma_sleep(10); - } - - pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames; - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalPCMFramesProcessed; - } - - return result; -} - -static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalPCMFramesProcessed; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - /* Keep going until everything has been read. */ - totalPCMFramesProcessed = 0; - while (totalPCMFramesProcessed < frameCount) { - ma_uint64 targetFrame; - - /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) { - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture; - if (framesToProcess > framesRemaining) { - framesToProcess = framesRemaining; - } - - /* We need to ensure the output buffer is zeroed. */ - MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf); - - pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess; - totalPCMFramesProcessed += framesToProcess; - } - - /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) { - pDevice->null_device.currentPeriodFramesRemainingCapture = 0; - } - - /* If we've consumed the whole buffer we can return now. */ - MA_ASSERT(totalPCMFramesProcessed <= frameCount); - if (totalPCMFramesProcessed == frameCount) { - break; - } - - /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames; - for (;;) { - ma_uint64 currentFrame; - - /* Stop waiting if the device has been stopped. */ - if (!c89atomic_load_32(&pDevice->null_device.isStarted)) { - break; - } - - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); - if (currentFrame >= targetFrame) { - break; - } - - /* Getting here means we haven't yet reached the target sample, so continue waiting. */ - ma_sleep(10); - } - - pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalPCMFramesProcessed; - } - - return result; -} - -static ma_result ma_context_uninit__null(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_null); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - (void)pContext; - - pCallbacks->onContextInit = ma_context_init__null; - pCallbacks->onContextUninit = ma_context_uninit__null; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; - pCallbacks->onDeviceInit = ma_device_init__null; - pCallbacks->onDeviceUninit = ma_device_uninit__null; - pCallbacks->onDeviceStart = ma_device_start__null; - pCallbacks->onDeviceStop = ma_device_stop__null; - pCallbacks->onDeviceRead = ma_device_read__null; - pCallbacks->onDeviceWrite = ma_device_write__null; - pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ - - /* The null backend always works. */ - return MA_SUCCESS; -} -#endif - - - -/******************************************************************************* - -WIN32 COMMON - -*******************************************************************************/ -#if defined(MA_WIN32) -#if defined(MA_WIN32_DESKTOP) - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) - #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() - #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) - #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) - #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar) -#else - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit) - #define ma_CoUninitialize(pContext) CoUninitialize() - #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv) - #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) - #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) -#endif - -#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) -typedef size_t DWORD_PTR; -#endif - -#if !defined(WAVE_FORMAT_44M08) -#define WAVE_FORMAT_44M08 0x00000100 -#define WAVE_FORMAT_44S08 0x00000200 -#define WAVE_FORMAT_44M16 0x00000400 -#define WAVE_FORMAT_44S16 0x00000800 -#define WAVE_FORMAT_48M08 0x00001000 -#define WAVE_FORMAT_48S08 0x00002000 -#define WAVE_FORMAT_48M16 0x00004000 -#define WAVE_FORMAT_48S16 0x00008000 -#define WAVE_FORMAT_96M08 0x00010000 -#define WAVE_FORMAT_96S08 0x00020000 -#define WAVE_FORMAT_96M16 0x00040000 -#define WAVE_FORMAT_96S16 0x00080000 -#endif - -#ifndef SPEAKER_FRONT_LEFT -#define SPEAKER_FRONT_LEFT 0x1 -#define SPEAKER_FRONT_RIGHT 0x2 -#define SPEAKER_FRONT_CENTER 0x4 -#define SPEAKER_LOW_FREQUENCY 0x8 -#define SPEAKER_BACK_LEFT 0x10 -#define SPEAKER_BACK_RIGHT 0x20 -#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 -#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 -#define SPEAKER_BACK_CENTER 0x100 -#define SPEAKER_SIDE_LEFT 0x200 -#define SPEAKER_SIDE_RIGHT 0x400 -#define SPEAKER_TOP_CENTER 0x800 -#define SPEAKER_TOP_FRONT_LEFT 0x1000 -#define SPEAKER_TOP_FRONT_CENTER 0x2000 -#define SPEAKER_TOP_FRONT_RIGHT 0x4000 -#define SPEAKER_TOP_BACK_LEFT 0x8000 -#define SPEAKER_TOP_BACK_CENTER 0x10000 -#define SPEAKER_TOP_BACK_RIGHT 0x20000 -#endif - -/* -The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We -define our own implementation in this case. -*/ -#if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__) -typedef struct -{ - WAVEFORMATEX Format; - union - { - WORD wValidBitsPerSample; - WORD wSamplesPerBlock; - WORD wReserved; - } Samples; - DWORD dwChannelMask; - GUID SubFormat; -} WAVEFORMATEXTENSIBLE; -#endif - -#ifndef WAVE_FORMAT_EXTENSIBLE -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE -#endif - -#ifndef WAVE_FORMAT_IEEE_FLOAT -#define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#endif - -/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ -static ma_uint8 ma_channel_id_to_ma__win32(DWORD id) -{ - switch (id) - { - case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; - case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; - case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; - case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */ -static DWORD ma_channel_id_to_win32(DWORD id) -{ - switch (id) - { - case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER; - case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; - case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY; - case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT; - case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER; - case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts a channel mapping to a Win32-style channel mask. */ -static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels) -{ - DWORD dwChannelMask = 0; - ma_uint32 iChannel; - - for (iChannel = 0; iChannel < channels; ++iChannel) { - dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]); - } - - return dwChannelMask; -} - -/* Converts a Win32-style channel mask to a miniaudio channel map. */ -static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) -{ - if (channels == 1 && dwChannelMask == 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else if (channels == 2 && dwChannelMask == 0) { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else { - /* Just iterate over each bit. */ - ma_uint32 iChannel = 0; - ma_uint32 iBit; - - for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { - DWORD bitValue = (dwChannelMask & (1UL << iBit)); - if (bitValue != 0) { - /* The bit is set. */ - pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue); - iChannel += 1; - } - } - } - } -} - -#ifdef __cplusplus -static ma_bool32 ma_is_guid_equal(const void* a, const void* b) -{ - return IsEqualGUID(*(const GUID*)a, *(const GUID*)b); -} -#else -#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) -#endif - -static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) -{ - static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - return ma_is_guid_equal(guid, &nullguid); -} - -static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF) -{ - MA_ASSERT(pWF != NULL); - - if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF; - if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { - if (pWFEX->Samples.wValidBitsPerSample == 32) { - return ma_format_s32; - } - if (pWFEX->Samples.wValidBitsPerSample == 24) { - if (pWFEX->Format.wBitsPerSample == 32) { - /*return ma_format_s24_32;*/ - } - if (pWFEX->Format.wBitsPerSample == 24) { - return ma_format_s24; - } - } - if (pWFEX->Samples.wValidBitsPerSample == 16) { - return ma_format_s16; - } - if (pWFEX->Samples.wValidBitsPerSample == 8) { - return ma_format_u8; - } - } - if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { - if (pWFEX->Samples.wValidBitsPerSample == 32) { - return ma_format_f32; - } - /* - if (pWFEX->Samples.wValidBitsPerSample == 64) { - return ma_format_f64; - } - */ - } - } else { - if (pWF->wFormatTag == WAVE_FORMAT_PCM) { - if (pWF->wBitsPerSample == 32) { - return ma_format_s32; - } - if (pWF->wBitsPerSample == 24) { - return ma_format_s24; - } - if (pWF->wBitsPerSample == 16) { - return ma_format_s16; - } - if (pWF->wBitsPerSample == 8) { - return ma_format_u8; - } - } - if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { - if (pWF->wBitsPerSample == 32) { - return ma_format_f32; - } - if (pWF->wBitsPerSample == 64) { - /*return ma_format_f64;*/ - } - } - } - - return ma_format_unknown; -} -#endif - - -/******************************************************************************* - -WASAPI Backend - -*******************************************************************************/ -#ifdef MA_HAS_WASAPI -#if 0 -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */ -#endif -#include -#include -#if defined(_MSC_VER) - #pragma warning(pop) -#endif -#endif /* 0 */ - -static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType); - -/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */ -#define MA_WIN32_WINNT_VISTA 0x0600 -#define MA_VER_MINORVERSION 0x01 -#define MA_VER_MAJORVERSION 0x02 -#define MA_VER_SERVICEPACKMAJOR 0x20 -#define MA_VER_GREATER_EQUAL 0x03 - -typedef struct { - DWORD dwOSVersionInfoSize; - DWORD dwMajorVersion; - DWORD dwMinorVersion; - DWORD dwBuildNumber; - DWORD dwPlatformId; - WCHAR szCSDVersion[128]; - WORD wServicePackMajor; - WORD wServicePackMinor; - WORD wSuiteMask; - BYTE wProductType; - BYTE wReserved; -} ma_OSVERSIONINFOEXW; - -typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask); -typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask); - - -#ifndef PROPERTYKEY_DEFINED -#define PROPERTYKEY_DEFINED -#ifndef __WATCOMC__ -typedef struct -{ - GUID fmtid; - DWORD pid; -} PROPERTYKEY; -#endif -#endif - -/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ -static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp) -{ - MA_ZERO_OBJECT(pProp); -} - - -static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; -static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; - -static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ -#endif - -static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */ -static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */ -static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */ -static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */ -static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */ -static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */ -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */ -static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */ -static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ -#endif - -static const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ -static const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ -#ifdef __cplusplus -#define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance -#define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance -#else -#define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance -#define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance -#endif - -typedef struct ma_IUnknown ma_IUnknown; -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -#define MA_MM_DEVICE_STATE_ACTIVE 1 -#define MA_MM_DEVICE_STATE_DISABLED 2 -#define MA_MM_DEVICE_STATE_NOTPRESENT 4 -#define MA_MM_DEVICE_STATE_UNPLUGGED 8 - -typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator; -typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection; -typedef struct ma_IMMDevice ma_IMMDevice; -#else -typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler; -typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation; -#endif -typedef struct ma_IPropertyStore ma_IPropertyStore; -typedef struct ma_IAudioClient ma_IAudioClient; -typedef struct ma_IAudioClient2 ma_IAudioClient2; -typedef struct ma_IAudioClient3 ma_IAudioClient3; -typedef struct ma_IAudioRenderClient ma_IAudioRenderClient; -typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient; - -typedef ma_int64 MA_REFERENCE_TIME; - -#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 -#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 -#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 -#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 -#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 -#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 -#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 -#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 -#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 -#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 - -/* Buffer flags. */ -#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1 -#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2 -#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4 - -typedef enum -{ - ma_eRender = 0, - ma_eCapture = 1, - ma_eAll = 2 -} ma_EDataFlow; - -typedef enum -{ - ma_eConsole = 0, - ma_eMultimedia = 1, - ma_eCommunications = 2 -} ma_ERole; - -typedef enum -{ - MA_AUDCLNT_SHAREMODE_SHARED, - MA_AUDCLNT_SHAREMODE_EXCLUSIVE -} MA_AUDCLNT_SHAREMODE; - -typedef enum -{ - MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ -} MA_AUDIO_STREAM_CATEGORY; - -typedef struct -{ - ma_uint32 cbSize; - BOOL bIsOffload; - MA_AUDIO_STREAM_CATEGORY eCategory; -} ma_AudioClientProperties; - -/* IUnknown */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis); -} ma_IUnknownVtbl; -struct ma_IUnknown -{ - ma_IUnknownVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* IMMNotificationClient */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); - - /* IMMNotificationClient */ - HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState); - HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID); - HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key); - } ma_IMMNotificationClientVtbl; - - /* IMMDeviceEnumerator */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis); - - /* IMMDeviceEnumerator */ - HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); - HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); - HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice); - HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); - HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); - } ma_IMMDeviceEnumeratorVtbl; - struct ma_IMMDeviceEnumerator - { - ma_IMMDeviceEnumeratorVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } - - - /* IMMDeviceCollection */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis); - - /* IMMDeviceCollection */ - HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices); - HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice); - } ma_IMMDeviceCollectionVtbl; - struct ma_IMMDeviceCollection - { - ma_IMMDeviceCollectionVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); } - static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); } - - - /* IMMDevice */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); - - /* IMMDevice */ - HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface); - HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); - HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID); - HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); - } ma_IMMDeviceVtbl; - struct ma_IMMDevice - { - ma_IMMDeviceVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } - static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } - static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); } - static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } -#else - /* IActivateAudioInterfaceAsyncOperation */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis); - - /* IActivateAudioInterfaceAsyncOperation */ - HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface); - } ma_IActivateAudioInterfaceAsyncOperationVtbl; - struct ma_IActivateAudioInterfaceAsyncOperation - { - ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } -#endif - -/* IPropertyStore */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis); - - /* IPropertyStore */ - HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); - HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); - HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar); - HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar); - HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); -} ma_IPropertyStoreVtbl; -struct ma_IPropertyStore -{ - ma_IPropertyStoreVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } -static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } -static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } - - -/* IAudioClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp); -} ma_IAudioClientVtbl; -struct ma_IAudioClient -{ - ma_IAudioClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } - -/* IAudioClient2 */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp); - - /* IAudioClient2 */ - HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); - HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); -} ma_IAudioClient2Vtbl; -struct ma_IAudioClient2 -{ - ma_IAudioClient2Vtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } -static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } -static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } - - -/* IAudioClient3 */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp); - - /* IAudioClient2 */ - HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); - HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); - - /* IAudioClient3 */ - HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); -} ma_IAudioClient3Vtbl; -struct ma_IAudioClient3 -{ - ma_IAudioClient3Vtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } -static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } -static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } -static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } - - -/* IAudioRenderClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis); - - /* IAudioRenderClient */ - HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData); - HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags); -} ma_IAudioRenderClientVtbl; -struct ma_IAudioRenderClient -{ - ma_IAudioRenderClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); } -static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); } - - -/* IAudioCaptureClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis); - - /* IAudioRenderClient */ - HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition); - HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead); - HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket); -} ma_IAudioCaptureClientVtbl; -struct ma_IAudioCaptureClient -{ - ma_IAudioCaptureClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } - -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -#include -typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; - -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis); - - /* IActivateAudioInterfaceCompletionHandler */ - HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation); -} ma_completion_handler_uwp_vtbl; -struct ma_completion_handler_uwp -{ - ma_completion_handler_uwp_vtbl* lpVtbl; - MA_ATOMIC(4, ma_uint32) counter; - HANDLE hEvent; -}; - -static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject) -{ - /* - We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To - "implement" this, we just make sure we return pThis when the IAgileObject is requested. - */ - if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) { - *ppObject = NULL; - return E_NOINTERFACE; - } - - /* Getting here means the IID is IUnknown or IMMNotificationClient. */ - *ppObject = (void*)pThis; - ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); - return S_OK; -} - -static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) -{ - return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1; -} - -static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) -{ - ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1; - if (newRefCount == 0) { - return 0; /* We don't free anything here because we never allocate the object on the heap. */ - } - - return (ULONG)newRefCount; -} - -static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation) -{ - (void)pActivateOperation; - SetEvent(pThis->hEvent); - return S_OK; -} - - -static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = { - ma_completion_handler_uwp_QueryInterface, - ma_completion_handler_uwp_AddRef, - ma_completion_handler_uwp_Release, - ma_completion_handler_uwp_ActivateCompleted -}; - -static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler) -{ - MA_ASSERT(pHandler != NULL); - MA_ZERO_OBJECT(pHandler); - - pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance; - pHandler->counter = 1; - pHandler->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); - if (pHandler->hEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler) -{ - if (pHandler->hEvent != NULL) { - CloseHandle(pHandler->hEvent); - } -} - -static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) -{ - WaitForSingleObject(pHandler->hEvent, INFINITE); -} -#endif /* !MA_WIN32_DESKTOP */ - -/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject) -{ - /* - We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else - we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK. - */ - if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) { - *ppObject = NULL; - return E_NOINTERFACE; - } - - /* Getting here means the IID is IUnknown or IMMNotificationClient. */ - *ppObject = (void*)pThis; - ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); - return S_OK; -} - -static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) -{ - return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1; -} - -static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) -{ - ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1; - if (newRefCount == 0) { - return 0; /* We don't free anything here because we never allocate the object on the heap. */ - } - - return (ULONG)newRefCount; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState) -{ - ma_bool32 isThisDevice = MA_FALSE; - ma_bool32 isCapture = MA_FALSE; - ma_bool32 isPlayback = MA_FALSE; - -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ -#endif - - /* - There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect - that the device is disabled or has been unplugged. - */ - if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { - isCapture = MA_TRUE; - if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { - isThisDevice = MA_TRUE; - } - } - - if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { - isPlayback = MA_TRUE; - if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { - isThisDevice = MA_TRUE; - } - } - - - /* - If the device ID matches our device we need to mark our device as detached and stop it. When a - device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device - was started at the time of being removed. - */ - if (isThisDevice) { - if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) { - /* - Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll - use this to determine whether or not we need to automatically start the device when it's - plugged back in again. - */ - if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { - if (isPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; - } - if (isCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE; - } - - ma_device_stop(pThis->pDevice); - } - } - - if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) { - /* The device was activated. If we were detached, we need to start it again. */ - ma_bool8 tryRestartingDevice = MA_FALSE; - - if (isPlayback) { - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - tryRestartingDevice = MA_TRUE; - } - } - - if (isCapture) { - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - tryRestartingDevice = MA_TRUE; - } - } - - if (tryRestartingDevice) { - if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) { - ma_device_start(pThis->pDevice); - } - } - } - } - - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - /* We don't need to worry about this event for our purposes. */ - (void)pThis; - (void)pDeviceID; - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - /* We don't need to worry about this event for our purposes. */ - (void)pThis; - (void)pDeviceID; - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ -#endif - - /* We only ever use the eConsole role in miniaudio. */ - if (role != ma_eConsole) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting: role != eConsole\n"); - return S_OK; - } - - /* We only care about devices with the same data flow and role as the current device. */ - if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || - (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); - return S_OK; - } - - /* Don't do automatic stream routing if we're not allowed. */ - if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || - (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n"); - return S_OK; - } - - /* - Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to - AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once - it's fixed. - */ - if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) || - (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n"); - return S_OK; - } - - - - - /* - Second attempt at device rerouting. We're going to retrieve the device's state at the time of - the route change. We're then going to stop the device, reinitialize the device, and then start - it again if the state before stopping was ma_device_state_started. - */ - { - ma_uint32 previousState = ma_device_get_state(pThis->pDevice); - ma_bool8 restartDevice = MA_FALSE; - - if (previousState == ma_device_state_started) { - ma_device_stop(pThis->pDevice); - restartDevice = MA_TRUE; - } - - if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ - if (dataFlow == ma_eRender) { - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { - restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ - } else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ - } - } - } else { - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { - restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ - } else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ - } - } - } - - if (restartDevice) { - ma_device_start(pThis->pDevice); - } - } - } - - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - (void)pThis; - (void)pDeviceID; - (void)key; - return S_OK; -} - -static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { - ma_IMMNotificationClient_QueryInterface, - ma_IMMNotificationClient_AddRef, - ma_IMMNotificationClient_Release, - ma_IMMNotificationClient_OnDeviceStateChanged, - ma_IMMNotificationClient_OnDeviceAdded, - ma_IMMNotificationClient_OnDeviceRemoved, - ma_IMMNotificationClient_OnDefaultDeviceChanged, - ma_IMMNotificationClient_OnPropertyValueChanged -}; -#endif /* MA_WIN32_DESKTOP */ - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -typedef ma_IMMDevice ma_WASAPIDeviceInterface; -#else -typedef ma_IUnknown ma_WASAPIDeviceInterface; -#endif - - -#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1 -#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2 -#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3 - -static ma_context_command__wasapi ma_context_init_command__wasapi(int code) -{ - ma_context_command__wasapi cmd; - - MA_ZERO_OBJECT(&cmd); - cmd.code = code; - - return cmd; -} - -static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd) -{ - /* For now we are doing everything synchronously, but I might relax this later if the need arises. */ - ma_result result; - ma_bool32 isUsingLocalEvent = MA_FALSE; - ma_event localEvent; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCmd != NULL); - - if (pCmd->pEvent == NULL) { - isUsingLocalEvent = MA_TRUE; - - result = ma_event_init(&localEvent); - if (result != MA_SUCCESS) { - return result; /* Failed to create the event for this command. */ - } - } - - /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */ - ma_mutex_lock(&pContext->wasapi.commandLock); - { - ma_uint32 index; - - /* Spin until we've got some space available. */ - while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) { - ma_yield(); - } - - /* Space is now available. Can safely add to the list. */ - index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commands[index] = *pCmd; - pContext->wasapi.commands[index].pEvent = &localEvent; - pContext->wasapi.commandCount += 1; - - /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */ - ma_semaphore_release(&pContext->wasapi.commandSem); - } - ma_mutex_unlock(&pContext->wasapi.commandLock); - - if (isUsingLocalEvent) { - ma_event_wait(&localEvent); - ma_event_uninit(&localEvent); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCmd != NULL); - - result = ma_semaphore_wait(&pContext->wasapi.commandSem); - if (result == MA_SUCCESS) { - ma_mutex_lock(&pContext->wasapi.commandLock); - { - *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex]; - pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commandCount -= 1; - } - ma_mutex_unlock(&pContext->wasapi.commandLock); - } - - return result; -} - -static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData) -{ - ma_result result; - ma_context* pContext = (ma_context*)pUserData; - MA_ASSERT(pContext != NULL); - - for (;;) { - ma_context_command__wasapi cmd; - result = ma_context_next_command__wasapi(pContext, &cmd); - if (result != MA_SUCCESS) { - break; - } - - switch (cmd.code) - { - case MA_CONTEXT_COMMAND_QUIT__WASAPI: - { - /* Do nothing. Handled after the switch. */ - } break; - - case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: - { - if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); - } else { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); - } - } break; - - case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI: - { - if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL; - } - } - - if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL; - } - } - } break; - - default: - { - /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */ - MA_ASSERT(MA_FALSE); - } break; - } - - if (cmd.pEvent != NULL) { - ma_event_signal(cmd.pEvent); - } - - if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) { - break; /* Received a quit message. Get out of here. */ - } - } - - return (ma_thread_result)0; -} - -static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService) -{ - ma_result result; - ma_result cmdResult; - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); - cmd.data.createAudioClient.deviceType = deviceType; - cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; - cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; - cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */ - - result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */ - if (result != MA_SUCCESS) { - return result; - } - - return *cmd.data.createAudioClient.pResult; -} - -#if 0 /* Not used at the moment, but leaving here for future use. */ -static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI); - cmd.data.releaseAudioClient.pDevice = pDevice; - cmd.data.releaseAudioClient.deviceType = deviceType; - - result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */ - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} -#endif - - -static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) -{ - MA_ASSERT(pWF != NULL); - MA_ASSERT(pInfo != NULL); - - if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) { - return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */ - } - - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF); - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels; - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec; - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0; - pInfo->nativeDataFormatCount += 1; -} - -static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) -{ - HRESULT hr; - WAVEFORMATEX* pWF = NULL; - - MA_ASSERT(pAudioClient != NULL); - MA_ASSERT(pInfo != NULL); - - /* Shared Mode. We use GetMixFormat() here. */ - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF); - if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval."); - return ma_result_from_HRESULT(hr); - } - - /* - Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on - UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on - out, MA_SUCCESS is guaranteed to be returned. - */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - ma_IPropertyStore *pProperties; - - /* - The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is - correct which will simplify our searching. - */ - hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - PROPVARIANT var; - ma_PropVariantInit(&var); - - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); - if (SUCCEEDED(hr)) { - pWF = (WAVEFORMATEX*)var.blob.pBlobData; - - /* - In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format - first. If this fails, fall back to a search. - */ - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); - if (SUCCEEDED(hr)) { - /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */ - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo); - } else { - /* - The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel - count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. - */ - ma_uint32 channels = pWF->nChannels; - ma_channel defaultChannelMap[MA_MAX_CHANNELS]; - WAVEFORMATEXTENSIBLE wf; - ma_bool32 found; - ma_uint32 iFormat; - - /* Make sure we don't overflow the channel map. */ - if (channels > MA_MAX_CHANNELS) { - channels = MA_MAX_CHANNELS; - } - - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); - - MA_ZERO_OBJECT(&wf); - wf.Format.cbSize = sizeof(wf); - wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf.Format.nChannels = (WORD)channels; - wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); - - found = MA_FALSE; - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - ma_format format = g_maFormatPriorities[iFormat]; - ma_uint32 iSampleRate; - - wf.Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample; - if (format == ma_format_f32) { - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } else { - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } - - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { - wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; - - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL); - if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); - found = MA_TRUE; - break; - } - } - - if (found) { - break; - } - } - - ma_PropVariantClear(pContext, &var); - - if (!found) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval."); - } - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval."); - } - - ma_IPropertyStore_Release(pProperties); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); - } - } - #endif - - return MA_SUCCESS; -} - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType) -{ - if (deviceType == ma_device_type_playback) { - return ma_eRender; - } else if (deviceType == ma_device_type_capture) { - return ma_eCapture; - } else { - MA_ASSERT(MA_FALSE); - return ma_eRender; /* Should never hit this. */ - } -} - -static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator) -{ - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDeviceEnumerator != NULL); - - *ppDeviceEnumerator = NULL; /* Safety. */ - - hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - *ppDeviceEnumerator = pDeviceEnumerator; - - return MA_SUCCESS; -} - -static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) -{ - HRESULT hr; - ma_IMMDevice* pMMDefaultDevice = NULL; - LPWSTR pDefaultDeviceID = NULL; - ma_EDataFlow dataFlow; - ma_ERole role; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceEnumerator != NULL); - - (void)pContext; - - /* Grab the EDataFlow type from the device type. */ - dataFlow = ma_device_type_to_EDataFlow(deviceType); - - /* The role is always eConsole, but we may make this configurable later. */ - role = ma_eConsole; - - hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice); - if (FAILED(hr)) { - return NULL; - } - - hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID); - - ma_IMMDevice_Release(pMMDefaultDevice); - pMMDefaultDevice = NULL; - - if (FAILED(hr)) { - return NULL; - } - - return pDefaultDeviceID; -} - -static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ -{ - ma_result result; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - LPWSTR pDefaultDeviceID = NULL; - - MA_ASSERT(pContext != NULL); - - result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator); - if (result != MA_SUCCESS) { - return NULL; - } - - pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - return pDefaultDeviceID; -} - -static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice) -{ - ma_IMMDeviceEnumerator* pDeviceEnumerator; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppMMDevice != NULL); - - hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator."); - return ma_result_from_HRESULT(hr); - } - - if (pDeviceID == NULL) { - hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice); - } else { - hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice); - } - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice."); - return ma_result_from_HRESULT(hr); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) -{ - LPWSTR pDeviceIDString; - HRESULT hr; - - MA_ASSERT(pDeviceID != NULL); - - hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); - if (SUCCEEDED(hr)) { - size_t idlen = wcslen(pDeviceIDString); - if (idlen+1 > ma_countof(pDeviceID->wasapi)) { - ma_CoTaskMemFree(pContext, pDeviceIDString); - MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */ - return MA_ERROR; - } - - MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t)); - pDeviceID->wasapi[idlen] = '\0'; - - ma_CoTaskMemFree(pContext, pDeviceIDString); - - return MA_SUCCESS; - } - - return MA_ERROR; -} - -static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, LPWSTR pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pMMDevice != NULL); - MA_ASSERT(pInfo != NULL); - - /* ID. */ - result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); - if (result == MA_SUCCESS) { - if (pDefaultDeviceID != NULL) { - if (wcscmp(pInfo->id.wasapi, pDefaultDeviceID) == 0) { - pInfo->isDefault = MA_TRUE; - } - } - } - - /* Description / Friendly Name */ - { - ma_IPropertyStore *pProperties; - hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - PROPVARIANT var; - - ma_PropVariantInit(&var); - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); - ma_PropVariantClear(pContext, &var); - } - - ma_IPropertyStore_Release(pProperties); - } - } - - /* Format */ - if (!onlySimpleInfo) { - ma_IAudioClient* pAudioClient; - hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); - if (SUCCEEDED(hr)) { - result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo); - - ma_IAudioClient_Release(pAudioClient); - return result; - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval."); - return ma_result_from_HRESULT(hr); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result = MA_SUCCESS; - UINT deviceCount; - HRESULT hr; - ma_uint32 iDevice; - LPWSTR pDefaultDeviceID = NULL; - ma_IMMDeviceCollection* pDeviceCollection = NULL; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */ - pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - - /* We need to enumerate the devices which returns a device collection. */ - hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); - if (SUCCEEDED(hr)) { - hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count."); - result = ma_result_from_HRESULT(hr); - goto done; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - ma_IMMDevice* pMMDevice; - - MA_ZERO_OBJECT(&deviceInfo); - - hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); - if (SUCCEEDED(hr)) { - result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */ - - ma_IMMDevice_Release(pMMDevice); - if (result == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - break; - } - } - } - } - } - -done: - if (pDefaultDeviceID != NULL) { - ma_CoTaskMemFree(pContext, pDefaultDeviceID); - pDefaultDeviceID = NULL; - } - - if (pDeviceCollection != NULL) { - ma_IMMDeviceCollection_Release(pDeviceCollection); - pDeviceCollection = NULL; - } - - return result; -} - -static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppAudioClient != NULL); - MA_ASSERT(ppMMDevice != NULL); - - result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice); - if (result != MA_SUCCESS) { - return result; - } - - hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - return MA_SUCCESS; -} -#else -static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) -{ - ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; - ma_completion_handler_uwp completionHandler; - IID iid; - LPOLESTR iidStr; - HRESULT hr; - ma_result result; - HRESULT activateResult; - ma_IUnknown* pActivatedInterface; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppAudioClient != NULL); - - if (pDeviceID != NULL) { - MA_COPY_MEMORY(&iid, pDeviceID->wasapi, sizeof(iid)); - } else { - if (deviceType == ma_device_type_playback) { - iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; - } else { - iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; - } - } - -#if defined(__cplusplus) - hr = StringFromIID(iid, &iidStr); -#else - hr = StringFromIID(&iid, &iidStr); -#endif - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory."); - return ma_result_from_HRESULT(hr); - } - - result = ma_completion_handler_uwp_init(&completionHandler); - if (result != MA_SUCCESS) { - ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync()."); - return result; - } - -#if defined(__cplusplus) - hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); -#else - hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); -#endif - if (FAILED(hr)) { - ma_completion_handler_uwp_uninit(&completionHandler); - ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed."); - return ma_result_from_HRESULT(hr); - } - - ma_CoTaskMemFree(pContext, iidStr); - - /* Wait for the async operation for finish. */ - ma_completion_handler_uwp_wait(&completionHandler); - ma_completion_handler_uwp_uninit(&completionHandler); - - hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); - ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); - - if (FAILED(hr) || FAILED(activateResult)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device."); - return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); - } - - /* Here is where we grab the IAudioClient interface. */ - hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface."); - return ma_result_from_HRESULT(hr); - } - - if (ppActivatedInterface) { - *ppActivatedInterface = pActivatedInterface; - } else { - ma_IUnknown_Release(pActivatedInterface); - } - - return MA_SUCCESS; -} -#endif - -static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) -{ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); -#else - return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); -#endif -} - - -static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - /* Different enumeration for desktop and UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* Desktop */ - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - - hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData); - ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData); - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); -#else - /* - UWP - - The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate - over devices without using MMDevice, I'm restricting devices to defaults. - - Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ - */ - if (callback) { - ma_bool32 cbResult = MA_TRUE; - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - ma_result result; - ma_IMMDevice* pMMDevice = NULL; - LPWSTR pDefaultDeviceID = NULL; - - result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); - if (result != MA_SUCCESS) { - return result; - } - - /* We need the default device ID so we can set the isDefault flag in the device info. */ - pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType); - - result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */ - - if (pDefaultDeviceID != NULL) { - ma_CoTaskMemFree(pContext, pDefaultDeviceID); - pDefaultDeviceID = NULL; - } - - ma_IMMDevice_Release(pMMDevice); - - return result; -#else - ma_IAudioClient* pAudioClient; - ma_result result; - - /* UWP currently only uses default devices. */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo); - - pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */ - - ma_IAudioClient_Release(pAudioClient); - return result; -#endif -} - -static ma_result ma_device_uninit__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pDevice->wasapi.pDeviceEnumerator) { - ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); - ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); - } -#endif - - if (pDevice->wasapi.pRenderClient) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - } - if (pDevice->wasapi.pCaptureClient) { - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - } - - if (pDevice->wasapi.pAudioClientPlayback) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - } - if (pDevice->wasapi.pAudioClientCapture) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - } - - if (pDevice->wasapi.hEventPlayback) { - CloseHandle(pDevice->wasapi.hEventPlayback); - } - if (pDevice->wasapi.hEventCapture) { - CloseHandle(pDevice->wasapi.hEventCapture); - } - - return MA_SUCCESS; -} - - -typedef struct -{ - /* Input. */ - ma_format formatIn; - ma_uint32 channelsIn; - ma_uint32 sampleRateIn; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesIn; - ma_uint32 periodSizeInMillisecondsIn; - ma_uint32 periodsIn; - ma_share_mode shareMode; - ma_performance_profile performanceProfile; - ma_bool32 noAutoConvertSRC; - ma_bool32 noDefaultQualitySRC; - ma_bool32 noHardwareOffloading; - - /* Output. */ - ma_IAudioClient* pAudioClient; - ma_IAudioRenderClient* pRenderClient; - ma_IAudioCaptureClient* pCaptureClient; - ma_format formatOut; - ma_uint32 channelsOut; - ma_uint32 sampleRateOut; - ma_channel channelMapOut[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesOut; - ma_uint32 periodsOut; - ma_bool32 usingAudioClient3; - char deviceName[256]; - ma_device_id id; -} ma_device_init_internal_data__wasapi; - -static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData) -{ - HRESULT hr; - ma_result result = MA_SUCCESS; - const char* errorMsg = ""; - MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED; - DWORD streamFlags = 0; - MA_REFERENCE_TIME periodDurationInMicroseconds; - ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; - WAVEFORMATEXTENSIBLE wf; - ma_WASAPIDeviceInterface* pDeviceInterface = NULL; - ma_IAudioClient2* pAudioClient2; - ma_uint32 nativeSampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pData != NULL); - - /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - pData->pAudioClient = NULL; - pData->pRenderClient = NULL; - pData->pCaptureClient = NULL; - - streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */ - streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; - } - if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) { - streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; - } - if (deviceType == ma_device_type_loopback) { - streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; - } - - result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface); - if (result != MA_SUCCESS) { - goto done; - } - - MA_ZERO_OBJECT(&wf); - - /* Try enabling hardware offloading. */ - if (!pData->noHardwareOffloading) { - hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2); - if (SUCCEEDED(hr)) { - BOOL isHardwareOffloadingSupported = 0; - hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported); - if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { - ma_AudioClientProperties clientProperties; - MA_ZERO_OBJECT(&clientProperties); - clientProperties.cbSize = sizeof(clientProperties); - clientProperties.bIsOffload = 1; - clientProperties.eCategory = MA_AudioCategory_Other; - ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); - } - - pAudioClient2->lpVtbl->Release(pAudioClient2); - } - } - - /* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */ - result = MA_FORMAT_NOT_SUPPORTED; - if (pData->shareMode == ma_share_mode_exclusive) { - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* In exclusive mode on desktop we always use the backend's native format. */ - ma_IPropertyStore* pStore = NULL; - hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); - if (SUCCEEDED(hr)) { - PROPVARIANT prop; - ma_PropVariantInit(&prop); - hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); - if (SUCCEEDED(hr)) { - WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData; - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); - if (SUCCEEDED(hr)) { - MA_COPY_MEMORY(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE)); - } - - ma_PropVariantClear(pContext, &prop); - } - - ma_IPropertyStore_Release(pStore); - } - #else - /* - I do not know how to query the device's native format on UWP so for now I'm just disabling support for - exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported() - until you find one that works. - - TODO: Add support for exclusive mode to UWP. - */ - hr = S_FALSE; - #endif - - if (hr == S_OK) { - shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE; - result = MA_SUCCESS; - } else { - result = MA_SHARE_MODE_NOT_SUPPORTED; - } - } else { - /* In shared mode we are always using the format reported by the operating system. */ - WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat); - if (hr != S_OK) { - result = MA_FORMAT_NOT_SUPPORTED; - } else { - MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(wf)); - result = MA_SUCCESS; - } - - ma_CoTaskMemFree(pContext, pNativeFormat); - - shareMode = MA_AUDCLNT_SHAREMODE_SHARED; - } - - /* Return an error if we still haven't found a format. */ - if (result != MA_SUCCESS) { - errorMsg = "[WASAPI] Failed to find best device mix format."; - goto done; - } - - /* - Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use - WASAPI to perform the sample rate conversion. - */ - nativeSampleRate = wf.Format.nSamplesPerSec; - if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { - wf.Format.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; - wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign; - } - - pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf); - if (pData->formatOut == ma_format_unknown) { - /* - The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED - in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for - completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED. - */ - if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - result = MA_SHARE_MODE_NOT_SUPPORTED; - } else { - result = MA_FORMAT_NOT_SUPPORTED; - } - - errorMsg = "[WASAPI] Native format not supported."; - goto done; - } - - pData->channelsOut = wf.Format.nChannels; - pData->sampleRateOut = wf.Format.nSamplesPerSec; - - /* Get the internal channel map based on the channel mask. */ - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); - - /* Period size. */ - pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; - pData->periodSizeInFramesOut = pData->periodSizeInFramesIn; - if (pData->periodSizeInFramesOut == 0) { - if (pData->periodSizeInMillisecondsIn == 0) { - if (pData->performanceProfile == ma_performance_profile_low_latency) { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.Format.nSamplesPerSec); - } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.Format.nSamplesPerSec); - } - } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec); - } - } - - periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec; - - - /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ - if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; - - /* - If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing - it and trying it again. - */ - hr = E_FAIL; - for (;;) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); - if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { - if (bufferDuration > 500*10000) { - break; - } else { - if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */ - break; - } - - bufferDuration = bufferDuration * 2; - continue; - } - } else { - break; - } - } - - if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { - ma_uint32 bufferSizeInFrames; - hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); - if (SUCCEEDED(hr)) { - bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5); - - /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */ - ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); - #else - hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient); - #endif - - if (SUCCEEDED(hr)) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); - } - } - } - - if (FAILED(hr)) { - /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */ - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED; - } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY; - } else { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr); - } - goto done; - } - } - - if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) { - /* - Low latency shared mode via IAudioClient3. - - NOTE - ==== - Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the - use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using - any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to - that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. - */ - #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE - { - if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) { - ma_IAudioClient3* pAudioClient3 = NULL; - hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); - if (SUCCEEDED(hr)) { - ma_uint32 defaultPeriodInFrames; - ma_uint32 fundamentalPeriodInFrames; - ma_uint32 minPeriodInFrames; - ma_uint32 maxPeriodInFrames; - hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); - if (SUCCEEDED(hr)) { - ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; - ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; - - /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */ - actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames; - actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames; - - /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */ - actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); - - /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */ - if (actualPeriodInFrames >= desiredPeriodInFrames) { - /* - MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, - IAudioClient3_InitializeSharedAudioStream() will fail. - */ - hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL); - if (SUCCEEDED(hr)) { - wasInitializedUsingIAudioClient3 = MA_TRUE; - pData->periodSizeInFramesOut = actualPeriodInFrames; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n"); - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n"); - } - - ma_IAudioClient3_Release(pAudioClient3); - pAudioClient3 = NULL; - } - } - } - #else - { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n"); - } - #endif - - /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ - if (!wasInitializedUsingIAudioClient3) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL); - if (FAILED(hr)) { - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; - } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { - errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY; - } else { - errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr); - } - - goto done; - } - } - } - - if (!wasInitializedUsingIAudioClient3) { - ma_uint32 bufferSizeInFrames; - hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); - if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); - goto done; - } - - pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; - } - - pData->usingAudioClient3 = wasInitializedUsingIAudioClient3; - - - if (deviceType == ma_device_type_playback) { - result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient); - } else { - result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient); - } - - /*if (FAILED(hr)) {*/ - if (result != MA_SUCCESS) { - errorMsg = "[WASAPI] Failed to get audio client service."; - goto done; - } - - - /* Grab the name of the device. */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - ma_IPropertyStore *pProperties; - hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - PROPVARIANT varName; - ma_PropVariantInit(&varName); - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); - ma_PropVariantClear(pContext, &varName); - } - - ma_IPropertyStore_Release(pProperties); - } - } - #endif - - /* - For the WASAPI backend we need to know the actual IDs of the device in order to do automatic - stream routing so that IDs can be compared and we can determine which device has been detached - and whether or not it matches with our ma_device. - */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - /* Desktop */ - ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id); - } - #else - { - /* UWP */ - /* TODO: Implement me. Need to figure out how to get the ID of the default device. */ - } - #endif - -done: - /* Clean up. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pDeviceInterface != NULL) { - ma_IMMDevice_Release(pDeviceInterface); - } -#else - if (pDeviceInterface != NULL) { - ma_IUnknown_Release(pDeviceInterface); - } -#endif - - if (result != MA_SUCCESS) { - if (pData->pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient); - pData->pRenderClient = NULL; - } - if (pData->pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient); - pData->pCaptureClient = NULL; - } - if (pData->pAudioClient) { - ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - pData->pAudioClient = NULL; - } - - if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s", errorMsg); - } - - return result; - } else { - return MA_SUCCESS; - } -} - -static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_device_init_internal_data__wasapi data; - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* We only re-initialize the playback or capture device. Never a full-duplex device. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - - /* - Before reinitializing the device we need to free the previous audio clients. - - There's a known memory leak here. We will be calling this from the routing change callback that - is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion - this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably - need some system where we post an event, but delay the execution of it until the callback has - returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for - a command thread which might be useful for this. - */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - if (pDevice->wasapi.pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - - if (pDevice->wasapi.pAudioClientCapture) { - /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/ - pDevice->wasapi.pAudioClientCapture = NULL; - } - } - - if (deviceType == ma_device_type_playback) { - if (pDevice->wasapi.pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; - } - - if (pDevice->wasapi.pAudioClientPlayback) { - /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/ - pDevice->wasapi.pAudioClientPlayback = NULL; - } - } - - - if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - } else { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - } - - data.sampleRateIn = pDevice->sampleRate; - data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->wasapi.originalPeriods; - data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; - data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; - result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); - if (result != MA_SUCCESS) { - return result; - } - - /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; - ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); - - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture); - - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); - - /* We must always have a valid ID. */ - ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); - } - - if (deviceType == ma_device_type_playback) { - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; - ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); - - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback); - - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - - /* We must always have a valid ID because rerouting will look at it. */ - ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result = MA_SUCCESS; - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; -#endif - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->wasapi); - pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - - /* Exclusive mode is not allowed with loopback. */ - if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { - return MA_INVALID_DEVICE_CONFIG; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - ma_device_init_internal_data__wasapi data; - data.formatIn = pDescriptorCapture->format; - data.channelsIn = pDescriptorCapture->channels; - data.sampleRateIn = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; - data.periodsIn = pDescriptorCapture->periodCount; - data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - - result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); - if (result != MA_SUCCESS) { - return result; - } - - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; - - /* - The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, - however, because we want to block until we actually have something for the first call to ma_device_read(). - */ - pDevice->wasapi.hEventCapture = CreateEventW(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ - if (pDevice->wasapi.hEventCapture == NULL) { - result = ma_result_from_GetLastError(GetLastError()); - - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); - return result; - } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture); - - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); - - /* We must always have a valid ID. */ - ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); - - /* The descriptor needs to be updated with actual values. */ - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__wasapi data; - data.formatIn = pDescriptorPlayback->format; - data.channelsIn = pDescriptorPlayback->channels; - data.sampleRateIn = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; - data.periodsIn = pDescriptorPlayback->periodCount; - data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - - result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - CloseHandle(pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; - } - return result; - } - - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; - - /* - The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled - only after the whole available space has been filled, never before. - - The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able - to get passed WaitForMultipleObjects(). - */ - pDevice->wasapi.hEventPlayback = CreateEventW(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ - if (pDevice->wasapi.hEventPlayback == NULL) { - result = ma_result_from_GetLastError(GetLastError()); - - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - CloseHandle(pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; - } - - if (pDevice->wasapi.pRenderClient != NULL) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; - } - if (pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - pDevice->wasapi.pAudioClientPlayback = NULL; - } - - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); - return result; - } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback); - - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - - /* We must always have a valid ID because rerouting will look at it. */ - ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); - - /* The descriptor needs to be updated with actual values. */ - pDescriptorPlayback->format = data.formatOut; - pDescriptorPlayback->channels = data.channelsOut; - pDescriptorPlayback->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorPlayback->periodCount = data.periodsOut; - } - - /* - We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When - we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just - stop the device outright and let the application handle it. - */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { - if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID == NULL) { - pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; - } - if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { - pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; - } - } - - hr = ma_CoCreateInstance(pDevice->pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_device_uninit__wasapi(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; - pDevice->wasapi.notificationClient.counter = 1; - pDevice->wasapi.notificationClient.pDevice = pDevice; - - hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); - if (SUCCEEDED(hr)) { - pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator; - } else { - /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */ - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - } -#endif - - c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE); - c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE); - - return MA_SUCCESS; -} - -static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount) -{ - ma_uint32 paddingFramesCount; - HRESULT hr; - ma_share_mode shareMode; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrameCount != NULL); - - *pFrameCount = 0; - - if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { - return MA_INVALID_OPERATION; - } - - /* - I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing - higher level function calls from doing anything because it thinks nothing is available. I have - taken a look at the documentation and it looks like this is unnecessary in exclusive mode. - - From Microsoft's documentation: - - For an exclusive-mode rendering or capture stream that was initialized with the - AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding - value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during - each processing pass. - - Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the - entire buffer. This depends on the caller making sure they wait on the event handler. - */ - shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; - if (shareMode == ma_share_mode_shared) { - /* Shared mode. */ - hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; - } else { - *pFrameCount = paddingFramesCount; - } - } else { - /* Exclusive mode. */ - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; - } else { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n"); - - result = ma_device_reinit__wasapi(pDevice, deviceType); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n"); - return result; - } - - ma_device__post_init_setup(pDevice, deviceType); - - ma_device__on_notification_rerouted(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_device_start__wasapi(ma_device* pDevice) -{ - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device."); - return ma_result_from_HRESULT(hr); - } - - c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device."); - return ma_result_from_HRESULT(hr); - } - - c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__wasapi(ma_device* pDevice) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); - return ma_result_from_HRESULT(hr); - } - - /* The audio client needs to be reset otherwise restarting will fail. */ - hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); - return ma_result_from_HRESULT(hr); - } - - /* If we have a mapped buffer we need to release it. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to - the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. - */ - if (c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) { - /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ - DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; - - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); - } else { - ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; - ma_uint32 framesAvailablePlayback; - for (;;) { - result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); - if (result != MA_SUCCESS) { - break; - } - - if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { - break; - } - - /* - Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames - has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. - */ - if (framesAvailablePlayback == prevFramesAvaialablePlayback) { - break; - } - prevFramesAvaialablePlayback = framesAvailablePlayback; - - WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); - ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */ - } - } - } - - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); - return ma_result_from_HRESULT(hr); - } - - /* The audio client needs to be reset otherwise restarting will fail. */ - hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); - return ma_result_from_HRESULT(hr); - } - - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE); - } - - return MA_SUCCESS; -} - - -#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS -#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 -#endif - -static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalFramesProcessed = 0; - - /* - When reading, we need to get a buffer and process all of it before releasing it. Because the - frame count (frameCount) can be different to the size of the buffer, we'll need to cache the - pointer to the buffer. - */ - - /* Keep running until we've processed the requested number of frames. */ - while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - - /* If we have a mapped data buffer, consume that first. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ - ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { - framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; - } - - /* Now just copy the data over to the output buffer. */ - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - framesToProcessNow, - pDevice->capture.internalFormat, pDevice->capture.internalChannels - ); - - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; - - /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferCaptureLen == 0) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - } - } else { - /* We don't have any cached data pointer, so grab another one. */ - HRESULT hr; - DWORD flags; - - /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); - if (hr == S_OK) { - /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ - - /* Overrun detection. */ - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - /* Glitched. Probably due to an overrun. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); - - /* - If we got an overrun it probably means we're straddling the end of the buffer. In order to prevent - a never-ending sequence of glitches we're going to recover by completely clearing out the capture - buffer. - */ - { - ma_uint32 iterationCount = 4; /* Safety to prevent an infinite loop. */ - ma_uint32 i; - - for (i = 0; i < iterationCount; i += 1) { - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - if (FAILED(hr)) { - break; - } - - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { - break; - } - } - } - - /* We should not have a valid buffer at this point so make sure everything is empty. */ - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } else { - /* The data is clean. */ - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - - if (flags != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); - } - } - - continue; - } else { - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) { - /* - No data is available. We need to wait for more. There's two situations to consider - here. The first is normal capture mode. If this times out it probably means the - microphone isn't delivering data for whatever reason. In this case we'll just - abort the read and return whatever we were able to get. The other situations is - loopback mode, in which case a timeout probably just means the nothing is playing - through the speakers. - */ - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - if (pDevice->type == ma_device_type_loopback) { - continue; /* Keep waiting in loopback mode. */ - } else { - result = MA_ERROR; - break; /* Wait failed. */ - } - } - - /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ - } else { - /* An error occured and we need to abort. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); - result = ma_result_from_HRESULT(hr); - break; - } - } - } - } - - /* - If we were unable to process the entire requested frame count, but we still have a mapped buffer, - there's a good chance either an error occurred or the device was stopped mid-read. In this case - we'll need to make sure the buffer is released. - */ - if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - return result; -} - -static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalFramesProcessed = 0; - - /* Keep writing to the device until it's stopped or we've consumed all of our input. */ - while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - - /* - We're going to do this in a similar way to capture. We'll first check if the cached data pointer - is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with - a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE - it means we need to wait for some data to become available. - */ - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - /* We still have some space available in the mapped data buffer. Write to it. */ - ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { - framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); - } - - /* Now just copy the data over to the output buffer. */ - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - framesToProcessNow, - pDevice->playback.internalFormat, pDevice->playback.internalChannels - ); - - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; - - /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - - /* - In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never - seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine - whether or not we need to wait for more data. - */ - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; /* Wait failed. Probably timed out. */ - } - } - } - } else { - /* We don't have a mapped data buffer so we'll need to get one. */ - HRESULT hr; - ma_uint32 bufferSizeInFrames; - - /* Special rules for exclusive mode. */ - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; - } else { - bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; - } - - hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); - if (hr == S_OK) { - /* We have data available. */ - pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } else { - if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { - /* Not enough data available. We need to wait for more. */ - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; /* Wait failed. Probably timed out. */ - } - } else { - /* Some error occurred. We'll need to abort. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr); - result = ma_result_from_HRESULT(hr); - break; - } - } - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalFramesProcessed; - } - - return result; -} - -static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - SetEvent((HANDLE)pDevice->wasapi.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__wasapi(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_wasapi); - - if (pContext->wasapi.commandThread != NULL) { - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); - ma_context_post_command__wasapi(pContext, &cmd); - ma_thread_wait(&pContext->wasapi.commandThread); - - /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#ifdef MA_WIN32_DESKTOP - /* - WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven - exclusive mode does not work until SP1. - - Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. - */ - { - ma_OSVERSIONINFOEXW osvi; - ma_handle kernel32DLL; - ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; - ma_PFNVerSetConditionMask _VerSetConditionMask; - - kernel32DLL = ma_dlopen(pContext, "kernel32.dll"); - if (kernel32DLL == NULL) { - return MA_NO_BACKEND; - } - - _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW"); - _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask"); - if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { - ma_dlclose(pContext, kernel32DLL); - return MA_NO_BACKEND; - } - - MA_ZERO_OBJECT(&osvi); - osvi.dwOSVersionInfoSize = sizeof(osvi); - osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); - osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); - osvi.wServicePackMajor = 1; - if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { - result = MA_SUCCESS; - } else { - result = MA_NO_BACKEND; - } - - ma_dlclose(pContext, kernel32DLL); - } -#endif - - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&pContext->wasapi); - - /* - Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread - than the one that retrieved it with GetService(). This can result in a deadlock in two - situations: - - 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and - 2) When uninitializing and reinitializing the internal IAudioClient object in response to - automatic stream routing. - - We could define ma_device_uninit() such that it must be called on the same thread as - ma_device_init(). We could also just not release the IAudioClient when performing automatic - stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so - we're going to have to work around this with a worker thread. This is not ideal, but I can't - think of a better way to do this. - - More information about this can be found here: - - https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient - - Note this section: - - When releasing an IAudioRenderClient interface instance, the client must call the interface's - Release method from the same thread as the call to IAudioClient::GetService that created the - object. - */ - { - result = ma_mutex_init(&pContext->wasapi.commandLock); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(0, &pContext->wasapi.commandSem); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - - result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - } - - - pCallbacks->onContextInit = ma_context_init__wasapi; - pCallbacks->onContextUninit = ma_context_uninit__wasapi; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi; - pCallbacks->onDeviceInit = ma_device_init__wasapi; - pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; - pCallbacks->onDeviceStart = ma_device_start__wasapi; - pCallbacks->onDeviceStop = ma_device_stop__wasapi; - pCallbacks->onDeviceRead = ma_device_read__wasapi; - pCallbacks->onDeviceWrite = ma_device_write__wasapi; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; - - return MA_SUCCESS; -} -#endif - -/****************************************************************************** - -DirectSound Backend - -******************************************************************************/ -#ifdef MA_HAS_DSOUND -/*#include */ - -/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/ - -/* miniaudio only uses priority or exclusive modes. */ -#define MA_DSSCL_NORMAL 1 -#define MA_DSSCL_PRIORITY 2 -#define MA_DSSCL_EXCLUSIVE 3 -#define MA_DSSCL_WRITEPRIMARY 4 - -#define MA_DSCAPS_PRIMARYMONO 0x00000001 -#define MA_DSCAPS_PRIMARYSTEREO 0x00000002 -#define MA_DSCAPS_PRIMARY8BIT 0x00000004 -#define MA_DSCAPS_PRIMARY16BIT 0x00000008 -#define MA_DSCAPS_CONTINUOUSRATE 0x00000010 -#define MA_DSCAPS_EMULDRIVER 0x00000020 -#define MA_DSCAPS_CERTIFIED 0x00000040 -#define MA_DSCAPS_SECONDARYMONO 0x00000100 -#define MA_DSCAPS_SECONDARYSTEREO 0x00000200 -#define MA_DSCAPS_SECONDARY8BIT 0x00000400 -#define MA_DSCAPS_SECONDARY16BIT 0x00000800 - -#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001 -#define MA_DSBCAPS_STATIC 0x00000002 -#define MA_DSBCAPS_LOCHARDWARE 0x00000004 -#define MA_DSBCAPS_LOCSOFTWARE 0x00000008 -#define MA_DSBCAPS_CTRL3D 0x00000010 -#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020 -#define MA_DSBCAPS_CTRLPAN 0x00000040 -#define MA_DSBCAPS_CTRLVOLUME 0x00000080 -#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 -#define MA_DSBCAPS_CTRLFX 0x00000200 -#define MA_DSBCAPS_STICKYFOCUS 0x00004000 -#define MA_DSBCAPS_GLOBALFOCUS 0x00008000 -#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000 -#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 -#define MA_DSBCAPS_LOCDEFER 0x00040000 -#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000 - -#define MA_DSBPLAY_LOOPING 0x00000001 -#define MA_DSBPLAY_LOCHARDWARE 0x00000002 -#define MA_DSBPLAY_LOCSOFTWARE 0x00000004 -#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008 -#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 -#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 - -#define MA_DSCBSTART_LOOPING 0x00000001 - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - WAVEFORMATEX* lpwfxFormat; - GUID guid3DAlgorithm; -} MA_DSBUFFERDESC; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - WAVEFORMATEX* lpwfxFormat; - DWORD dwFXCount; - void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ -} MA_DSCBUFFERDESC; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwMinSecondarySampleRate; - DWORD dwMaxSecondarySampleRate; - DWORD dwPrimaryBuffers; - DWORD dwMaxHwMixingAllBuffers; - DWORD dwMaxHwMixingStaticBuffers; - DWORD dwMaxHwMixingStreamingBuffers; - DWORD dwFreeHwMixingAllBuffers; - DWORD dwFreeHwMixingStaticBuffers; - DWORD dwFreeHwMixingStreamingBuffers; - DWORD dwMaxHw3DAllBuffers; - DWORD dwMaxHw3DStaticBuffers; - DWORD dwMaxHw3DStreamingBuffers; - DWORD dwFreeHw3DAllBuffers; - DWORD dwFreeHw3DStaticBuffers; - DWORD dwFreeHw3DStreamingBuffers; - DWORD dwTotalHwMemBytes; - DWORD dwFreeHwMemBytes; - DWORD dwMaxContigFreeHwMemBytes; - DWORD dwUnlockTransferRateHwBuffers; - DWORD dwPlayCpuOverheadSwBuffers; - DWORD dwReserved1; - DWORD dwReserved2; -} MA_DSCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwUnlockTransferRate; - DWORD dwPlayCpuOverhead; -} MA_DSBCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwFormats; - DWORD dwChannels; -} MA_DSCCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; -} MA_DSCBCAPS; - -typedef struct -{ - DWORD dwOffset; - HANDLE hEventNotify; -} MA_DSBPOSITIONNOTIFY; - -typedef struct ma_IDirectSound ma_IDirectSound; -typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer; -typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture; -typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer; -typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify; - - -/* -COM objects. The way these work is that you have a vtable (a list of function pointers, kind of -like how C++ works internally), and then you have a structure with a single member, which is a -pointer to the vtable. The vtable is where the methods of the object are defined. Methods need -to be in a specific order, and parent classes need to have their methods declared first. -*/ - -/* IDirectSound */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis); - - /* IDirectSound */ - HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter); - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps); - HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate); - HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel); - HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis); - HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig); - HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice); -} ma_IDirectSoundVtbl; -struct ma_IDirectSound -{ - ma_IDirectSoundVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); } -static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); } -static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); } -static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); } -static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); } -static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); } -static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); } -static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } - - -/* IDirectSoundBuffer */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis); - - /* IDirectSoundBuffer */ - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); - HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); - HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); - HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); - HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); - HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc); - HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); - HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat); - HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); - HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); - HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis); - HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); - HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis); -} ma_IDirectSoundBufferVtbl; -struct ma_IDirectSoundBuffer -{ - ma_IDirectSoundBufferVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); } - - -/* IDirectSoundCapture */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis); - - /* IDirectSoundCapture */ - HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter); - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice); -} ma_IDirectSoundCaptureVtbl; -struct ma_IDirectSoundCapture -{ - ma_IDirectSoundCaptureVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } - - -/* IDirectSoundCaptureBuffer */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis); - - /* IDirectSoundCaptureBuffer */ - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); - HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); - HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); - HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis); - HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); -} ma_IDirectSoundCaptureBufferVtbl; -struct ma_IDirectSoundCaptureBuffer -{ - ma_IDirectSoundCaptureBufferVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } - - -/* IDirectSoundNotify */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis); - - /* IDirectSoundNotify */ - HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies); -} ma_IDirectSoundNotifyVtbl; -struct ma_IDirectSoundNotify -{ - ma_IDirectSoundNotifyVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } - - -typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext); - -static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) -{ - /* Normalize the range in case we were given something stupid. */ - if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) { - sampleRateMin = (ma_uint32)ma_standard_sample_rate_min; - } - if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) { - sampleRateMax = (ma_uint32)ma_standard_sample_rate_max; - } - if (sampleRateMin > sampleRateMax) { - sampleRateMin = sampleRateMax; - } - - if (sampleRateMin == sampleRateMax) { - return sampleRateMax; - } else { - size_t iStandardRate; - for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; - if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) { - return standardRate; - } - } - } - - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return 0; -} - -/* -Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown, -the channel count and channel map will be left unmodified. -*/ -static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut) -{ - WORD channels; - DWORD channelMap; - - channels = 0; - if (pChannelsOut != NULL) { - channels = *pChannelsOut; - } - - channelMap = 0; - if (pChannelMapOut != NULL) { - channelMap = *pChannelMapOut; - } - - /* - The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper - 16 bits is for the geometry. - */ - switch ((BYTE)(speakerConfig)) { - case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break; - case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break; - case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; - case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; - case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; - default: break; - } - - if (pChannelsOut != NULL) { - *pChannelsOut = channels; - } - - if (pChannelMapOut != NULL) { - *pChannelMapOut = channelMap; - } -} - - -static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound) -{ - ma_IDirectSound* pDirectSound; - HWND hWnd; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDirectSound != NULL); - - *ppDirectSound = NULL; - pDirectSound = NULL; - - if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* The cooperative level must be set before doing anything else. */ - hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); - if (hWnd == NULL) { - hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); - } - - hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - *ppDirectSound = pDirectSound; - return MA_SUCCESS; -} - -static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture) -{ - ma_IDirectSoundCapture* pDirectSoundCapture; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDirectSoundCapture != NULL); - - /* DirectSound does not support exclusive mode for capture. */ - if (shareMode == ma_share_mode_exclusive) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - *ppDirectSoundCapture = NULL; - pDirectSoundCapture = NULL; - - hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - *ppDirectSoundCapture = pDirectSoundCapture; - return MA_SUCCESS; -} - -static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate) -{ - HRESULT hr; - MA_DSCCAPS caps; - WORD bitsPerSample; - DWORD sampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDirectSoundCapture != NULL); - - if (pChannels) { - *pChannels = 0; - } - if (pBitsPerSample) { - *pBitsPerSample = 0; - } - if (pSampleRate) { - *pSampleRate = 0; - } - - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - if (pChannels) { - *pChannels = (WORD)caps.dwChannels; - } - - /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */ - bitsPerSample = 16; - sampleRate = 48000; - - if (caps.dwChannels == 1) { - if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ - } - } - } else if (caps.dwChannels == 2) { - if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ - } - } - } - - if (pBitsPerSample) { - *pBitsPerSample = bitsPerSample; - } - if (pSampleRate) { - *pSampleRate = sampleRate; - } - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_context* pContext; - ma_device_type deviceType; - ma_enum_devices_callback_proc callback; - void* pUserData; - ma_bool32 terminated; -} ma_context_enumerate_devices_callback_data__dsound; - -static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) -{ - ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; - ma_device_info deviceInfo; - - (void)lpcstrModule; - - MA_ZERO_OBJECT(&deviceInfo); - - /* ID. */ - if (lpGuid != NULL) { - MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16); - } else { - MA_ZERO_MEMORY(deviceInfo.id.dsound, 16); - deviceInfo.isDefault = MA_TRUE; - } - - /* Name / Description */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1); - - - /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ - MA_ASSERT(pData != NULL); - pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData); - if (pData->terminated) { - return FALSE; /* Stop enumeration. */ - } else { - return TRUE; /* Continue enumeration. */ - } -} - -static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__dsound data; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - data.pContext = pContext; - data.callback = callback; - data.pUserData = pUserData; - data.terminated = MA_FALSE; - - /* Playback. */ - if (!data.terminated) { - data.deviceType = ma_device_type_playback; - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); - } - - /* Capture. */ - if (!data.terminated) { - data.deviceType = ma_device_type_capture; - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); - } - - return MA_SUCCESS; -} - - -typedef struct -{ - const ma_device_id* pDeviceID; - ma_device_info* pDeviceInfo; - ma_bool32 found; -} ma_context_get_device_info_callback_data__dsound; - -static BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) -{ - ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; - MA_ASSERT(pData != NULL); - - if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) { - /* Default device. */ - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); - pData->pDeviceInfo->isDefault = MA_TRUE; - pData->found = MA_TRUE; - return FALSE; /* Stop enumeration. */ - } else { - /* Not the default device. */ - if (lpGuid != NULL && pData->pDeviceID != NULL) { - if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); - pData->found = MA_TRUE; - return FALSE; /* Stop enumeration. */ - } - } - } - - (void)lpcstrModule; - return TRUE; -} - -static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - HRESULT hr; - - if (pDeviceID != NULL) { - ma_context_get_device_info_callback_data__dsound data; - - /* ID. */ - MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16); - - /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */ - data.pDeviceID = pDeviceID; - data.pDeviceInfo = pDeviceInfo; - data.found = MA_FALSE; - if (deviceType == ma_device_type_playback) { - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data); - } else { - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data); - } - - if (!data.found) { - return MA_NO_DEVICE; - } - } else { - /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */ - - /* ID */ - MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16); - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; - } - - /* Retrieving detailed information is slightly different depending on the device type. */ - if (deviceType == ma_device_type_playback) { - /* Playback. */ - ma_IDirectSound* pDirectSound; - MA_DSCAPS caps; - WORD channels; - - result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound); - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps(pDirectSound, &caps); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - - /* Channels. Only a single channel count is reported for DirectSound. */ - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - /* It supports at least stereo, but could support more. */ - DWORD speakerConfig; - - channels = 2; - - /* Look at the speaker configuration to get a better idea on the channel count. */ - hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig); - if (SUCCEEDED(hr)) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - channels = 1; - } - - - /* - In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel - count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio - in order to keep the size of this within reason. - */ - if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */ - size_t iStandardSampleRate; - for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { - ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; - if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - } - } - } else { - /* Only a single sample rate is supported. */ - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - } - - ma_IDirectSound_Release(pDirectSound); - } else { - /* - Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture - devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just - reporting the best format. - */ - ma_IDirectSoundCapture* pDirectSoundCapture; - WORD channels; - WORD bitsPerSample; - DWORD sampleRate; - - result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate); - if (result != MA_SUCCESS) { - ma_IDirectSoundCapture_Release(pDirectSoundCapture); - return result; - } - - ma_IDirectSoundCapture_Release(pDirectSoundCapture); - - /* The format is always an integer format and is based on the bits per sample. */ - if (bitsPerSample == 8) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; - } else if (bitsPerSample == 16) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; - } else if (bitsPerSample == 24) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; - } else if (bitsPerSample == 32) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - - pDeviceInfo->nativeDataFormats[0].channels = channels; - pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - } - - return MA_SUCCESS; -} - - - -static ma_result ma_device_uninit__dsound(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->dsound.pCaptureBuffer != NULL) { - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - } - if (pDevice->dsound.pCapture != NULL) { - ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture); - } - - if (pDevice->dsound.pPlaybackBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - } - if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); - } - if (pDevice->dsound.pPlayback != NULL) { - ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF) -{ - GUID subformat; - - if (format == ma_format_unknown) { - format = MA_DEFAULT_FORMAT; - } - - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - switch (format) - { - case ma_format_u8: - case ma_format_s16: - case ma_format_s24: - /*case ma_format_s24_32:*/ - case ma_format_s32: - { - subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } break; - - case ma_format_f32: - { - subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } break; - - default: - return MA_FORMAT_NOT_SUPPORTED; - } - - MA_ZERO_OBJECT(pWF); - pWF->Format.cbSize = sizeof(*pWF); - pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - pWF->Format.nChannels = (WORD)channels; - pWF->Format.nSamplesPerSec = (DWORD)sampleRate; - pWF->Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - pWF->Format.nBlockAlign = (WORD)(pWF->Format.nChannels * pWF->Format.wBitsPerSample / 8); - pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec; - pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample; - pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); - pWF->SubFormat = subformat; - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for - reliable glitch-free processing so going to use 30ms instead. - */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); - ma_uint32 periodSizeInFrames; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); - if (periodSizeInFrames < minPeriodSizeInFrames) { - periodSizeInFrames = minPeriodSizeInFrames; - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->dsound); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* - Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize - the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using - full-duplex mode. - */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - WAVEFORMATEXTENSIBLE wf; - MA_DSCBUFFERDESC descDS; - ma_uint32 periodSizeInFrames; - ma_uint32 periodCount; - char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - WAVEFORMATEXTENSIBLE* pActualFormat; - - result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - - /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.Format.nSamplesPerSec, pConfig->performanceProfile); - periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; - - MA_ZERO_OBJECT(&descDS); - descDS.dwSize = sizeof(descDS); - descDS.dwFlags = 0; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.Format.nBlockAlign; - descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - /* Get the _actual_ properties of the buffer. */ - pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); - return ma_result_from_HRESULT(hr); - } - - /* We can now start setting the output data formats. */ - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat); - pDescriptorCapture->channels = pActualFormat->Format.nChannels; - pDescriptorCapture->sampleRate = pActualFormat->Format.nSamplesPerSec; - - /* Get the native channel map based on the channel mask. */ - if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - } else { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - } - - /* - After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the - user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case. - */ - if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) { - descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount; - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - } - - /* DirectSound should give us a buffer exactly the size we asked for. */ - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = periodCount; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - WAVEFORMATEXTENSIBLE wf; - MA_DSBUFFERDESC descDSPrimary; - MA_DSCAPS caps; - char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - WAVEFORMATEXTENSIBLE* pActualFormat; - ma_uint32 periodSizeInFrames; - ma_uint32 periodCount; - MA_DSBUFFERDESC descDS; - - result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - MA_ZERO_OBJECT(&descDSPrimary); - descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC); - descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - - /* We may want to make some adjustments to the format if we are using defaults. */ - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - if (pDescriptorPlayback->channels == 0) { - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - DWORD speakerConfig; - - /* It supports at least stereo, but could support more. */ - wf.Format.nChannels = 2; - - /* Look at the speaker configuration to get a better idea on the channel count. */ - if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - wf.Format.nChannels = 1; - } - } - - if (pDescriptorPlayback->sampleRate == 0) { - /* We base the sample rate on the values returned by GetCaps(). */ - if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); - } else { - wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate; - } - } - - wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; - - /* - From MSDN: - - The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest - supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer - and compare the result with the format that was requested with the SetFormat method. - */ - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* Get the _actual_ properties of the buffer. */ - pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* We now have enough information to start setting some output properties. */ - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat); - pDescriptorPlayback->channels = pActualFormat->Format.nChannels; - pDescriptorPlayback->sampleRate = pActualFormat->Format.nSamplesPerSec; - - /* Get the internal channel map based on the channel mask. */ - if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - } else { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - } - - /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; - - /* - Meaning of dwFlags (from MSDN): - - DSBCAPS_CTRLPOSITIONNOTIFY - The buffer has position notification capability. - - DSBCAPS_GLOBALFOCUS - With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to - another application, even if the new application uses DirectSound. - - DSBCAPS_GETCURRENTPOSITION2 - In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated - sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the - application can get a more accurate play cursor. - */ - MA_ZERO_OBJECT(&descDS); - descDS.dwSize = sizeof(descDS); - descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); - descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* DirectSound should give us a buffer exactly the size we asked for. */ - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = periodCount; - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_data_loop__dsound(ma_device* pDevice) -{ - ma_result result = MA_SUCCESS; - ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - HRESULT hr; - DWORD lockOffsetInBytesCapture; - DWORD lockSizeInBytesCapture; - DWORD mappedSizeInBytesCapture; - DWORD mappedDeviceFramesProcessedCapture; - void* pMappedDeviceBufferCapture; - DWORD lockOffsetInBytesPlayback; - DWORD lockSizeInBytesPlayback; - DWORD mappedSizeInBytesPlayback; - void* pMappedDeviceBufferPlayback; - DWORD prevReadCursorInBytesCapture = 0; - DWORD prevPlayCursorInBytesPlayback = 0; - ma_bool32 physicalPlayCursorLoopFlagPlayback = 0; - DWORD virtualWriteCursorInBytesPlayback = 0; - ma_bool32 virtualWriteCursorLoopFlagPlayback = 0; - ma_bool32 isPlaybackDeviceStarted = MA_FALSE; - ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ - ma_uint32 waitTimeInMilliseconds = 1; - - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); - return ma_result_from_HRESULT(hr); - } - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - switch (pDevice->type) - { - case ma_device_type_duplex: - { - DWORD physicalCaptureCursorInBytes; - DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - /* If nothing is available we just sleep for a bit and return from this iteration. */ - if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - /* - The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure - we don't return until every frame has been copied over. - */ - if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { - /* The capture position has not looped. This is the simple case. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); - } else { - /* - The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, - do it again from the start. - */ - if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { - /* Lock up to the end of the buffer. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; - } else { - /* Lock starting from the start of the buffer. */ - lockOffsetInBytesCapture = 0; - lockSizeInBytesCapture = physicalReadCursorInBytes; - } - } - - if (lockSizeInBytesCapture == 0) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - return ma_result_from_HRESULT(hr); - } - - - /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */ - mappedDeviceFramesProcessedCapture = 0; - - for (;;) { /* Keep writing to the playback device. */ - ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 outputFramesInClientFormatCount; - ma_uint32 outputFramesInClientFormatConsumed = 0; - ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap); - ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture; - void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture); - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess); - if (result != MA_SUCCESS) { - break; - } - - outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess; - mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess; - - ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); - - /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */ - for (;;) { - ma_uint32 framesWrittenThisIteration; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - DWORD availableBytesPlayback; - DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */ - - /* We need the physical play and write cursors. */ - if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } - - /* If there's no room available for writing we need to wait for more. */ - if (availableBytesPlayback == 0) { - /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ - if (!isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } else { - ma_sleep(waitTimeInMilliseconds); - continue; - } - } - - - /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ - lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. Go up to the end of the buffer. */ - lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - } else { - /* Different loop iterations. Go up to the physical play cursor. */ - lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } - - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - /* - Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent - endless glitching due to it constantly running out of data. - */ - if (isPlaybackDeviceStarted) { - DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback; - if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) { - silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback; - if (silentPaddingInBytes > lockSizeInBytesPlayback) { - silentPaddingInBytes = lockSizeInBytesPlayback; - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); - } - } - - /* At this point we have a buffer for output. */ - if (silentPaddingInBytes > 0) { - MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes); - framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback; - } else { - ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed); - ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback; - void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback); - void* pConvertedFramesOut = pMappedDeviceBufferPlayback; - - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut); - if (result != MA_SUCCESS) { - break; - } - - outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut; - framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut; - } - - - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback; - if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) { - virtualWriteCursorInBytesPlayback = 0; - virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; - } - - /* - We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds - a bit of a buffer to prevent the playback buffer from getting starved. - */ - framesWrittenToPlaybackDevice += framesWrittenThisIteration; - if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } - - if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) { - break; /* We're finished with the output data.*/ - } - } - - if (clientCapturedFramesToProcess == 0) { - break; /* We just consumed every input sample. */ - } - } - - - /* At this point we're done with the mapped portion of the capture buffer. */ - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); - } - prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); - } break; - - - - case ma_device_type_capture: - { - DWORD physicalCaptureCursorInBytes; - DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); - if (FAILED(hr)) { - return MA_ERROR; - } - - /* If the previous capture position is the same as the current position we need to wait a bit longer. */ - if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) { - ma_sleep(waitTimeInMilliseconds); - continue; - } - - /* Getting here means we have capture data available. */ - if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { - /* The capture position has not looped. This is the simple case. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); - } else { - /* - The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, - do it again from the start. - */ - if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { - /* Lock up to the end of the buffer. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; - } else { - /* Lock starting from the start of the buffer. */ - lockOffsetInBytesCapture = 0; - lockSizeInBytesCapture = physicalReadCursorInBytes; - } - } - - if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - } - - if (lockSizeInBytesCapture != mappedSizeInBytesCapture) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture); - } - - ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); - - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); - } - prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; - - if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) { - prevReadCursorInBytesCapture = 0; - } - } break; - - - - case ma_device_type_playback: - { - DWORD availableBytesPlayback; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); - if (FAILED(hr)) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } - - /* If there's no room available for writing we need to wait for more. */ - if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) { - /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ - if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } else { - ma_sleep(waitTimeInMilliseconds); - continue; - } - } - - /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ - lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. Go up to the end of the buffer. */ - lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - } else { - /* Different loop iterations. Go up to the physical play cursor. */ - lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } - - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - /* At this point we have a buffer for output. */ - ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback); - - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback; - if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) { - virtualWriteCursorInBytesPlayback = 0; - virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; - } - - /* - We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds - a bit of a buffer to prevent the playback buffer from getting starved. - */ - framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback; - if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } - } break; - - - default: return MA_INVALID_ARGS; /* Invalid device type. */ - } - - if (result != MA_SUCCESS) { - return result; - } - } - - /* Getting here means the device is being stopped. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */ - if (isPlaybackDeviceStarted) { - for (;;) { - DWORD availableBytesPlayback = 0; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); - if (FAILED(hr)) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - break; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - break; - } - } - - if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) { - break; - } - - ma_sleep(waitTimeInMilliseconds); - } - } - - hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); - } - - ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__dsound(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_dsound); - - ma_dlclose(pContext, pContext->dsound.hDSoundDLL); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll"); - if (pContext->dsound.hDSoundDLL == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate"); - pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); - pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); - - pCallbacks->onContextInit = ma_context_init__dsound; - pCallbacks->onContextUninit = ma_context_uninit__dsound; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound; - pCallbacks->onDeviceInit = ma_device_init__dsound; - pCallbacks->onDeviceUninit = ma_device_uninit__dsound; - pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */ - pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */ - pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound; - - return MA_SUCCESS; -} -#endif - - - -/****************************************************************************** - -WinMM Backend - -******************************************************************************/ -#ifdef MA_HAS_WINMM - -/* -Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures -are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping -the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version. -*/ -typedef struct -{ - WORD wMid; - WORD wPid; - MMVERSION vDriverVersion; - CHAR szPname[MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - DWORD dwSupport; - GUID ManufacturerGuid; - GUID ProductGuid; - GUID NameGuid; -} MA_WAVEOUTCAPS2A; -typedef struct -{ - WORD wMid; - WORD wPid; - MMVERSION vDriverVersion; - CHAR szPname[MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - GUID ManufacturerGuid; - GUID ProductGuid; - GUID NameGuid; -} MA_WAVEINCAPS2A; - -typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); -typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc); -typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo); -typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo); -typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); -typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic); -typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi); -typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi); -typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi); - -static ma_result ma_result_from_MMRESULT(MMRESULT resultMM) -{ - switch (resultMM) { - case MMSYSERR_NOERROR: return MA_SUCCESS; - case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; - case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; - case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; - case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; - case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; - case MMSYSERR_HANDLEBUSY: return MA_BUSY; - case MMSYSERR_ERROR: return MA_ERROR; - default: return MA_ERROR; - } -} - -static char* ma_find_last_character(char* str, char ch) -{ - char* last; - - if (str == NULL) { - return NULL; - } - - last = NULL; - while (*str != '\0') { - if (*str == ch) { - last = str; - } - - str += 1; - } - - return last; -} - -static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels) -{ - return periodSizeInFrames * ma_get_bytes_per_frame(format, channels); -} - - -/* -Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so -we can do things generically and typesafely. Names are being kept the same for consistency. -*/ -typedef struct -{ - CHAR szPname[MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - GUID NameGuid; -} MA_WAVECAPSA; - -static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate) -{ - WORD bitsPerSample = 0; - DWORD sampleRate = 0; - - if (pBitsPerSample) { - *pBitsPerSample = 0; - } - if (pSampleRate) { - *pSampleRate = 0; - } - - if (channels == 1) { - bitsPerSample = 16; - if ((dwFormats & WAVE_FORMAT_48M16) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((dwFormats & WAVE_FORMAT_48M08) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) { - sampleRate = 96000; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - } - } else { - bitsPerSample = 16; - if ((dwFormats & WAVE_FORMAT_48S16) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((dwFormats & WAVE_FORMAT_48S08) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) { - sampleRate = 96000; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - } - } - - if (pBitsPerSample) { - *pBitsPerSample = bitsPerSample; - } - if (pSampleRate) { - *pSampleRate = sampleRate; - } - - return MA_SUCCESS; -} - -static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF) -{ - ma_result result; - - MA_ASSERT(pWF != NULL); - - MA_ZERO_OBJECT(pWF); - pWF->cbSize = sizeof(*pWF); - pWF->wFormatTag = WAVE_FORMAT_PCM; - pWF->nChannels = (WORD)channels; - if (pWF->nChannels > 2) { - pWF->nChannels = 2; - } - - result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec); - if (result != MA_SUCCESS) { - return result; - } - - pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); - pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo) -{ - WORD bitsPerSample; - DWORD sampleRate; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* - Name / Description - - Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking - situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try - looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. - */ - - /* Set the default to begin with. */ - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1); - - /* - Now try the registry. There's a few things to consider here: - - The name GUID can be null, in which we case we just need to stick to the original 31 characters. - - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. - - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The - problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), - but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to - usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component - name, and then concatenate the name from the registry. - */ - if (!ma_is_guid_null(&pCaps->NameGuid)) { - wchar_t guidStrW[256]; - if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { - char guidStr[256]; - char keyStr[1024]; - HKEY hKey; - - WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE); - - ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"); - ma_strcat_s(keyStr, sizeof(keyStr), guidStr); - - if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { - BYTE nameFromReg[512]; - DWORD nameFromRegSize = sizeof(nameFromReg); - LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize); - ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); - - if (resultWin32 == ERROR_SUCCESS) { - /* We have the value from the registry, so now we need to construct the name string. */ - char name[1024]; - if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { - char* nameBeg = ma_find_last_character(name, '('); - if (nameBeg != NULL) { - size_t leadingLen = (nameBeg - name); - ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1); - - /* The closing ")", if it can fit. */ - if (leadingLen + nameFromRegSize < sizeof(name)-1) { - ma_strcat_s(name, sizeof(name), ")"); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1); - } - } - } - } - } - } - - - result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate); - if (result != MA_SUCCESS) { - return result; - } - - if (bitsPerSample == 8) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; - } else if (bitsPerSample == 16) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; - } else if (bitsPerSample == 24) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; - } else if (bitsPerSample == 32) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels; - pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo) -{ - MA_WAVECAPSA caps; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); - caps.dwFormats = pCaps->dwFormats; - caps.wChannels = pCaps->wChannels; - caps.NameGuid = pCaps->NameGuid; - return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); -} - -static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo) -{ - MA_WAVECAPSA caps; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); - caps.dwFormats = pCaps->dwFormats; - caps.wChannels = pCaps->wChannels; - caps.NameGuid = pCaps->NameGuid; - return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); -} - - -static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - UINT playbackDeviceCount; - UINT captureDeviceCount; - UINT iPlaybackDevice; - UINT iCaptureDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); - for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { - MMRESULT result; - MA_WAVEOUTCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { - ma_device_info deviceInfo; - - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.winmm = iPlaybackDevice; - - /* The first enumerated device is the default device. */ - if (iPlaybackDevice == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - return MA_SUCCESS; /* Enumeration was stopped. */ - } - } - } - } - - /* Capture. */ - captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); - for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { - MMRESULT result; - MA_WAVEINCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { - ma_device_info deviceInfo; - - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.winmm = iCaptureDevice; - - /* The first enumerated device is the default device. */ - if (iCaptureDevice == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - return MA_SUCCESS; /* Enumeration was stopped. */ - } - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - UINT winMMDeviceID; - - MA_ASSERT(pContext != NULL); - - winMMDeviceID = 0; - if (pDeviceID != NULL) { - winMMDeviceID = (UINT)pDeviceID->winmm; - } - - pDeviceInfo->id.winmm = winMMDeviceID; - - /* The first ID is the default device. */ - if (winMMDeviceID == 0) { - pDeviceInfo->isDefault = MA_TRUE; - } - - if (deviceType == ma_device_type_playback) { - MMRESULT result; - MA_WAVEOUTCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { - return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); - } - } else { - MMRESULT result; - MA_WAVEINCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { - return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); - } - } - - return MA_NO_DEVICE; -} - - -static ma_result ma_device_uninit__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture); - CloseHandle((HANDLE)pDevice->winmm.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback); - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback); - CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* WinMM has a minimum period size of 40ms. */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate); - ma_uint32 periodSizeInFrames; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); - if (periodSizeInFrames < minPeriodSizeInFrames) { - periodSizeInFrames = minPeriodSizeInFrames; - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - const char* errorMsg = ""; - ma_result errorCode = MA_ERROR; - ma_result result = MA_SUCCESS; - ma_uint32 heapSize; - UINT winMMDeviceIDPlayback = 0; - UINT winMMDeviceIDCapture = 0; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->winmm); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exlusive mode with WinMM. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pDescriptorPlayback->pDeviceID != NULL) { - winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm; - } - if (pDescriptorCapture->pDeviceID != NULL) { - winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm; - } - - /* The capture device needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - WAVEINCAPSA caps; - WAVEFORMATEX wf; - MMRESULT resultMM; - - /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventCapture = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventCapture == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); - goto on_error; - } - - /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; - goto on_error; - } - - result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); - if (result != MA_SUCCESS) { - errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; - goto on_error; - } - - resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); - if (resultMM != MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; - goto on_error; - } - - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf); - pDescriptorCapture->channels = wf.nChannels; - pDescriptorCapture->sampleRate = wf.nSamplesPerSec; - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - WAVEOUTCAPSA caps; - WAVEFORMATEX wf; - MMRESULT resultMM; - - /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventPlayback = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventPlayback == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); - goto on_error; - } - - /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; - goto on_error; - } - - result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); - if (result != MA_SUCCESS) { - errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; - goto on_error; - } - - resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); - if (resultMM != MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; - goto on_error; - } - - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf); - pDescriptorPlayback->channels = wf.nChannels; - pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - } - - /* - The heap allocated data is allocated like so: - - [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] - */ - heapSize = 0; - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); - } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); - } - - pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); - if (pDevice->winmm._pHeapData == NULL) { - errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; - goto on_error; - } - - MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize); - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPeriod; - - if (pConfig->deviceType == ma_device_type_capture) { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount)); - } else { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); - } - - /* Prepare headers. */ - for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); - - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); - - /* - The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means - it's unlocked and available for writing. A value of 1 means it's locked. - */ - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPeriod; - - if (pConfig->deviceType == ma_device_type_playback) { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDescriptorPlayback->periodCount); - } else { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount)); - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); - } - - /* Prepare headers. */ - for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); - - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR)); - - /* - The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means - it's unlocked and available for writing. A value of 1 means it's locked. - */ - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; - } - } - - return MA_SUCCESS; - -on_error: - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { - ma_uint32 iPeriod; - for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); - } - } - - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { - ma_uint32 iPeriod; - for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR)); - } - } - - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); - } - - return errorCode; -} - -static ma_result ma_device_start__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - MMRESULT resultMM; - WAVEHDR* pWAVEHDR; - ma_uint32 iPeriod; - - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); - - /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ - for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); - if (resultMM != MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); - return ma_result_from_MMRESULT(resultMM); - } - - /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */ - pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */ - } - - /* Capture devices need to be explicitly started, unlike playback devices. */ - resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); - return ma_result_from_MMRESULT(resultMM); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__winmm(ma_device* pDevice) -{ - MMRESULT resultMM; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.hDeviceCapture == NULL) { - return MA_INVALID_ARGS; - } - - resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_uint32 iPeriod; - WAVEHDR* pWAVEHDR; - - if (pDevice->winmm.hDevicePlayback == NULL) { - return MA_INVALID_ARGS; - } - - /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; - for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { - if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { - break; /* An error occurred so just abandon ship and stop the device without draining. */ - } - - pWAVEHDR[iPeriod].dwUser = 0; - } - } - - resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback); - if (resultMM != MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - MMRESULT resultMM; - ma_uint32 totalFramesWritten; - WAVEHDR* pWAVEHDR; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pPCMFrames != NULL); - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; - - /* Keep processing as much data as possible. */ - totalFramesWritten = 0; - while (totalFramesWritten < frameCount) { - /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ - /* - This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to - write it out and move on to the next iteration. - */ - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback; - - ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten)); - const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf); - void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf); - MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - - pDevice->winmm.headerFramesConsumedPlayback += framesToCopy; - totalFramesWritten += framesToCopy; - - /* If we've consumed the buffer entirely we need to write it out to the device. */ - if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); - - /* The device will be started here. */ - resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR)); - if (resultMM != MMSYSERR_NOERROR) { - result = ma_result_from_MMRESULT(resultMM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); - break; - } - - /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; - pDevice->winmm.headerFramesConsumedPlayback = 0; - } - - /* If at this point we have consumed the entire input buffer we can return. */ - MA_ASSERT(totalFramesWritten <= frameCount); - if (totalFramesWritten == frameCount) { - break; - } - - /* Getting here means there's more to process. */ - continue; - } - - /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; - } - - /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ - pDevice->winmm.headerFramesConsumedPlayback = 0; - } - - /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - break; - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalFramesWritten; - } - - return result; -} - -static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - MMRESULT resultMM; - ma_uint32 totalFramesRead; - WAVEHDR* pWAVEHDR; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pPCMFrames != NULL); - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; - - /* Keep processing as much data as possible. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ - /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */ - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture; - - ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead)); - const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf); - void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf); - MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - - pDevice->winmm.headerFramesConsumedCapture += framesToCopy; - totalFramesRead += framesToCopy; - - /* If we've consumed the buffer entirely we need to add it back to the device. */ - if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); - - /* The device will be started here. */ - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR)); - if (resultMM != MMSYSERR_NOERROR) { - result = ma_result_from_MMRESULT(resultMM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); - break; - } - - /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; - pDevice->winmm.headerFramesConsumedCapture = 0; - } - - /* If at this point we have filled the entire input buffer we can return. */ - MA_ASSERT(totalFramesRead <= frameCount); - if (totalFramesRead == frameCount) { - break; - } - - /* Getting here means there's more to process. */ - continue; - } - - /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; - } - - /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ - pDevice->winmm.headerFramesConsumedCapture = 0; - } - - /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - break; - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -static ma_result ma_context_uninit__winmm(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_winmm); - - ma_dlclose(pContext, pContext->winmm.hWinMM); - return MA_SUCCESS; -} - -static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll"); - if (pContext->winmm.hWinMM == NULL) { - return MA_NO_BACKEND; - } - - pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset"); - - pCallbacks->onContextInit = ma_context_init__winmm; - pCallbacks->onContextUninit = ma_context_uninit__winmm; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm; - pCallbacks->onDeviceInit = ma_device_init__winmm; - pCallbacks->onDeviceUninit = ma_device_uninit__winmm; - pCallbacks->onDeviceStart = ma_device_start__winmm; - pCallbacks->onDeviceStop = ma_device_stop__winmm; - pCallbacks->onDeviceRead = ma_device_read__winmm; - pCallbacks->onDeviceWrite = ma_device_write__winmm; - pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */ - - return MA_SUCCESS; -} -#endif - - - - -/****************************************************************************** - -ALSA Backend - -******************************************************************************/ -#ifdef MA_HAS_ALSA - -#include /* poll(), struct pollfd */ -#include /* eventfd() */ - -#ifdef MA_NO_RUNTIME_LINKING - -/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ -#if !defined(__cplusplus) - #if defined(__STRICT_ANSI__) - #if !defined(inline) - #define inline __inline__ __attribute__((always_inline)) - #define MA_INLINE_DEFINED - #endif - #endif -#endif -#include -#if defined(MA_INLINE_DEFINED) - #undef inline - #undef MA_INLINE_DEFINED -#endif - -typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t; -typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t; -typedef snd_pcm_stream_t ma_snd_pcm_stream_t; -typedef snd_pcm_format_t ma_snd_pcm_format_t; -typedef snd_pcm_access_t ma_snd_pcm_access_t; -typedef snd_pcm_t ma_snd_pcm_t; -typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; -typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; -typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; -typedef snd_pcm_info_t ma_snd_pcm_info_t; -typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; -typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; -typedef snd_pcm_state_t ma_snd_pcm_state_t; - -/* snd_pcm_stream_t */ -#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK -#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE - -/* snd_pcm_format_t */ -#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN -#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8 -#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE -#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE -#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE -#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE -#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE -#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE -#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE -#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE -#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE -#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE -#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW -#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW -#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE -#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE - -/* ma_snd_pcm_access_t */ -#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED -#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED -#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX -#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED -#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED - -/* Channel positions. */ -#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN -#define MA_SND_CHMAP_NA SND_CHMAP_NA -#define MA_SND_CHMAP_MONO SND_CHMAP_MONO -#define MA_SND_CHMAP_FL SND_CHMAP_FL -#define MA_SND_CHMAP_FR SND_CHMAP_FR -#define MA_SND_CHMAP_RL SND_CHMAP_RL -#define MA_SND_CHMAP_RR SND_CHMAP_RR -#define MA_SND_CHMAP_FC SND_CHMAP_FC -#define MA_SND_CHMAP_LFE SND_CHMAP_LFE -#define MA_SND_CHMAP_SL SND_CHMAP_SL -#define MA_SND_CHMAP_SR SND_CHMAP_SR -#define MA_SND_CHMAP_RC SND_CHMAP_RC -#define MA_SND_CHMAP_FLC SND_CHMAP_FLC -#define MA_SND_CHMAP_FRC SND_CHMAP_FRC -#define MA_SND_CHMAP_RLC SND_CHMAP_RLC -#define MA_SND_CHMAP_RRC SND_CHMAP_RRC -#define MA_SND_CHMAP_FLW SND_CHMAP_FLW -#define MA_SND_CHMAP_FRW SND_CHMAP_FRW -#define MA_SND_CHMAP_FLH SND_CHMAP_FLH -#define MA_SND_CHMAP_FCH SND_CHMAP_FCH -#define MA_SND_CHMAP_FRH SND_CHMAP_FRH -#define MA_SND_CHMAP_TC SND_CHMAP_TC -#define MA_SND_CHMAP_TFL SND_CHMAP_TFL -#define MA_SND_CHMAP_TFR SND_CHMAP_TFR -#define MA_SND_CHMAP_TFC SND_CHMAP_TFC -#define MA_SND_CHMAP_TRL SND_CHMAP_TRL -#define MA_SND_CHMAP_TRR SND_CHMAP_TRR -#define MA_SND_CHMAP_TRC SND_CHMAP_TRC -#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC -#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC -#define MA_SND_CHMAP_TSL SND_CHMAP_TSL -#define MA_SND_CHMAP_TSR SND_CHMAP_TSR -#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE -#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE -#define MA_SND_CHMAP_BC SND_CHMAP_BC -#define MA_SND_CHMAP_BLC SND_CHMAP_BLC -#define MA_SND_CHMAP_BRC SND_CHMAP_BRC - -/* Open mode flags. */ -#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE -#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS -#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT -#else -#include /* For EPIPE, etc. */ -typedef unsigned long ma_snd_pcm_uframes_t; -typedef long ma_snd_pcm_sframes_t; -typedef int ma_snd_pcm_stream_t; -typedef int ma_snd_pcm_format_t; -typedef int ma_snd_pcm_access_t; -typedef int ma_snd_pcm_state_t; -typedef struct ma_snd_pcm_t ma_snd_pcm_t; -typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; -typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; -typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; -typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t; -typedef struct -{ - void* addr; - unsigned int first; - unsigned int step; -} ma_snd_pcm_channel_area_t; -typedef struct -{ - unsigned int channels; - unsigned int pos[1]; -} ma_snd_pcm_chmap_t; - -/* snd_pcm_state_t */ -#define MA_SND_PCM_STATE_OPEN 0 -#define MA_SND_PCM_STATE_SETUP 1 -#define MA_SND_PCM_STATE_PREPARED 2 -#define MA_SND_PCM_STATE_RUNNING 3 -#define MA_SND_PCM_STATE_XRUN 4 -#define MA_SND_PCM_STATE_DRAINING 5 -#define MA_SND_PCM_STATE_PAUSED 6 -#define MA_SND_PCM_STATE_SUSPENDED 7 -#define MA_SND_PCM_STATE_DISCONNECTED 8 - -/* snd_pcm_stream_t */ -#define MA_SND_PCM_STREAM_PLAYBACK 0 -#define MA_SND_PCM_STREAM_CAPTURE 1 - -/* snd_pcm_format_t */ -#define MA_SND_PCM_FORMAT_UNKNOWN -1 -#define MA_SND_PCM_FORMAT_U8 1 -#define MA_SND_PCM_FORMAT_S16_LE 2 -#define MA_SND_PCM_FORMAT_S16_BE 3 -#define MA_SND_PCM_FORMAT_S24_LE 6 -#define MA_SND_PCM_FORMAT_S24_BE 7 -#define MA_SND_PCM_FORMAT_S32_LE 10 -#define MA_SND_PCM_FORMAT_S32_BE 11 -#define MA_SND_PCM_FORMAT_FLOAT_LE 14 -#define MA_SND_PCM_FORMAT_FLOAT_BE 15 -#define MA_SND_PCM_FORMAT_FLOAT64_LE 16 -#define MA_SND_PCM_FORMAT_FLOAT64_BE 17 -#define MA_SND_PCM_FORMAT_MU_LAW 20 -#define MA_SND_PCM_FORMAT_A_LAW 21 -#define MA_SND_PCM_FORMAT_S24_3LE 32 -#define MA_SND_PCM_FORMAT_S24_3BE 33 - -/* snd_pcm_access_t */ -#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0 -#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1 -#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2 -#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3 -#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4 - -/* Channel positions. */ -#define MA_SND_CHMAP_UNKNOWN 0 -#define MA_SND_CHMAP_NA 1 -#define MA_SND_CHMAP_MONO 2 -#define MA_SND_CHMAP_FL 3 -#define MA_SND_CHMAP_FR 4 -#define MA_SND_CHMAP_RL 5 -#define MA_SND_CHMAP_RR 6 -#define MA_SND_CHMAP_FC 7 -#define MA_SND_CHMAP_LFE 8 -#define MA_SND_CHMAP_SL 9 -#define MA_SND_CHMAP_SR 10 -#define MA_SND_CHMAP_RC 11 -#define MA_SND_CHMAP_FLC 12 -#define MA_SND_CHMAP_FRC 13 -#define MA_SND_CHMAP_RLC 14 -#define MA_SND_CHMAP_RRC 15 -#define MA_SND_CHMAP_FLW 16 -#define MA_SND_CHMAP_FRW 17 -#define MA_SND_CHMAP_FLH 18 -#define MA_SND_CHMAP_FCH 19 -#define MA_SND_CHMAP_FRH 20 -#define MA_SND_CHMAP_TC 21 -#define MA_SND_CHMAP_TFL 22 -#define MA_SND_CHMAP_TFR 23 -#define MA_SND_CHMAP_TFC 24 -#define MA_SND_CHMAP_TRL 25 -#define MA_SND_CHMAP_TRR 26 -#define MA_SND_CHMAP_TRC 27 -#define MA_SND_CHMAP_TFLC 28 -#define MA_SND_CHMAP_TFRC 29 -#define MA_SND_CHMAP_TSL 30 -#define MA_SND_CHMAP_TSR 31 -#define MA_SND_CHMAP_LLFE 32 -#define MA_SND_CHMAP_RLFE 33 -#define MA_SND_CHMAP_BC 34 -#define MA_SND_CHMAP_BLC 35 -#define MA_SND_CHMAP_BRC 36 - -/* Open mode flags. */ -#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000 -#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000 -#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000 -#endif - -typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode); -typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm); -typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void); -typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); -typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); -typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); -typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask); -typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum); -typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); -typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); -typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); -typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); -typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); -typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access); -typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); -typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); -typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); -typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void); -typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); -typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val); -typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); -typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void); -typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val); -typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm); -typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); -typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id); -typedef int (* ma_snd_card_get_index_proc) (const char *name); -typedef int (* ma_snd_device_name_free_hint_proc) (void **hints); -typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames); -typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout); -typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock); -typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info); -typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void); -typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info); -typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); -typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -typedef int (* ma_snd_config_update_free_global_proc) (void); - -/* This array specifies each of the common devices that can be used for both playback and capture. */ -static const char* g_maCommonDeviceNamesALSA[] = { - "default", - "null", - "pulse", - "jack" -}; - -/* This array allows us to blacklist specific playback devices. */ -static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = { - "" -}; - -/* This array allows us to blacklist specific capture devices. */ -static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = { - "" -}; - - -static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format) -{ - ma_snd_pcm_format_t ALSAFormats[] = { - MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */ - MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */ - MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */ - MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */ - MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */ - MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */ - }; - - if (ma_is_big_endian()) { - ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN; - ALSAFormats[1] = MA_SND_PCM_FORMAT_U8; - ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE; - ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE; - ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE; - ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE; - } - - return ALSAFormats[format]; -} - -static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA) -{ - if (ma_is_little_endian()) { - switch (formatALSA) { - case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16; - case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24; - case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32; - case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32; - default: break; - } - } else { - switch (formatALSA) { - case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16; - case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24; - case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32; - case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32; - default: break; - } - } - - /* Endian agnostic. */ - switch (formatALSA) { - case MA_SND_PCM_FORMAT_U8: return ma_format_u8; - default: return ma_format_unknown; - } -} - -static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos) -{ - switch (alsaChannelPos) - { - case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO; - case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT; - case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT; - case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT; - case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT; - case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER; - case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE; - case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT; - case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT; - case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER; - case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER; - case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case MA_SND_CHMAP_RLC: return 0; - case MA_SND_CHMAP_RRC: return 0; - case MA_SND_CHMAP_FLW: return 0; - case MA_SND_CHMAP_FRW: return 0; - case MA_SND_CHMAP_FLH: return 0; - case MA_SND_CHMAP_FCH: return 0; - case MA_SND_CHMAP_FRH: return 0; - case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER; - case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT; - case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT; - case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER; - case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT; - case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT; - case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER; - default: break; - } - - return 0; -} - -static ma_bool32 ma_is_common_device_name__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - -static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - -static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name) -{ - if (deviceType == ma_device_type_playback) { - return ma_is_playback_device_blacklisted__alsa(name); - } else { - return ma_is_capture_device_blacklisted__alsa(name); - } -} - - -static const char* ma_find_char(const char* str, char c, int* index) -{ - int i = 0; - for (;;) { - if (str[i] == '\0') { - if (index) *index = -1; - return NULL; - } - - if (str[i] == c) { - if (index) *index = i; - return str + i; - } - - i += 1; - } - - /* Should never get here, but treat it as though the character was not found to make me feel better inside. */ - if (index) *index = -1; - return NULL; -} - -static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid) -{ - /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */ - - int commaPos; - const char* dev; - int i; - - if (hwid == NULL) { - return MA_FALSE; - } - - if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') { - return MA_FALSE; - } - - hwid += 3; - - dev = ma_find_char(hwid, ',', &commaPos); - if (dev == NULL) { - return MA_FALSE; - } else { - dev += 1; /* Skip past the ",". */ - } - - /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */ - for (i = 0; i < commaPos; ++i) { - if (hwid[i] < '0' || hwid[i] > '9') { - return MA_FALSE; - } - } - - /* Check if everything after the "," is numeric. If not, return false. */ - i = 0; - while (dev[i] != '\0') { - if (dev[i] < '0' || dev[i] > '9') { - return MA_FALSE; - } - i += 1; - } - - return MA_TRUE; -} - -static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ -{ - /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */ - - int colonPos; - int commaPos; - char card[256]; - const char* dev; - int cardIndex; - - if (dst == NULL) { - return -1; - } - if (dstSize < 7) { - return -1; /* Absolute minimum size of the output buffer is 7 bytes. */ - } - - *dst = '\0'; /* Safety. */ - if (src == NULL) { - return -1; - } - - /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */ - if (ma_is_device_name_in_hw_format__alsa(src)) { - return ma_strcpy_s(dst, dstSize, src); - } - - src = ma_find_char(src, ':', &colonPos); - if (src == NULL) { - return -1; /* Couldn't find a colon */ - } - - dev = ma_find_char(src, ',', &commaPos); - if (dev == NULL) { - dev = "0"; - ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */ - } else { - dev = dev + 5; /* +5 = ",DEV=" */ - ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */ - } - - cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); - if (cardIndex < 0) { - return -2; /* Failed to retrieve the card index. */ - } - - - /* Construction. */ - dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':'; - if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) { - return -3; - } - if (ma_strcat_s(dst, dstSize, ",") != 0) { - return -3; - } - if (ma_strcat_s(dst, dstSize, dev) != 0) { - return -3; - } - - return 0; -} - -static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID) -{ - ma_uint32 i; - - MA_ASSERT(pHWID != NULL); - - for (i = 0; i < count; ++i) { - if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) -{ - ma_snd_pcm_t* pPCM; - ma_snd_pcm_stream_t stream; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppPCM != NULL); - - *ppPCM = NULL; - pPCM = NULL; - - stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE; - - if (pDeviceID == NULL) { - ma_bool32 isDeviceOpen; - size_t i; - - /* - We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes - me feel better to try as hard as we can get to get _something_ working. - */ - const char* defaultDeviceNames[] = { - "default", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - - if (shareMode == ma_share_mode_exclusive) { - defaultDeviceNames[1] = "hw"; - defaultDeviceNames[2] = "hw:0"; - defaultDeviceNames[3] = "hw:0,0"; - } else { - if (deviceType == ma_device_type_playback) { - defaultDeviceNames[1] = "dmix"; - defaultDeviceNames[2] = "dmix:0"; - defaultDeviceNames[3] = "dmix:0,0"; - } else { - defaultDeviceNames[1] = "dsnoop"; - defaultDeviceNames[2] = "dsnoop:0"; - defaultDeviceNames[3] = "dsnoop:0,0"; - } - defaultDeviceNames[4] = "hw"; - defaultDeviceNames[5] = "hw:0"; - defaultDeviceNames[6] = "hw:0,0"; - } - - isDeviceOpen = MA_FALSE; - for (i = 0; i < ma_countof(defaultDeviceNames); ++i) { - if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { - if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { - isDeviceOpen = MA_TRUE; - break; - } - } - } - - if (!isDeviceOpen) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } else { - /* - We're trying to open a specific device. There's a few things to consider here: - - miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When - an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it - finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). - */ - - /* May end up needing to make small adjustments to the ID, so make a copy. */ - ma_device_id deviceID = *pDeviceID; - int resultALSA = -ENODEV; - - if (deviceID.alsa[0] != ':') { - /* The ID is not in ":0,0" format. Use the ID exactly as-is. */ - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode); - } else { - char hwid[256]; - - /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */ - if (deviceID.alsa[1] == '\0') { - deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */ - } - - if (shareMode == ma_share_mode_shared) { - if (deviceType == ma_device_type_playback) { - ma_strcpy_s(hwid, sizeof(hwid), "dmix"); - } else { - ma_strcpy_s(hwid, sizeof(hwid), "dsnoop"); - } - - if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); - } - } - - /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */ - if (resultALSA != 0) { - ma_strcpy_s(hwid, sizeof(hwid), "hw"); - if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); - } - } - } - - if (resultALSA < 0) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - *ppPCM = pPCM; - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - int resultALSA; - ma_bool32 cbResult = MA_TRUE; - char** ppDeviceHints; - ma_device_id* pUniqueIDs = NULL; - ma_uint32 uniqueIDCount = 0; - char** ppNextDeviceHint; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock); - - resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints); - if (resultALSA < 0) { - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); - return ma_result_from_errno(-resultALSA); - } - - ppNextDeviceHint = ppDeviceHints; - while (*ppNextDeviceHint != NULL) { - char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); - char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); - char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); - ma_device_type deviceType = ma_device_type_playback; - ma_bool32 stopEnumeration = MA_FALSE; - char hwid[sizeof(pUniqueIDs->alsa)]; - ma_device_info deviceInfo; - - if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) { - deviceType = ma_device_type_playback; - } - if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) { - deviceType = ma_device_type_capture; - } - - if (NAME != NULL) { - if (pContext->alsa.useVerboseDeviceEnumeration) { - /* Verbose mode. Use the name exactly as-is. */ - ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } else { - /* Simplified mode. Use ":%d,%d" format. */ - if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { - /* - At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the - plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device - initialization time and is used as an indicator to try and use the most appropriate plugin depending on the - device type and sharing mode. - */ - char* dst = hwid; - char* src = hwid+2; - while ((*dst++ = *src++)); - } else { - /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */ - ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } - - if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { - goto next_device; /* The device has already been enumerated. Move on to the next one. */ - } else { - /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ - size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); - ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); - if (pNewUniqueIDs == NULL) { - goto next_device; /* Failed to allocate memory. */ - } - - pUniqueIDs = pNewUniqueIDs; - MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); - uniqueIDCount += 1; - } - } - } else { - MA_ZERO_MEMORY(hwid, sizeof(hwid)); - } - - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1); - - /* - There's no good way to determine whether or not a device is the default on Linux. We're just going to do something simple and - just use the name of "default" as the indicator. - */ - if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - - /* - DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose - device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish - between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the - description. - - The value in DESC seems to be split into two lines, with the first line being the name of the device and the - second line being a description of the device. I don't like having the description be across two lines because - it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line - being put into parentheses. In simplified mode I'm just stripping the second line entirely. - */ - if (DESC != NULL) { - int lfPos; - const char* line2 = ma_find_char(DESC, '\n', &lfPos); - if (line2 != NULL) { - line2 += 1; /* Skip past the new-line character. */ - - if (pContext->alsa.useVerboseDeviceEnumeration) { - /* Verbose mode. Put the second line in brackets. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")"); - } else { - /* Simplified mode. Strip the second line entirely. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); - } - } else { - /* There's no second line. Just copy the whole description. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1); - } - } - - if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) { - cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); - } - - /* - Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback - again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which - means both Input and Output. - */ - if (cbResult) { - if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) { - if (deviceType == ma_device_type_playback) { - if (!ma_is_capture_device_blacklisted__alsa(NAME)) { - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } else { - if (!ma_is_playback_device_blacklisted__alsa(NAME)) { - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - } - } - } - - if (cbResult == MA_FALSE) { - stopEnumeration = MA_TRUE; - } - - next_device: - free(NAME); - free(DESC); - free(IOID); - ppNextDeviceHint += 1; - - /* We need to stop enumeration if the callback returned false. */ - if (stopEnumeration) { - break; - } - } - - ma_free(pUniqueIDs, &pContext->allocationCallbacks); - ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); - - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_device_type deviceType; - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_device_info* pDeviceInfo; - ma_bool32 foundDevice; -} ma_context_get_device_info_enum_callback_data__alsa; - -static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData) -{ - ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData; - MA_ASSERT(pData != NULL); - - (void)pContext; - - if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); - pData->foundDevice = MA_TRUE; - } else { - if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); - pData->foundDevice = MA_TRUE; - } - } - - /* Keep enumerating until we have found the device. */ - return !pData->foundDevice; -} - -static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pPCM != NULL); - MA_ASSERT(pHWParams != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; - } -} - -static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - ma_uint32 iSampleRate; - unsigned int minSampleRate; - unsigned int maxSampleRate; - int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */ - - /* There could be a range. */ - ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); - ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); - - /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */ - minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); - maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); - - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; - - if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); - } - } - - /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */ - if (!ma_is_standard_sample_rate(minSampleRate)) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); - } - - if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); - } -} - -static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_context_get_device_info_enum_callback_data__alsa data; - ma_result result; - int resultALSA; - ma_snd_pcm_t* pPCM; - ma_snd_pcm_hw_params_t* pHWParams; - ma_uint32 iFormat; - ma_uint32 iChannel; - - MA_ASSERT(pContext != NULL); - - /* We just enumerate to find basic information about the device. */ - data.deviceType = deviceType; - data.pDeviceID = pDeviceID; - data.pDeviceInfo = pDeviceInfo; - data.foundDevice = MA_FALSE; - result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data); - if (result != MA_SUCCESS) { - return result; - } - - if (!data.foundDevice) { - return MA_NO_DEVICE; - } - - if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { - pDeviceInfo->isDefault = MA_TRUE; - } - - /* For detailed info we need to open the device. */ - result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); - if (result != MA_SUCCESS) { - return result; - } - - /* We need to initialize a HW parameters object in order to know what formats are supported. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); - if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); - return ma_result_from_errno(-resultALSA); - } - - /* - Some ALSA devices can support many permutations of formats, channels and rates. We only support - a fixed number of permutations which means we need to employ some strategies to ensure the best - combinations are returned. An example is the "pulse" device which can do it's own data conversion - in software and as a result can support any combination of format, channels and rate. - - We want to ensure the the first data formats are the best. We have a list of favored sample - formats and sample rates, so these will be the basis of our iteration. - */ - - /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */ - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { - ma_format format = g_maFormatPriorities[iFormat]; - - /* - For each format we need to make sure we reset the configuration space so we don't return - channel counts and rates that aren't compatible with a format. - */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - - /* Test the format first. If this fails it means the format is not supported and we can skip it. */ - if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { - /* The format is supported. */ - unsigned int minChannels; - unsigned int maxChannels; - - /* - The configuration space needs to be restricted to this format so we can get an accurate - picture of which sample rates and channel counts are support with this format. - */ - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - - /* Now we need to check for supported channels. */ - ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels); - ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels); - - if (minChannels > MA_MAX_CHANNELS) { - continue; /* Too many channels. */ - } - if (maxChannels < MA_MIN_CHANNELS) { - continue; /* Not enough channels. */ - } - - /* - Make sure the channel count is clamped. This is mainly intended for the max channels - because some devices can report an unbound maximum. - */ - minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ - } else { - /* The device only supports a specific set of channels. We need to iterate over all of them. */ - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - /* Test the channel before applying it to the configuration space. */ - unsigned int channels = iChannel; - - /* Make sure our channel range is reset before testing again or else we'll always fail the test. */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - - if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) { - /* The channel count is supported. */ - - /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */ - ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels); - - /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo); - } else { - /* The channel count is not supported. Skip. */ - } - } - } - } else { - /* The format is not supported. Skip. */ - } - } - - ma_free(pHWParams, &pContext->allocationCallbacks); - - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__alsa(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - close(pDevice->alsa.wakeupfdCapture); - ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks); - } - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - close(pDevice->alsa.wakeupfdPlayback); - ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - ma_result result; - int resultALSA; - ma_snd_pcm_t* pPCM; - ma_bool32 isUsingMMap; - ma_snd_pcm_format_t formatALSA; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - int openMode; - ma_snd_pcm_hw_params_t* pHWParams; - ma_snd_pcm_sw_params_t* pSWParams; - ma_snd_pcm_uframes_t bufferBoundary; - int pollDescriptorCount; - struct pollfd* pPollDescriptors; - int wakeupfd; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ - MA_ASSERT(pDevice != NULL); - - formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format); - - openMode = 0; - if (pConfig->alsa.noAutoResample) { - openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE; - } - if (pConfig->alsa.noAutoChannels) { - openMode |= MA_SND_PCM_NO_AUTO_CHANNELS; - } - if (pConfig->alsa.noAutoFormat) { - openMode |= MA_SND_PCM_NO_AUTO_FORMAT; - } - - result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); - if (result != MA_SUCCESS) { - return result; - } - - - /* Hardware parameters. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); - if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); - return ma_result_from_errno(-resultALSA); - } - - /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */ - isUsingMMap = MA_FALSE; -#if 0 /* NOTE: MMAP mode temporarily disabled. */ - if (deviceType != ma_device_type_capture) { /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */ - if (!pConfig->alsa.noMMap && ma_device__is_async(pDevice)) { - if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { - pDevice->alsa.isUsingMMap = MA_TRUE; - } - } - } -#endif - - if (!isUsingMMap) { - resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - /* - Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't - find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS. - */ - - /* Format. */ - { - /* - At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is - supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. - */ - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) { - /* We're either requesting the native format or the specified format is not supported. */ - size_t iFormat; - - formatALSA = MA_SND_PCM_FORMAT_UNKNOWN; - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { - formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]); - break; - } - } - - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); - return MA_FORMAT_NOT_SUPPORTED; - } - } - - resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalFormat = ma_format_from_alsa(formatALSA); - if (internalFormat == ma_format_unknown) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); - return MA_FORMAT_NOT_SUPPORTED; - } - } - - /* Channels. */ - { - unsigned int channels = pDescriptor->channels; - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - - resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalChannels = (ma_uint32)channels; - } - - /* Sample Rate */ - { - unsigned int sampleRate; - - /* - It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes - problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable - resampling. - - To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a - sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling - doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly - faster rate. - - miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine - for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very - good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion. - - I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce - this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. - */ - ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0); - - sampleRate = pDescriptor->sampleRate; - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalSampleRate = (ma_uint32)sampleRate; - } - - /* Periods. */ - { - ma_uint32 periods = pDescriptor->periodCount; - - resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalPeriods = periods; - } - - /* Buffer Size */ - { - ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods; - - resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods; - } - - /* Apply hardware parameters. */ - resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); - return ma_result_from_errno(-resultALSA); - } - - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - pHWParams = NULL; - - - /* Software parameters. */ - pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); - if (pSWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); - if (resultALSA < 0) { - bufferBoundary = internalPeriodSizeInFrames * internalPeriods; - } - - if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */ - /* - Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to - the size of a period. But for full-duplex we need to set it such that it is at least two periods. - */ - resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); - if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); - return ma_result_from_errno(-resultALSA); - } - - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - pSWParams = NULL; - - - /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ - { - ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); - if (pChmap != NULL) { - ma_uint32 iChannel; - - /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */ - if (pChmap->channels >= internalChannels) { - /* Drop excess channels. */ - for (iChannel = 0; iChannel < internalChannels; ++iChannel) { - internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); - } - } else { - ma_uint32 i; - - /* - Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate - channels. If validation fails, fall back to defaults. - */ - ma_bool32 isValid = MA_TRUE; - - /* Fill with defaults. */ - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - - /* Overwrite first pChmap->channels channels. */ - for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) { - internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); - } - - /* Validate. */ - for (i = 0; i < internalChannels && isValid; ++i) { - ma_uint32 j; - for (j = i+1; j < internalChannels; ++j) { - if (internalChannelMap[i] == internalChannelMap[j]) { - isValid = MA_FALSE; - break; - } - } - } - - /* If our channel map is invalid, fall back to defaults. */ - if (!isValid) { - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - } - } - - free(pChmap); - pChmap = NULL; - } else { - /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */ - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - } - } - - - /* - We need to retrieve the poll descriptors so we can use poll() to wait for data to become - available for reading or writing. There's no well defined maximum for this so we're just going - to allocate this on the heap. - */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); - if (pollDescriptorCount <= 0) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); - return MA_ERROR; - } - - pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ - if (pPollDescriptors == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); - return MA_OUT_OF_MEMORY; - } - - /* - We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver - never returns from writei() and readi(). This has been observed with the "pulse" device. - */ - wakeupfd = eventfd(0, 0); - if (wakeupfd < 0) { - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); - return ma_result_from_errno(errno); - } - - /* We'll place the wakeup fd at the start of the buffer. */ - pPollDescriptors[0].fd = wakeupfd; - pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */ - pPollDescriptors[0].revents = 0; - - /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ - if (pollDescriptorCount <= 0) { - close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); - return MA_ERROR; - } - - if (deviceType == ma_device_type_capture) { - pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors; - pDevice->alsa.wakeupfdCapture = wakeupfd; - } else { - pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors; - pDevice->alsa.wakeupfdPlayback = wakeupfd; - } - - - /* We're done. Prepare the device. */ - resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); - if (resultALSA < 0) { - close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); - return ma_result_from_errno(-resultALSA); - } - - - if (deviceType == ma_device_type_capture) { - pDevice->alsa.pPCMCapture = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapCapture = isUsingMMap; - } else { - pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapPlayback = isUsingMMap; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS)); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->alsa); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__alsa(ma_device* pDevice) -{ - int resultALSA; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); - return ma_result_from_errno(-resultALSA); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__alsa(ma_device* pDevice) -{ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); - - /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); - - /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) -{ - for (;;) { - unsigned short revents; - int resultALSA; - int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); - if (resultPoll < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed."); - return ma_result_from_errno(errno); - } - - /* - Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor - has had it's POLLIN flag set. If so, we need to actually read the data and then exit - function. The wakeup descriptor will be the first item in the descriptors buffer. - */ - if ((pPollDescriptors[0].revents & POLLIN) != 0) { - ma_uint64 t; - int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ - if (resultRead < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed."); - return ma_result_from_errno(errno); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n"); - return MA_DEVICE_NOT_STARTED; - } - - /* - Getting here means that some data should be able to be read. We need to use ALSA to - translate the revents flags for us. - */ - resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed."); - return ma_result_from_errno(-resultALSA); - } - - if ((revents & POLLERR) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected."); - return ma_result_from_errno(errno); - } - - if ((revents & requiredEvent) == requiredEvent) { - break; /* We're done. Data available for reading or writing. */ - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_wait_read__alsa(ma_device* pDevice) -{ - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ -} - -static ma_result ma_device_wait_write__alsa(ma_device* pDevice) -{ - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ -} - -static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_result result; - - /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ - result = ma_device_wait_read__alsa(pDevice); - if (result != MA_SUCCESS) { - return result; - } - - /* Getting here means we should have data available. */ - resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); - if (resultALSA >= 0) { - break; /* Success. */ - } else { - if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/ - continue; /* Try again. */ - } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); - - /* Overrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); - return ma_result_from_errno((int)-resultALSA); - } - - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - continue; /* Try reading again. */ - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = resultALSA; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrames != NULL); - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_result result; - - /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ - result = ma_device_wait_write__alsa(pDevice); - if (result != MA_SUCCESS) { - return result; - } - - resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); - if (resultALSA >= 0) { - break; /* Success. */ - } else { - if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/ - continue; /* Try again. */ - } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); - - /* Underrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - /* - In my testing I have had a situation where writei() does not automatically restart the device even though I've set it - up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of - frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure - if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't - quite right here. - */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - continue; /* Try writing again. */ - } - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = resultALSA; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) -{ - ma_uint64 t = 1; - int resultWrite = 0; - - MA_ASSERT(pDevice != NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); - - /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ - if (pDevice->alsa.pPollDescriptorsCapture != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); - } - if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); - } - - if (resultWrite < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); - return ma_result_from_errno(errno); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__alsa(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_alsa); - - /* Clean up memory for memory leak checkers. */ - ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->alsa.asoundSO); -#endif - - ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libasoundNames[] = { - "libasound.so.2", - "libasound.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libasoundNames); ++i) { - pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]); - if (pContext->alsa.asoundSO != NULL) { - break; - } - } - - if (pContext->alsa.asoundSO == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); - return MA_NO_BACKEND; - } - - pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); - pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); - pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state"); - pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain"); - pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_reset"); - pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_nonblock"); - pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); - pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global"); -#else - /* The system below is just for type safety. */ - ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; - ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; - ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; - ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; - ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; - ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; - ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; - ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; - ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; - ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; - ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; - ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; - ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; - ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; - ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; - ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; - ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; - ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; - ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; - ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; - ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; - ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; - ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; - ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; - ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; - ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; - ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; - ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; - ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; - ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; - ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; - ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; - ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; - ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; - ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; - ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; - ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; - ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; - ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; - ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; - ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; - ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; - ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; - ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; - ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; - ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; - ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; - ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; - ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; - ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; - ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; - ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; - ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; - ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; - ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; - ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; - ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; - ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; - ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; - ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; - ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; - ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; - ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; - ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; - ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; - ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; - - pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; - pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close; - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof; - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any; - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format; - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first; - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask; - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels; - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near; - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax; - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format; - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels; - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min; - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max; - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate; - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min; - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max; - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size; - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods; - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access; - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format; - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels; - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate; - pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params; - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof; - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current; - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary; - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min; - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold; - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold; - pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params; - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof; - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test; - pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap; - pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state; - pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare; - pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; - pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; - pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; - pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset; - pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; - pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; - pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; - pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint; - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin; - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit; - pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover; - pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi; - pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei; - pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; - pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; - pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; - pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock; - pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; - pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors; - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count; - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents; - pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; -#endif - - pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; - - result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__alsa; - pCallbacks->onContextUninit = ma_context_uninit__alsa; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa; - pCallbacks->onDeviceInit = ma_device_init__alsa; - pCallbacks->onDeviceUninit = ma_device_uninit__alsa; - pCallbacks->onDeviceStart = ma_device_start__alsa; - pCallbacks->onDeviceStop = ma_device_stop__alsa; - pCallbacks->onDeviceRead = ma_device_read__alsa; - pCallbacks->onDeviceWrite = ma_device_write__alsa; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa; - - return MA_SUCCESS; -} -#endif /* ALSA */ - - - -/****************************************************************************** - -PulseAudio Backend - -******************************************************************************/ -#ifdef MA_HAS_PULSEAUDIO -/* -The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on -in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. - -PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it -allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it -appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or -write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the -simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient -when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API. - -Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to -get fun, and I don't mean that in a good way... - -The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands -don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is -enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost -all of PulseAudio's problems stem from. - -When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own -vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called -pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop -because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use -it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed. - -To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer -to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded -main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely -specialized such as if you want to integrate it into your application's existing main loop infrastructure. - -(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262. -It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.) - -Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to -miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's -one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which -is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if -you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()` -has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can -set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop. -All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected. -This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before -attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`. - -The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an -internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the -host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind. - -Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device. -The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call -`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get -information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object -is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to -run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the -context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up. -All of that just to retrieve basic information about a device! - -Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the -context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design -choices in PulseAudio. - -PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here -because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for -writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can -set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices -straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, -PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation) -because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback -would be where a program will want to write or read data to or from the stream, but when it's called before the application has even -requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at -that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the -stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data -callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio -doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been -started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data -callback is not fired. - -This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will -continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device -is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in -PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call -`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always -writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if -you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to -*not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining -important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained -before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write -data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again! - -This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not* -write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just -resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This -disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the -callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.) - -Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context, -only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as -"corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think -it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you -guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is -absolutely beyond me. Would it really be that hard to just make it run synchronously? - -Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that -they were initialized in. - -That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're -embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to -run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche -requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is -constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a -parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These -changes alone will change PulseAudio from one of the worst audio APIs to one of the best. -*/ - - -/* -It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header -to check for type safety. We cannot do this when linking at run time because the header might not be available. -*/ -#ifdef MA_NO_RUNTIME_LINKING - -/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ -#if !defined(__cplusplus) - #if defined(__STRICT_ANSI__) - #if !defined(inline) - #define inline __inline__ __attribute__((always_inline)) - #define MA_INLINE_DEFINED - #endif - #endif -#endif -#include -#if defined(MA_INLINE_DEFINED) - #undef inline - #undef MA_INLINE_DEFINED -#endif - -#define MA_PA_OK PA_OK -#define MA_PA_ERR_ACCESS PA_ERR_ACCESS -#define MA_PA_ERR_INVALID PA_ERR_INVALID -#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY -#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED - -#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX -#define MA_PA_RATE_MAX PA_RATE_MAX - -typedef pa_context_flags_t ma_pa_context_flags_t; -#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS -#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN -#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL - -typedef pa_stream_flags_t ma_pa_stream_flags_t; -#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS -#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED -#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING -#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC -#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE -#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS -#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS -#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT -#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE -#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS -#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE -#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE -#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT -#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED -#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY -#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS -#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND -#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED -#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND -#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME -#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH - -typedef pa_sink_flags_t ma_pa_sink_flags_t; -#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS -#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL -#define MA_PA_SINK_LATENCY PA_SINK_LATENCY -#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE -#define MA_PA_SINK_NETWORK PA_SINK_NETWORK -#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL -#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME -#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME -#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY -#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS - -typedef pa_source_flags_t ma_pa_source_flags_t; -#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS -#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL -#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY -#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE -#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK -#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL -#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME -#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY -#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME - -typedef pa_context_state_t ma_pa_context_state_t; -#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED -#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING -#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING -#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME -#define MA_PA_CONTEXT_READY PA_CONTEXT_READY -#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED -#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED - -typedef pa_stream_state_t ma_pa_stream_state_t; -#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED -#define MA_PA_STREAM_CREATING PA_STREAM_CREATING -#define MA_PA_STREAM_READY PA_STREAM_READY -#define MA_PA_STREAM_FAILED PA_STREAM_FAILED -#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED - -typedef pa_operation_state_t ma_pa_operation_state_t; -#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING -#define MA_PA_OPERATION_DONE PA_OPERATION_DONE -#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED - -typedef pa_sink_state_t ma_pa_sink_state_t; -#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE -#define MA_PA_SINK_RUNNING PA_SINK_RUNNING -#define MA_PA_SINK_IDLE PA_SINK_IDLE -#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED - -typedef pa_source_state_t ma_pa_source_state_t; -#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE -#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING -#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE -#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED - -typedef pa_seek_mode_t ma_pa_seek_mode_t; -#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE -#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE -#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ -#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END - -typedef pa_channel_position_t ma_pa_channel_position_t; -#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID -#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER -#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT -#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT -#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER -#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT -#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT -#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0 -#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1 -#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2 -#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3 -#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4 -#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5 -#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6 -#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7 -#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8 -#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9 -#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10 -#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11 -#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12 -#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13 -#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14 -#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15 -#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16 -#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17 -#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18 -#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19 -#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20 -#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21 -#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22 -#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23 -#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24 -#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25 -#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26 -#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27 -#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28 -#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29 -#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30 -#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31 -#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT -#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT -#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER -#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT -#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT -#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER -#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER - -typedef pa_channel_map_def_t ma_pa_channel_map_def_t; -#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF -#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA -#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX -#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX -#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS -#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT - -typedef pa_sample_format_t ma_pa_sample_format_t; -#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID -#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8 -#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW -#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW -#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE -#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE -#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE -#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE -#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE -#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE -#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE -#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE -#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE -#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE - -typedef pa_mainloop ma_pa_mainloop; -typedef pa_threaded_mainloop ma_pa_threaded_mainloop; -typedef pa_mainloop_api ma_pa_mainloop_api; -typedef pa_context ma_pa_context; -typedef pa_operation ma_pa_operation; -typedef pa_stream ma_pa_stream; -typedef pa_spawn_api ma_pa_spawn_api; -typedef pa_buffer_attr ma_pa_buffer_attr; -typedef pa_channel_map ma_pa_channel_map; -typedef pa_cvolume ma_pa_cvolume; -typedef pa_sample_spec ma_pa_sample_spec; -typedef pa_sink_info ma_pa_sink_info; -typedef pa_source_info ma_pa_source_info; - -typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t; -typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t; -typedef pa_source_info_cb_t ma_pa_source_info_cb_t; -typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t; -typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t; -typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t; -typedef pa_free_cb_t ma_pa_free_cb_t; -#else -#define MA_PA_OK 0 -#define MA_PA_ERR_ACCESS 1 -#define MA_PA_ERR_INVALID 2 -#define MA_PA_ERR_NOENTITY 5 -#define MA_PA_ERR_NOTSUPPORTED 19 - -#define MA_PA_CHANNELS_MAX 32 -#define MA_PA_RATE_MAX 384000 - -typedef int ma_pa_context_flags_t; -#define MA_PA_CONTEXT_NOFLAGS 0x00000000 -#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001 -#define MA_PA_CONTEXT_NOFAIL 0x00000002 - -typedef int ma_pa_stream_flags_t; -#define MA_PA_STREAM_NOFLAGS 0x00000000 -#define MA_PA_STREAM_START_CORKED 0x00000001 -#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002 -#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004 -#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008 -#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010 -#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020 -#define MA_PA_STREAM_FIX_FORMAT 0x00000040 -#define MA_PA_STREAM_FIX_RATE 0x00000080 -#define MA_PA_STREAM_FIX_CHANNELS 0x00000100 -#define MA_PA_STREAM_DONT_MOVE 0x00000200 -#define MA_PA_STREAM_VARIABLE_RATE 0x00000400 -#define MA_PA_STREAM_PEAK_DETECT 0x00000800 -#define MA_PA_STREAM_START_MUTED 0x00001000 -#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000 -#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000 -#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000 -#define MA_PA_STREAM_START_UNMUTED 0x00010000 -#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000 -#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000 -#define MA_PA_STREAM_PASSTHROUGH 0x00080000 - -typedef int ma_pa_sink_flags_t; -#define MA_PA_SINK_NOFLAGS 0x00000000 -#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001 -#define MA_PA_SINK_LATENCY 0x00000002 -#define MA_PA_SINK_HARDWARE 0x00000004 -#define MA_PA_SINK_NETWORK 0x00000008 -#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010 -#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020 -#define MA_PA_SINK_FLAT_VOLUME 0x00000040 -#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080 -#define MA_PA_SINK_SET_FORMATS 0x00000100 - -typedef int ma_pa_source_flags_t; -#define MA_PA_SOURCE_NOFLAGS 0x00000000 -#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001 -#define MA_PA_SOURCE_LATENCY 0x00000002 -#define MA_PA_SOURCE_HARDWARE 0x00000004 -#define MA_PA_SOURCE_NETWORK 0x00000008 -#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010 -#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020 -#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040 -#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080 - -typedef int ma_pa_context_state_t; -#define MA_PA_CONTEXT_UNCONNECTED 0 -#define MA_PA_CONTEXT_CONNECTING 1 -#define MA_PA_CONTEXT_AUTHORIZING 2 -#define MA_PA_CONTEXT_SETTING_NAME 3 -#define MA_PA_CONTEXT_READY 4 -#define MA_PA_CONTEXT_FAILED 5 -#define MA_PA_CONTEXT_TERMINATED 6 - -typedef int ma_pa_stream_state_t; -#define MA_PA_STREAM_UNCONNECTED 0 -#define MA_PA_STREAM_CREATING 1 -#define MA_PA_STREAM_READY 2 -#define MA_PA_STREAM_FAILED 3 -#define MA_PA_STREAM_TERMINATED 4 - -typedef int ma_pa_operation_state_t; -#define MA_PA_OPERATION_RUNNING 0 -#define MA_PA_OPERATION_DONE 1 -#define MA_PA_OPERATION_CANCELLED 2 - -typedef int ma_pa_sink_state_t; -#define MA_PA_SINK_INVALID_STATE -1 -#define MA_PA_SINK_RUNNING 0 -#define MA_PA_SINK_IDLE 1 -#define MA_PA_SINK_SUSPENDED 2 - -typedef int ma_pa_source_state_t; -#define MA_PA_SOURCE_INVALID_STATE -1 -#define MA_PA_SOURCE_RUNNING 0 -#define MA_PA_SOURCE_IDLE 1 -#define MA_PA_SOURCE_SUSPENDED 2 - -typedef int ma_pa_seek_mode_t; -#define MA_PA_SEEK_RELATIVE 0 -#define MA_PA_SEEK_ABSOLUTE 1 -#define MA_PA_SEEK_RELATIVE_ON_READ 2 -#define MA_PA_SEEK_RELATIVE_END 3 - -typedef int ma_pa_channel_position_t; -#define MA_PA_CHANNEL_POSITION_INVALID -1 -#define MA_PA_CHANNEL_POSITION_MONO 0 -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1 -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2 -#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3 -#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4 -#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5 -#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6 -#define MA_PA_CHANNEL_POSITION_LFE 7 -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8 -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9 -#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10 -#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11 -#define MA_PA_CHANNEL_POSITION_AUX0 12 -#define MA_PA_CHANNEL_POSITION_AUX1 13 -#define MA_PA_CHANNEL_POSITION_AUX2 14 -#define MA_PA_CHANNEL_POSITION_AUX3 15 -#define MA_PA_CHANNEL_POSITION_AUX4 16 -#define MA_PA_CHANNEL_POSITION_AUX5 17 -#define MA_PA_CHANNEL_POSITION_AUX6 18 -#define MA_PA_CHANNEL_POSITION_AUX7 19 -#define MA_PA_CHANNEL_POSITION_AUX8 20 -#define MA_PA_CHANNEL_POSITION_AUX9 21 -#define MA_PA_CHANNEL_POSITION_AUX10 22 -#define MA_PA_CHANNEL_POSITION_AUX11 23 -#define MA_PA_CHANNEL_POSITION_AUX12 24 -#define MA_PA_CHANNEL_POSITION_AUX13 25 -#define MA_PA_CHANNEL_POSITION_AUX14 26 -#define MA_PA_CHANNEL_POSITION_AUX15 27 -#define MA_PA_CHANNEL_POSITION_AUX16 28 -#define MA_PA_CHANNEL_POSITION_AUX17 29 -#define MA_PA_CHANNEL_POSITION_AUX18 30 -#define MA_PA_CHANNEL_POSITION_AUX19 31 -#define MA_PA_CHANNEL_POSITION_AUX20 32 -#define MA_PA_CHANNEL_POSITION_AUX21 33 -#define MA_PA_CHANNEL_POSITION_AUX22 34 -#define MA_PA_CHANNEL_POSITION_AUX23 35 -#define MA_PA_CHANNEL_POSITION_AUX24 36 -#define MA_PA_CHANNEL_POSITION_AUX25 37 -#define MA_PA_CHANNEL_POSITION_AUX26 38 -#define MA_PA_CHANNEL_POSITION_AUX27 39 -#define MA_PA_CHANNEL_POSITION_AUX28 40 -#define MA_PA_CHANNEL_POSITION_AUX29 41 -#define MA_PA_CHANNEL_POSITION_AUX30 42 -#define MA_PA_CHANNEL_POSITION_AUX31 43 -#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50 -#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE - -typedef int ma_pa_channel_map_def_t; -#define MA_PA_CHANNEL_MAP_AIFF 0 -#define MA_PA_CHANNEL_MAP_ALSA 1 -#define MA_PA_CHANNEL_MAP_AUX 2 -#define MA_PA_CHANNEL_MAP_WAVEEX 3 -#define MA_PA_CHANNEL_MAP_OSS 4 -#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF - -typedef int ma_pa_sample_format_t; -#define MA_PA_SAMPLE_INVALID -1 -#define MA_PA_SAMPLE_U8 0 -#define MA_PA_SAMPLE_ALAW 1 -#define MA_PA_SAMPLE_ULAW 2 -#define MA_PA_SAMPLE_S16LE 3 -#define MA_PA_SAMPLE_S16BE 4 -#define MA_PA_SAMPLE_FLOAT32LE 5 -#define MA_PA_SAMPLE_FLOAT32BE 6 -#define MA_PA_SAMPLE_S32LE 7 -#define MA_PA_SAMPLE_S32BE 8 -#define MA_PA_SAMPLE_S24LE 9 -#define MA_PA_SAMPLE_S24BE 10 -#define MA_PA_SAMPLE_S24_32LE 11 -#define MA_PA_SAMPLE_S24_32BE 12 - -typedef struct ma_pa_mainloop ma_pa_mainloop; -typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop; -typedef struct ma_pa_mainloop_api ma_pa_mainloop_api; -typedef struct ma_pa_context ma_pa_context; -typedef struct ma_pa_operation ma_pa_operation; -typedef struct ma_pa_stream ma_pa_stream; -typedef struct ma_pa_spawn_api ma_pa_spawn_api; - -typedef struct -{ - ma_uint32 maxlength; - ma_uint32 tlength; - ma_uint32 prebuf; - ma_uint32 minreq; - ma_uint32 fragsize; -} ma_pa_buffer_attr; - -typedef struct -{ - ma_uint8 channels; - ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX]; -} ma_pa_channel_map; - -typedef struct -{ - ma_uint8 channels; - ma_uint32 values[MA_PA_CHANNELS_MAX]; -} ma_pa_cvolume; - -typedef struct -{ - ma_pa_sample_format_t format; - ma_uint32 rate; - ma_uint8 channels; -} ma_pa_sample_spec; - -typedef struct -{ - const char* name; - ma_uint32 index; - const char* description; - ma_pa_sample_spec sample_spec; - ma_pa_channel_map channel_map; - ma_uint32 owner_module; - ma_pa_cvolume volume; - int mute; - ma_uint32 monitor_source; - const char* monitor_source_name; - ma_uint64 latency; - const char* driver; - ma_pa_sink_flags_t flags; - void* proplist; - ma_uint64 configured_latency; - ma_uint32 base_volume; - ma_pa_sink_state_t state; - ma_uint32 n_volume_steps; - ma_uint32 card; - ma_uint32 n_ports; - void** ports; - void* active_port; - ma_uint8 n_formats; - void** formats; -} ma_pa_sink_info; - -typedef struct -{ - const char *name; - ma_uint32 index; - const char *description; - ma_pa_sample_spec sample_spec; - ma_pa_channel_map channel_map; - ma_uint32 owner_module; - ma_pa_cvolume volume; - int mute; - ma_uint32 monitor_of_sink; - const char *monitor_of_sink_name; - ma_uint64 latency; - const char *driver; - ma_pa_source_flags_t flags; - void* proplist; - ma_uint64 configured_latency; - ma_uint32 base_volume; - ma_pa_source_state_t state; - ma_uint32 n_volume_steps; - ma_uint32 card; - ma_uint32 n_ports; - void** ports; - void* active_port; - ma_uint8 n_formats; - void** formats; -} ma_pa_source_info; - -typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata); -typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata); -typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata); -typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata); -typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata); -typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata); -typedef void (* ma_pa_free_cb_t) (void* p); -#endif - - -typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void); -typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m); -typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval); -typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m); -typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval); -typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m); -typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void); -typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); -typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); -typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); -typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name); -typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c); -typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); -typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); -typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); -typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); -typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); -typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); -typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); -typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); -typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); -typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); -typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s); -typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); -typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); -typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); -typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); -typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); -typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); -typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); -typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); -typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); -typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); -typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); -typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); - -typedef struct -{ - ma_uint32 count; - ma_uint32 capacity; - ma_device_info* pInfo; -} ma_pulse_device_enum_data; - -static ma_result ma_result_from_pulse(int result) -{ - if (result < 0) { - return MA_ERROR; - } - - switch (result) { - case MA_PA_OK: return MA_SUCCESS; - case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED; - case MA_PA_ERR_INVALID: return MA_INVALID_ARGS; - case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE; - default: return MA_ERROR; - } -} - -#if 0 -static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) -{ - if (ma_is_little_endian()) { - switch (format) { - case ma_format_s16: return MA_PA_SAMPLE_S16LE; - case ma_format_s24: return MA_PA_SAMPLE_S24LE; - case ma_format_s32: return MA_PA_SAMPLE_S32LE; - case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE; - default: break; - } - } else { - switch (format) { - case ma_format_s16: return MA_PA_SAMPLE_S16BE; - case ma_format_s24: return MA_PA_SAMPLE_S24BE; - case ma_format_s32: return MA_PA_SAMPLE_S32BE; - case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE; - default: break; - } - } - - /* Endian agnostic. */ - switch (format) { - case ma_format_u8: return MA_PA_SAMPLE_U8; - default: return MA_PA_SAMPLE_INVALID; - } -} -#endif - -static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) -{ - if (ma_is_little_endian()) { - switch (format) { - case MA_PA_SAMPLE_S16LE: return ma_format_s16; - case MA_PA_SAMPLE_S24LE: return ma_format_s24; - case MA_PA_SAMPLE_S32LE: return ma_format_s32; - case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32; - default: break; - } - } else { - switch (format) { - case MA_PA_SAMPLE_S16BE: return ma_format_s16; - case MA_PA_SAMPLE_S24BE: return ma_format_s24; - case MA_PA_SAMPLE_S32BE: return ma_format_s32; - case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32; - default: break; - } - } - - /* Endian agnostic. */ - switch (format) { - case MA_PA_SAMPLE_U8: return ma_format_u8; - default: return ma_format_unknown; - } -} - -static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position) -{ - switch (position) - { - case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE; - case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO; - case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER; - case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT; - case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE; - case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0; - case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1; - case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2; - case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3; - case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4; - case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5; - case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6; - case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7; - case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8; - case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9; - case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10; - case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11; - case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12; - case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13; - case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14; - case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15; - case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16; - case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17; - case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18; - case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19; - case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20; - case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21; - case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22; - case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23; - case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24; - case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25; - case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26; - case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27; - case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28; - case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29; - case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30; - case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31; - case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - default: return MA_CHANNEL_NONE; - } -} - -#if 0 -static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position) -{ - switch (position) - { - case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID; - case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER; - case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE; - case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT; - case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER; - case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT; - case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18; - case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19; - case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20; - case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21; - case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22; - case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23; - case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24; - case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25; - case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26; - case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27; - case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28; - case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29; - case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30; - case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31; - default: return (ma_pa_channel_position_t)position; - } -} -#endif - -static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) -{ - int resultPA; - ma_pa_operation_state_t state; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pOP != NULL); - - for (;;) { - state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); - if (state != MA_PA_OPERATION_RUNNING) { - break; /* Done. */ - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) -{ - ma_result result; - - if (pOP == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - return result; -} - -static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) -{ - int resultPA; - ma_pa_context_state_t state; - - for (;;) { - state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); - if (state == MA_PA_CONTEXT_READY) { - break; /* Done. */ - } - - if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context."); - return MA_ERROR; - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - /* Should never get here. */ - return MA_SUCCESS; -} - -static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) -{ - int resultPA; - ma_pa_stream_state_t state; - - for (;;) { - state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); - if (state == MA_PA_STREAM_READY) { - break; /* Done. */ - } - - if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream."); - return MA_ERROR; - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) -{ - ma_result result; - ma_ptr pMainLoop; - ma_ptr pPulseContext; - - MA_ASSERT(ppMainLoop != NULL); - MA_ASSERT(ppPulseContext != NULL); - - /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ - pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); - if (pMainLoop == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); - return MA_FAILED_TO_INIT_BACKEND; - } - - pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); - if (pPulseContext == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return MA_FAILED_TO_INIT_BACKEND; - } - - /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ - result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return result; - } - - /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ - result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return result; - } - - *ppMainLoop = pMainLoop; - *ppPulseContext = pPulseContext; - - return MA_SUCCESS; -} - - -static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_pa_sink_info* pInfoOut; - - if (endOfList > 0) { - return; - } - - pInfoOut = (ma_pa_sink_info*)pUserData; - MA_ASSERT(pInfoOut != NULL); - - *pInfoOut = *pInfo; - - (void)pPulseContext; /* Unused. */ -} - -static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_pa_source_info* pInfoOut; - - if (endOfList > 0) { - return; - } - - pInfoOut = (ma_pa_source_info*)pUserData; - MA_ASSERT(pInfoOut != NULL); - - *pInfoOut = *pInfo; - - (void)pPulseContext; /* Unused. */ -} - -#if 0 -static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_device* pDevice; - - if (endOfList > 0) { - return; - } - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1); - - (void)pPulseContext; /* Unused. */ -} - -static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_device* pDevice; - - if (endOfList > 0) { - return; - } - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1); - - (void)pPulseContext; /* Unused. */ -} -#endif - -static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) -{ - ma_pa_operation* pOP; - - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); - if (pOP == NULL) { - return MA_ERROR; - } - - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); -} - -static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) -{ - ma_pa_operation* pOP; - - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); - if (pOP == NULL) { - return MA_ERROR; - } - - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); -} - -static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) -{ - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pIndex != NULL); - - if (pIndex != NULL) { - *pIndex = (ma_uint32)-1; - } - - if (deviceType == ma_device_type_playback) { - ma_pa_sink_info sinkInfo; - result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pIndex != NULL) { - *pIndex = sinkInfo.index; - } - } - - if (deviceType == ma_device_type_capture) { - ma_pa_source_info sourceInfo; - result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pIndex != NULL) { - *pIndex = sourceInfo.index; - } - } - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_context* pContext; - ma_enum_devices_callback_proc callback; - void* pUserData; - ma_bool32 isTerminated; - ma_uint32 defaultDeviceIndexPlayback; - ma_uint32 defaultDeviceIndexCapture; -} ma_context_enumerate_devices_callback_data__pulse; - -static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; - ma_device_info deviceInfo; - - MA_ASSERT(pData != NULL); - - if (endOfList || pData->isTerminated) { - return; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* The name from PulseAudio is the ID for miniaudio. */ - if (pSinkInfo->name != NULL) { - ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); - } - - /* The description from PulseAudio is the name for miniaudio. */ - if (pSinkInfo->description != NULL) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); - } - - if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) { - deviceInfo.isDefault = MA_TRUE; - } - - pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData); - - (void)pPulseContext; /* Unused. */ -} - -static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; - ma_device_info deviceInfo; - - MA_ASSERT(pData != NULL); - - if (endOfList || pData->isTerminated) { - return; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* The name from PulseAudio is the ID for miniaudio. */ - if (pSourceInfo->name != NULL) { - ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1); - } - - /* The description from PulseAudio is the name for miniaudio. */ - if (pSourceInfo->description != NULL) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1); - } - - if (pSourceInfo->index == pData->defaultDeviceIndexCapture) { - deviceInfo.isDefault = MA_TRUE; - } - - pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData); - - (void)pPulseContext; /* Unused. */ -} - -static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result = MA_SUCCESS; - ma_context_enumerate_devices_callback_data__pulse callbackData; - ma_pa_operation* pOP = NULL; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - callbackData.pContext = pContext; - callbackData.callback = callback; - callbackData.pUserData = pUserData; - callbackData.isTerminated = MA_FALSE; - callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1; - callbackData.defaultDeviceIndexCapture = (ma_uint32)-1; - - /* We need to get the index of the default devices. */ - ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); - ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); - - /* Playback. */ - if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); - if (pOP == NULL) { - result = MA_ERROR; - goto done; - } - - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - if (result != MA_SUCCESS) { - goto done; - } - } - - - /* Capture. */ - if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); - if (pOP == NULL) { - result = MA_ERROR; - goto done; - } - - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - if (result != MA_SUCCESS) { - goto done; - } - } - -done: - return result; -} - - -typedef struct -{ - ma_device_info* pDeviceInfo; - ma_uint32 defaultDeviceIndex; - ma_bool32 foundDevice; -} ma_context_get_device_info_callback_data__pulse; - -static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; - - if (endOfList > 0) { - return; - } - - MA_ASSERT(pData != NULL); - pData->foundDevice = MA_TRUE; - - if (pInfo->name != NULL) { - ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); - } - - if (pInfo->description != NULL) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); - } - - /* - We're just reporting a single data format here. I think technically PulseAudio might support - all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to - report the "native" device format. - */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); - pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; - pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; - pData->pDeviceInfo->nativeDataFormats[0].flags = 0; - pData->pDeviceInfo->nativeDataFormatCount = 1; - - if (pData->defaultDeviceIndex == pInfo->index) { - pData->pDeviceInfo->isDefault = MA_TRUE; - } - - (void)pPulseContext; /* Unused. */ -} - -static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; - - if (endOfList > 0) { - return; - } - - MA_ASSERT(pData != NULL); - pData->foundDevice = MA_TRUE; - - if (pInfo->name != NULL) { - ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); - } - - if (pInfo->description != NULL) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); - } - - /* - We're just reporting a single data format here. I think technically PulseAudio might support - all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to - report the "native" device format. - */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); - pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; - pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; - pData->pDeviceInfo->nativeDataFormats[0].flags = 0; - pData->pDeviceInfo->nativeDataFormatCount = 1; - - if (pData->defaultDeviceIndex == pInfo->index) { - pData->pDeviceInfo->isDefault = MA_TRUE; - } - - (void)pPulseContext; /* Unused. */ -} - -static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result = MA_SUCCESS; - ma_context_get_device_info_callback_data__pulse callbackData; - ma_pa_operation* pOP = NULL; - const char* pDeviceName = NULL; - - MA_ASSERT(pContext != NULL); - - callbackData.pDeviceInfo = pDeviceInfo; - callbackData.foundDevice = MA_FALSE; - - if (pDeviceID != NULL) { - pDeviceName = pDeviceID->pulse; - } else { - pDeviceName = NULL; - } - - result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); - - if (deviceType == ma_device_type_playback) { - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData); - } else { - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData); - } - - if (pOP != NULL) { - ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); - } else { - result = MA_ERROR; - goto done; - } - - if (!callbackData.foundDevice) { - result = MA_NO_DEVICE; - goto done; - } - -done: - return result; -} - -static ma_result ma_device_uninit__pulse(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } - - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - -static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss) -{ - ma_pa_buffer_attr attr; - attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels); - attr.tlength = attr.maxlength / periods; - attr.prebuf = (ma_uint32)-1; - attr.minreq = (ma_uint32)-1; - attr.fragsize = attr.maxlength / periods; - - return attr; -} - -static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) -{ - static int g_StreamCounter = 0; - char actualStreamName[256]; - - if (pStreamName != NULL) { - ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); - } else { - ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:"); - ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */ - } - g_StreamCounter += 1; - - return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); -} - - -static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 bpf; - ma_uint32 deviceState; - ma_uint64 frameCount; - ma_uint64 framesProcessed; - - MA_ASSERT(pDevice != NULL); - - /* - Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio - can fire this callback before the stream has even started. Ridiculous. - */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - return; - } - - bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - MA_ASSERT(bpf > 0); - - frameCount = byteCount / bpf; - framesProcessed = 0; - - while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { - const void* pMappedPCMFrames; - size_t bytesMapped; - ma_uint64 framesMapped; - - int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { - break; /* Failed to map. Abort. */ - } - - framesMapped = bytesMapped / bpf; - if (framesMapped > 0) { - if (pMappedPCMFrames != NULL) { - ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped); - } else { - /* It's a hole. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n"); - } - - pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream); - if (pulseResult < 0) { - break; /* Failed to drop the buffer. */ - } - - framesProcessed += framesMapped; - - } else { - /* Nothing was mapped. Just abort. */ - break; - } - } -} - -static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) -{ - ma_result result = MA_SUCCESS; - ma_uint64 framesProcessed = 0; - size_t bytesMapped; - ma_uint32 bpf; - ma_uint32 deviceState; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pStream != NULL); - - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - MA_ASSERT(bpf > 0); - - deviceState = ma_device_get_state(pDevice); - - bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream); - if (bytesMapped != (size_t)-1) { - if (bytesMapped > 0) { - ma_uint64 framesMapped; - void* pMappedPCMFrames; - int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); - goto done; - } - - framesMapped = bytesMapped / bpf; - - if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ - ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); - } else { - /* Device is not started. Write silence. */ - ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels); - } - - pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); - if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); - goto done; /* Failed to write data to stream. */ - } - - framesProcessed += framesMapped; - } else { - result = MA_SUCCESS; /* No data available for writing. */ - goto done; - } - } else { - result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */ - goto done; - } - -done: - if (pFramesProcessed != NULL) { - *pFramesProcessed = framesProcessed; - } - - return result; -} - -static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 bpf; - ma_uint64 frameCount; - ma_uint64 framesProcessed; - ma_uint32 deviceState; - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* - Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio - can fire this callback before the stream has even started. Ridiculous. - */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - return; - } - - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - MA_ASSERT(bpf > 0); - - frameCount = byteCount / bpf; - framesProcessed = 0; - - while (framesProcessed < frameCount) { - ma_uint64 framesProcessedThisIteration; - - /* Don't keep trying to process frames if the device isn't started. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - break; - } - - result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration); - if (result != MA_SUCCESS) { - break; - } - - framesProcessed += framesProcessedThisIteration; - } -} - -static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - int suspended; - - (void)pStream; - - suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended); - - if (suspended < 0) { - return; - } - - if (suspended == 1) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); - ma_device__on_notification_stopped(pDevice); - } else { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); - ma_device__on_notification_started(pDevice); - } -} - -static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - - (void)pStream; - (void)pUserData; - - ma_device__on_notification_rerouted(pDevice); -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - There have been reports from users where buffers of < ~20ms result glitches when running through - PipeWire. To work around this we're going to have to use a different default buffer size. - */ - const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25; - const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; - - MA_ASSERT(nativeSampleRate != 0); - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate); - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate); - } - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - return pDescriptor->periodSizeInFrames; - } -} - -static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - /* - Notes for PulseAudio: - - - We're always using native format/channels/rate regardless of whether or not PulseAudio - supports the format directly through their own data conversion system. I'm doing this to - reduce as much variability from the PulseAudio side as possible because it's seems to be - extremely unreliable at everything it does. - - - When both the period size in frames and milliseconds are 0, we default to miniaudio's - default buffer sizes rather than leaving it up to PulseAudio because I don't trust - PulseAudio to give us any kind of reasonable latency by default. - - - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this - flag, capture mode will just not work properly until you open another PulseAudio app. - */ - - ma_result result = MA_SUCCESS; - int error = 0; - const char* devPlayback = NULL; - const char* devCapture = NULL; - ma_format format = ma_format_unknown; - ma_uint32 channels = 0; - ma_uint32 sampleRate = 0; - ma_pa_sink_info sinkInfo; - ma_pa_source_info sourceInfo; - ma_pa_sample_spec ss; - ma_pa_channel_map cmap; - ma_pa_buffer_attr attr; - const ma_pa_sample_spec* pActualSS = NULL; - const ma_pa_channel_map* pActualCMap = NULL; - const ma_pa_buffer_attr* pActualAttr = NULL; - ma_uint32 iChannel; - ma_pa_stream_flags_t streamFlags; - - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->pulse); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with the PulseAudio backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - if (pDescriptorPlayback->pDeviceID != NULL) { - devPlayback = pDescriptorPlayback->pDeviceID->pulse; - } - - format = pDescriptorPlayback->format; - channels = pDescriptorPlayback->channels; - sampleRate = pDescriptorPlayback->sampleRate; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - if (pDescriptorCapture->pDeviceID != NULL) { - devCapture = pDescriptorCapture->pDeviceID->pulse; - } - - format = pDescriptorCapture->format; - channels = pDescriptorCapture->channels; - sampleRate = pDescriptorCapture->sampleRate; - } - - - - result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); - return result; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); - goto on_error0; - } - - ss = sourceInfo.sample_spec; - cmap = sourceInfo.channel_map; - - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { - if (ma_is_little_endian()) { - ss.format = MA_PA_SAMPLE_FLOAT32LE; - } else { - ss.format = MA_PA_SAMPLE_FLOAT32BE; - } - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); - } - if (ss.rate == 0) { - ss.rate = MA_DEFAULT_SAMPLE_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); - } - if (ss.channels == 0) { - ss.channels = MA_DEFAULT_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); - } - - /* We now have enough information to calculate our actual period size in frames. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); - - attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - - pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); - if (pDevice->pulse.pStreamCapture == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); - result = MA_ERROR; - goto on_error0; - } - - - /* The callback needs to be set before connecting the stream. */ - ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); - - /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); - - /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); - - - /* Connect after we've got all of our internal state set up. */ - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; - if (devCapture != NULL) { - streamFlags |= MA_PA_STREAM_DONT_MOVE; - } - - error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); - if (error != MA_PA_OK) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); - result = ma_result_from_pulse(error); - goto on_error1; - } - - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (result != MA_SUCCESS) { - goto on_error2; - } - - - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualSS != NULL) { - ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); - } - - pDescriptorCapture->format = ma_format_from_pulse(ss.format); - pDescriptorCapture->channels = ss.channels; - pDescriptorCapture->sampleRate = ss.rate; - - if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate); - result = MA_ERROR; - goto on_error4; - } - - /* Internal channel map. */ - - /* - Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting - the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono - and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For - all other channel counts we need to just put up with whatever PipeWire reports and hope it gets - fixed sooner than later. I might remove this hack later. - */ - if (pDescriptorCapture->channels > 2) { - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - - for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { - pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); - } - } else { - /* Hack for mono and stereo. */ - if (pDescriptorCapture->channels == 1) { - pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO; - } else if (pDescriptorCapture->channels == 2) { - pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT; - pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - } - - - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualAttr != NULL) { - attr = *pActualAttr; - } - - if (attr.fragsize > 0) { - pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1); - } else { - pDescriptorCapture->periodCount = 1; - } - - pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); - goto on_error2; - } - - ss = sinkInfo.sample_spec; - cmap = sinkInfo.channel_map; - - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { - if (ma_is_little_endian()) { - ss.format = MA_PA_SAMPLE_FLOAT32LE; - } else { - ss.format = MA_PA_SAMPLE_FLOAT32BE; - } - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); - } - if (ss.rate == 0) { - ss.rate = MA_DEFAULT_SAMPLE_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); - } - if (ss.channels == 0) { - ss.channels = MA_DEFAULT_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); - } - - /* We now have enough information to calculate the actual buffer size in frames. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); - - attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - - pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); - if (pDevice->pulse.pStreamPlayback == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); - result = MA_ERROR; - goto on_error2; - } - - - /* - Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a - device state of ma_device_state_uninitialized. - */ - ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); - - /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); - - /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); - - - /* Connect after we've got all of our internal state set up. */ - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; - if (devPlayback != NULL) { - streamFlags |= MA_PA_STREAM_DONT_MOVE; - } - - error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); - if (error != MA_PA_OK) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); - result = ma_result_from_pulse(error); - goto on_error3; - } - - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (result != MA_SUCCESS) { - goto on_error3; - } - - - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualSS != NULL) { - ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); - } - - pDescriptorPlayback->format = ma_format_from_pulse(ss.format); - pDescriptorPlayback->channels = ss.channels; - pDescriptorPlayback->sampleRate = ss.rate; - - if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate); - result = MA_ERROR; - goto on_error4; - } - - /* Internal channel map. */ - - /* - Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting - the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono - and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For - all other channel counts we need to just put up with whatever PipeWire reports and hope it gets - fixed sooner than later. I might remove this hack later. - */ - if (pDescriptorPlayback->channels > 2) { - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { - pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); - } - } else { - /* Hack for mono and stereo. */ - if (pDescriptorPlayback->channels == 1) { - pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO; - } else if (pDescriptorPlayback->channels == 2) { - pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT; - pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - } - - - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualAttr != NULL) { - attr = *pActualAttr; - } - - if (attr.tlength > 0) { - pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1); - } else { - pDescriptorPlayback->periodCount = 1; - } - - pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - } - - - /* - We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main - part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for - us later on because that will only do it if it's a fully asynchronous backend - i.e. the - onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. - */ - if (pConfig->deviceType == ma_device_type_duplex) { - ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; - ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; - ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; - - result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); - goto on_error4; - } - } - - return MA_SUCCESS; - - -on_error4: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } -on_error3: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } -on_error2: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } -on_error1: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } -on_error0: - return result; -} - - -static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) -{ - ma_bool32* pIsSuccessful = (ma_bool32*)pUserData; - MA_ASSERT(pIsSuccessful != NULL); - - *pIsSuccessful = (ma_bool32)success; - - (void)pStream; /* Unused. */ -} - -static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork) -{ - ma_context* pContext = pDevice->pContext; - ma_bool32 wasSuccessful; - ma_pa_stream* pStream; - ma_pa_operation* pOP; - ma_result result; - - /* This should not be called with a duplex device type. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - wasSuccessful = MA_FALSE; - - pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); - MA_ASSERT(pStream != NULL); - - pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); - if (pOP == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); - return MA_ERROR; - } - - result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); - return result; - } - - if (!wasSuccessful) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start"); - return MA_ERROR; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__pulse(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - We need to fill some data before uncorking. Not doing this will result in the write callback - never getting fired. We're not going to abort if writing fails because I still want the device - to get uncorked. - */ - ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ - - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__pulse(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - Ideally we would drain the device here, but there's been cases where PulseAudio seems to be - broken on some systems to the point where no audio processing seems to happen. When this - happens, draining never completes and we get stuck here. For now I'm disabling draining of - the device so we don't just freeze the application. - */ - #if 0 - ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); - ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - #endif - - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop__pulse(ma_device* pDevice) -{ - int resultPA; - - MA_ASSERT(pDevice != NULL); - - /* NOTE: Don't start the device here. It'll be done at a higher level. */ - - /* - All data is handled through callbacks. All we need to do is iterate over the main loop and let - the callbacks deal with it. - */ - while (ma_device_get_state(pDevice) == ma_device_state_started) { - resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); - if (resultPA < 0) { - break; - } - } - - /* NOTE: Don't stop the device here. It'll be done at a higher level. */ - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__pulse(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_pulseaudio); - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); - - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->pulse.pulseSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libpulseNames[] = { - "libpulse.so", - "libpulse.so.0" - }; - size_t i; - - for (i = 0; i < ma_countof(libpulseNames); ++i) { - pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]); - if (pContext->pulse.pulseSO != NULL) { - break; - } - } - - if (pContext->pulse.pulseSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new"); - pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free"); - pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_quit"); - pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api"); - pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate"); - pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup"); - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); - pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new"); - pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref"); - pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect"); - pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect"); - pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback"); - pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state"); - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); - pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list"); - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); - pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref"); - pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state"); - pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend"); - pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid"); - pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible"); - pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new"); - pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref"); - pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback"); - pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record"); - pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect"); - pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state"); - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); - pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map"); - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); - pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name"); - pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback"); - pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback"); - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); - pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended"); - pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush"); - pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain"); - pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked"); - pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork"); - pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger"); - pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write"); - pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write"); - pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek"); - pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop"); - pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size"); - pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size"); -#else - /* This strange assignment system is just for type safety. */ - ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; - ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; - ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; - ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; - ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; - ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; - ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; - ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; - ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; - ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; - ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; - ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; - ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; - ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; - ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; - ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; - ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; - ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; - ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; - ma_pa_context_new_proc _pa_context_new = pa_context_new; - ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; - ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; - ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; - ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; - ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; - ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; - ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; - ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; - ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; - ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; - ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; - ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; - ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; - ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; - ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; - ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; - ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; - ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; - ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; - ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; - ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; - ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; - ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; - ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; - ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; - ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; - ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; - ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; - ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; - ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; - ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; - ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; - ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; - ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; - ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; - ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; - ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; - ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; - ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; - ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; - ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; - - pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new; - pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free; - pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit; - pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api; - pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate; - pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup; - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new; - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free; - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start; - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop; - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock; - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock; - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait; - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal; - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept; - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval; - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api; - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread; - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name; - pContext->pulse.pa_context_new = (ma_proc)_pa_context_new; - pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref; - pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect; - pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect; - pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback; - pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state; - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list; - pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list; - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name; - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name; - pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref; - pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state; - pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend; - pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid; - pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible; - pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new; - pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref; - pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback; - pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record; - pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect; - pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state; - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec; - pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map; - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr; - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr; - pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name; - pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; - pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; - pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; - pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; - pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; - pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked; - pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork; - pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger; - pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write; - pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write; - pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek; - pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop; - pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size; - pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; -#endif - - /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ - pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); - if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { - return MA_OUT_OF_MEMORY; - } - - pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); - if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->pulse.pulseSO); - #endif - return result; - } - - /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ - pCallbacks->onContextInit = ma_context_init__pulse; - pCallbacks->onContextUninit = ma_context_uninit__pulse; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse; - pCallbacks->onDeviceInit = ma_device_init__pulse; - pCallbacks->onDeviceUninit = ma_device_uninit__pulse; - pCallbacks->onDeviceStart = ma_device_start__pulse; - pCallbacks->onDeviceStop = ma_device_stop__pulse; - pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse; - - return MA_SUCCESS; -} -#endif - - -/****************************************************************************** - -JACK Backend - -******************************************************************************/ -#ifdef MA_HAS_JACK - -/* It is assumed jack.h is available when compile-time linking is being used. */ -#ifdef MA_NO_RUNTIME_LINKING -#include - -typedef jack_nframes_t ma_jack_nframes_t; -typedef jack_options_t ma_jack_options_t; -typedef jack_status_t ma_jack_status_t; -typedef jack_client_t ma_jack_client_t; -typedef jack_port_t ma_jack_port_t; -typedef JackProcessCallback ma_JackProcessCallback; -typedef JackBufferSizeCallback ma_JackBufferSizeCallback; -typedef JackShutdownCallback ma_JackShutdownCallback; -#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE -#define ma_JackNoStartServer JackNoStartServer -#define ma_JackPortIsInput JackPortIsInput -#define ma_JackPortIsOutput JackPortIsOutput -#define ma_JackPortIsPhysical JackPortIsPhysical -#else -typedef ma_uint32 ma_jack_nframes_t; -typedef int ma_jack_options_t; -typedef int ma_jack_status_t; -typedef struct ma_jack_client_t ma_jack_client_t; -typedef struct ma_jack_port_t ma_jack_port_t; -typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg); -typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); -typedef void (* ma_JackShutdownCallback) (void* arg); -#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" -#define ma_JackNoStartServer 1 -#define ma_JackPortIsInput 1 -#define ma_JackPortIsOutput 2 -#define ma_JackPortIsPhysical 4 -#endif - -typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); -typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_client_name_size_proc) (void); -typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); -typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg); -typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); -typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client); -typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client); -typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); -typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port); -typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); -typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port); -typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes); -typedef void (* ma_jack_free_proc) (void* ptr); - -static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient) -{ - size_t maxClientNameSize; - char clientName[256]; - ma_jack_status_t status; - ma_jack_client_t* pClient; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppClient != NULL); - - if (ppClient) { - *ppClient = NULL; - } - - maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ - ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); - - pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); - if (pClient == NULL) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - if (ppClient) { - *ppClient = pClient; - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - (void)cbResult; /* For silencing a static analysis warning. */ - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_jack_client_t* pClient; - ma_result result; - const char** ppPorts; - - MA_ASSERT(pContext != NULL); - - if (pDeviceID != NULL && pDeviceID->jack != 0) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - /* Jack only uses default devices. */ - pDeviceInfo->isDefault = MA_TRUE; - - /* Jack only supports f32 and has a specific channel count and sample rate. */ - pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; - - /* The channel count and sample rate can only be determined by opening the device. */ - result = ma_context_open_client__jack(pContext, &pClient); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); - return result; - } - - pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); - pDeviceInfo->nativeDataFormats[0].channels = 0; - - ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); - if (ppPorts == NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { - pDeviceInfo->nativeDataFormats[0].channels += 1; - } - - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__jack(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->jack.pClient != NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient); - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static void ma_device__jack_shutdown_callback(void* pUserData) -{ - /* JACK died. Stop the device. */ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_device_stop(pDevice); -} - -static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); - if (pNewBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - - pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; - pDevice->playback.internalPeriodSizeInFrames = frameCount; - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); - if (pNewBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - - pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; - pDevice->playback.internalPeriodSizeInFrames = frameCount; - } - - return 0; -} - -static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData) -{ - ma_device* pDevice; - ma_context* pContext; - ma_uint32 iChannel; - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - /* Channels need to be interleaved. */ - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); - if (pSrc != NULL) { - float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; - ma_jack_nframes_t iFrame; - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - *pDst = *pSrc; - - pDst += pDevice->capture.internalChannels; - pSrc += 1; - } - } - } - - ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); - - /* Channels need to be deinterleaved. */ - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); - if (pDst != NULL) { - const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; - ma_jack_nframes_t iFrame; - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - *pDst = *pSrc; - - pDst += 1; - pSrc += pDevice->playback.internalChannels; - } - } - } - } - - return 0; -} - -static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - ma_uint32 periodSizeInFrames; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDevice != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* Only supporting default devices with JACK. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); - return MA_NO_DEVICE; - } - - /* No exclusive mode with the JACK backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Open the client. */ - result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); - return result; - } - - /* Callbacks. */ - if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); - - - /* The buffer size in frames can change. */ - periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPort; - const char** ppPorts; - - pDescriptorCapture->format = ma_format_f32; - pDescriptorCapture->channels = 0; - pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); - if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* Need to count the number of ports first so we can allocate some memory. */ - while (ppPorts[pDescriptorCapture->channels] != NULL) { - pDescriptorCapture->channels += 1; - } - - pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsCapture == NULL) { - return MA_OUT_OF_MEMORY; - } - - for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) { - char name[64]; - ma_strcpy_s(name, sizeof(name), "capture"); - ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ - - pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); - if (pDevice->jack.ppPortsCapture[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } - - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - - pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferCapture == NULL) { - ma_device_uninit__jack(pDevice); - return MA_OUT_OF_MEMORY; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPort; - const char** ppPorts; - - pDescriptorPlayback->format = ma_format_f32; - pDescriptorPlayback->channels = 0; - pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); - if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* Need to count the number of ports first so we can allocate some memory. */ - while (ppPorts[pDescriptorPlayback->channels] != NULL) { - pDescriptorPlayback->channels += 1; - } - - pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsPlayback == NULL) { - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) { - char name[64]; - ma_strcpy_s(name, sizeof(name), "playback"); - ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ - - pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); - if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } - - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - - pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { - ma_device_uninit__jack(pDevice); - return MA_OUT_OF_MEMORY; - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_start__jack(ma_device* pDevice) -{ - ma_context* pContext = pDevice->pContext; - int resultJACK; - size_t i; - - resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); - if (resultJACK != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); - return MA_FAILED_TO_START_BACKEND_DEVICE; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); - if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); - return MA_ERROR; - } - - for (i = 0; ppServerPorts[i] != NULL; ++i) { - const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); - - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); - if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); - return MA_ERROR; - } - } - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); - if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); - return MA_ERROR; - } - - for (i = 0; ppServerPorts[i] != NULL; ++i) { - const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); - - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); - if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); - return MA_ERROR; - } - } - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__jack(ma_device* pDevice) -{ - ma_context* pContext = pDevice->pContext; - - if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); - return MA_ERROR; - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__jack(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_jack); - - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - pContext->jack.pClientName = NULL; - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->jack.jackSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libjackNames[] = { -#ifdef MA_WIN32 - "libjack.dll", - "libjack64.dll" -#else - "libjack.so", - "libjack.so.0" -#endif - }; - size_t i; - - for (i = 0; i < ma_countof(libjackNames); ++i) { - pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]); - if (pContext->jack.jackSO != NULL) { - break; - } - } - - if (pContext->jack.jackSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open"); - pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close"); - pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size"); - pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback"); - pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback"); - pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown"); - pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate"); - pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size"); - pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports"); - pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate"); - pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate"); - pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect"); - pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register"); - pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name"); - pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer"); - pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free"); -#else - /* - This strange assignment system is here just to ensure type safety of miniaudio's function pointer - types. If anything differs slightly the compiler should throw a warning. - */ - ma_jack_client_open_proc _jack_client_open = jack_client_open; - ma_jack_client_close_proc _jack_client_close = jack_client_close; - ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; - ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; - ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; - ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; - ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; - ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; - ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; - ma_jack_activate_proc _jack_activate = jack_activate; - ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; - ma_jack_connect_proc _jack_connect = jack_connect; - ma_jack_port_register_proc _jack_port_register = jack_port_register; - ma_jack_port_name_proc _jack_port_name = jack_port_name; - ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; - ma_jack_free_proc _jack_free = jack_free; - - pContext->jack.jack_client_open = (ma_proc)_jack_client_open; - pContext->jack.jack_client_close = (ma_proc)_jack_client_close; - pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size; - pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback; - pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback; - pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown; - pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate; - pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size; - pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports; - pContext->jack.jack_activate = (ma_proc)_jack_activate; - pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate; - pContext->jack.jack_connect = (ma_proc)_jack_connect; - pContext->jack.jack_port_register = (ma_proc)_jack_port_register; - pContext->jack.jack_port_name = (ma_proc)_jack_port_name; - pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer; - pContext->jack.jack_free = (ma_proc)_jack_free; -#endif - - if (pConfig->jack.pClientName != NULL) { - pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks); - } - pContext->jack.tryStartServer = pConfig->jack.tryStartServer; - - /* - Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting - a temporary client. - */ - { - ma_jack_client_t* pDummyClient; - ma_result result = ma_context_open_client__jack(pContext, &pDummyClient); - if (result != MA_SUCCESS) { - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->jack.jackSO); - #endif - return MA_NO_BACKEND; - } - - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); - } - - - pCallbacks->onContextInit = ma_context_init__jack; - pCallbacks->onContextUninit = ma_context_uninit__jack; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; - pCallbacks->onDeviceInit = ma_device_init__jack; - pCallbacks->onDeviceUninit = ma_device_uninit__jack; - pCallbacks->onDeviceStart = ma_device_start__jack; - pCallbacks->onDeviceStop = ma_device_stop__jack; - pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* JACK */ - - - -/****************************************************************************** - -Core Audio Backend - -References -========== -- Technical Note TN2091: Device input using the HAL Output Audio Unit - https://developer.apple.com/library/archive/technotes/tn2091/_index.html - -******************************************************************************/ -#ifdef MA_HAS_COREAUDIO -#include - -#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 - #define MA_APPLE_MOBILE - #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 - #define MA_APPLE_TV - #endif - #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 - #define MA_APPLE_WATCH - #endif - #if __has_feature(objc_arc) - #define MA_BRIDGE_TRANSFER __bridge_transfer - #define MA_BRIDGE_RETAINED __bridge_retained - #else - #define MA_BRIDGE_TRANSFER - #define MA_BRIDGE_RETAINED - #endif -#else - #define MA_APPLE_DESKTOP -#endif - -#if defined(MA_APPLE_DESKTOP) -#include -#else -#include -#endif - -#include - -/* CoreFoundation */ -typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); -typedef void (* ma_CFRelease_proc)(CFTypeRef cf); - -/* CoreAudio */ -#if defined(MA_APPLE_DESKTOP) -typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); -typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); -typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); -typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); -typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); -#endif - -/* AudioToolbox */ -typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); -typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); -typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); -typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); -typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); -typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); -typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); -typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); - - -#define MA_COREAUDIO_OUTPUT_BUS 0 -#define MA_COREAUDIO_INPUT_BUS 1 - -#if defined(MA_APPLE_DESKTOP) -static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit); -#endif - -/* -Core Audio - -So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation -apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose -needing to figure out how this darn thing works, I'm going to outline a few things here. - -Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be -able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen -that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent -and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the -distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. - -Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When -retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific -data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the -devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be -the central APIs for retrieving information about the system and specific devices. - -To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a -structure with three variables and is used to identify which property you are getting or setting. The first is the "selector" -which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is -typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and -kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to -kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different. - -Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size -of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property -address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the -size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of -AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. -*/ - -static ma_result ma_result_from_OSStatus(OSStatus status) -{ - switch (status) - { - case noErr: return MA_SUCCESS; - #if defined(MA_APPLE_DESKTOP) - case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED; - case kAudioHardwareUnspecifiedError: return MA_ERROR; - case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS; - case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION; - case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION; - case kAudioHardwareBadObjectError: return MA_INVALID_ARGS; - case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS; - case kAudioHardwareBadStreamError: return MA_INVALID_ARGS; - case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION; - case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED; - case kAudioDevicePermissionsError: return MA_ACCESS_DENIED; - #endif - default: return MA_ERROR; - } -} - -#if 0 -static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) -{ - switch (bit) - { - case kAudioChannelBit_Left: return MA_CHANNEL_LEFT; - case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT; - case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER; - case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE; - case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; - case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; - case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; - case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; - case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return MA_CHANNEL_NONE; - } -} -#endif - -static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut) -{ - MA_ASSERT(pDescription != NULL); - MA_ASSERT(pFormatOut != NULL); - - *pFormatOut = ma_format_unknown; /* Safety. */ - - /* There's a few things miniaudio doesn't support. */ - if (pDescription->mFormatID != kAudioFormatLinearPCM) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* We don't support any non-packed formats that are aligned high. */ - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Only supporting native-endian. */ - if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */ - /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { - return MA_FORMAT_NOT_SUPPORTED; - }*/ - - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) { - if (pDescription->mBitsPerChannel == 32) { - *pFormatOut = ma_format_f32; - return MA_SUCCESS; - } - } else { - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) { - if (pDescription->mBitsPerChannel == 16) { - *pFormatOut = ma_format_s16; - return MA_SUCCESS; - } else if (pDescription->mBitsPerChannel == 24) { - if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) { - *pFormatOut = ma_format_s24; - return MA_SUCCESS; - } else { - if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) { - /* TODO: Implement ma_format_s24_32. */ - /**pFormatOut = ma_format_s24_32;*/ - /*return MA_SUCCESS;*/ - return MA_FORMAT_NOT_SUPPORTED; - } - } - } else if (pDescription->mBitsPerChannel == 32) { - *pFormatOut = ma_format_s32; - return MA_SUCCESS; - } - } else { - if (pDescription->mBitsPerChannel == 8) { - *pFormatOut = ma_format_u8; - return MA_SUCCESS; - } - } - } - - /* Getting here means the format is not supported. */ - return MA_FORMAT_NOT_SUPPORTED; -} - -#if defined(MA_APPLE_DESKTOP) -static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) -{ - switch (label) - { - case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE; - case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER; - case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE; - case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; - case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; - case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; - case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; - case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; - case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE; - case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO; - case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO; - case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO; - case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE; - case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE; - case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0; - case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1; - case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2; - case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3; - case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4; - case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5; - case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6; - case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7; - case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8; - case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9; - case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10; - case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11; - case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12; - case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13; - case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14; - case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15; - case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE; - - #if 0 /* Introduced in a later version of macOS. */ - case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE; - case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0; - case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1; - case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2; - case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3; - case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4; - case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5; - case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6; - case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7; - case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8; - case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9; - case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10; - case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11; - case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12; - case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13; - case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14; - case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15; - case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE; - #endif - - default: return MA_CHANNEL_NONE; - } -} - -static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap) -{ - MA_ASSERT(pChannelLayout != NULL); - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - UInt32 iChannel; - for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) { - pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); - } - } else -#if 0 - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - /* This is the same kind of system that's used by Windows audio APIs. */ - UInt32 iChannel = 0; - UInt32 iBit; - AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; - for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) { - AudioChannelBitmap bit = bitmap & (1 << iBit); - if (bit != 0) { - pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit); - } - } - } else -#endif - { - /* - Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should - be updated to determine the mapping based on the tag. - */ - UInt32 channelCount; - - /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */ - if (channelMapCap > 0xFFFFFFFF) { - channelMapCap = 0xFFFFFFFF; - } - - channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap); - - switch (pChannelLayout->mChannelLayoutTag) - { - case kAudioChannelLayoutTag_Mono: - case kAudioChannelLayoutTag_Stereo: - case kAudioChannelLayoutTag_StereoHeadphones: - case kAudioChannelLayoutTag_MatrixStereo: - case kAudioChannelLayoutTag_MidSide: - case kAudioChannelLayoutTag_XY: - case kAudioChannelLayoutTag_Binaural: - case kAudioChannelLayoutTag_Ambisonic_B_Format: - { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } break; - - case kAudioChannelLayoutTag_Octagonal: - { - pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; - pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; - } /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Hexagonal: - { - pChannelMap[5] = MA_CHANNEL_BACK_CENTER; - } /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Pentagonal: - { - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - } /* Intentional fallghrough. */ - case kAudioChannelLayoutTag_Quadraphonic: - { - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[1] = MA_CHANNEL_RIGHT; - pChannelMap[0] = MA_CHANNEL_LEFT; - } break; - - /* TODO: Add support for more tags here. */ - - default: - { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } break; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddressDevices; - UInt32 deviceObjectsDataSize; - OSStatus status; - AudioObjectID* pDeviceObjectIDs; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceCount != NULL); - MA_ASSERT(ppDeviceObjectIDs != NULL); - - /* Safety. */ - *pDeviceCount = 0; - *ppDeviceObjectIDs = NULL; - - propAddressDevices.mSelector = kAudioHardwarePropertyDevices; - propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDevices.mElement = kAudioObjectPropertyElementMaster; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks); - if (pDeviceObjectIDs == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); - if (status != noErr) { - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); - *ppDeviceObjectIDs = pDeviceObjectIDs; - - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID) -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - propAddress.mSelector = kAudioDevicePropertyDeviceUID; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - dataSize = sizeof(*pUID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) -{ - CFStringRef uid; - ma_result result; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid); - if (result != MA_SUCCESS) { - return result; - } - - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { - return MA_ERROR; - } - - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) -{ - AudioObjectPropertyAddress propAddress; - CFStringRef deviceName = NULL; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - dataSize = sizeof(deviceName); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { - return MA_ERROR; - } - - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); - return MA_SUCCESS; -} - -static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioBufferList* pBufferList; - ma_bool32 isSupported; - - MA_ASSERT(pContext != NULL); - - /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ - propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; - propAddress.mScope = scope; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return MA_FALSE; - } - - pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pBufferList == NULL) { - return MA_FALSE; /* Out of memory. */ - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); - if (status != noErr) { - ma_free(pBufferList, &pContext->allocationCallbacks); - return MA_FALSE; - } - - isSupported = MA_FALSE; - if (pBufferList->mNumberBuffers > 0) { - isSupported = MA_TRUE; - } - - ma_free(pBufferList, &pContext->allocationCallbacks); - return isSupported; -} - -static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID) -{ - return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput); -} - -static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID) -{ - return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput); -} - - -static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioStreamRangedDescription* pDescriptions; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDescriptionCount != NULL); - MA_ASSERT(ppDescriptions != NULL); - - /* - TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My - MacBook Pro uses s24/32 format, however, which miniaudio does not currently support. - */ - propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pDescriptions == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); - if (status != noErr) { - ma_free(pDescriptions, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pDescriptionCount = dataSize / sizeof(*pDescriptions); - *ppDescriptions = pDescriptions; - return MA_SUCCESS; -} - - -static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioChannelLayout* pChannelLayout; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppChannelLayout != NULL); - - *ppChannelLayout = NULL; /* Safety. */ - - propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pChannelLayout == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); - if (status != noErr) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *ppChannelLayout = pChannelLayout; - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount) -{ - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pChannelCount != NULL); - - *pChannelCount = 0; /* Safety. */ - - result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); - if (result != MA_SUCCESS) { - return result; - } - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - *pChannelCount = pChannelLayout->mNumberChannelDescriptions; - } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap); - } else { - *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return MA_SUCCESS; -} - -#if 0 -static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) -{ - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); - if (result != MA_SUCCESS) { - return result; /* Rather than always failing here, would it be more robust to simply assume a default? */ - } - - result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; -} -#endif - -static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioValueRange* pSampleRateRanges; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pSampleRateRangesCount != NULL); - MA_ASSERT(ppSampleRateRanges != NULL); - - /* Safety. */ - *pSampleRateRangesCount = 0; - *ppSampleRateRanges = NULL; - - propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pSampleRateRanges == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); - if (status != noErr) { - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); - *ppSampleRateRanges = pSampleRateRanges; - return MA_SUCCESS; -} - -#if 0 -static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut) -{ - UInt32 sampleRateRangeCount; - AudioValueRange* pSampleRateRanges; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pSampleRateOut != NULL); - - *pSampleRateOut = 0; /* Safety. */ - - result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); - if (result != MA_SUCCESS) { - return result; - } - - if (sampleRateRangeCount == 0) { - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_ERROR; /* Should never hit this case should we? */ - } - - if (sampleRateIn == 0) { - /* Search in order of miniaudio's preferred priority. */ - UInt32 iMALSampleRate; - for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) { - ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate]; - UInt32 iCASampleRate; - for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) { - AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate]; - if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) { - *pSampleRateOut = malSampleRate; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } - - /* - If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this - case we just fall back to the first one reported by Core Audio. - */ - MA_ASSERT(sampleRateRangeCount > 0); - - *pSampleRateOut = pSampleRateRanges[0].mMinimum; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } else { - /* Find the closest match to this sample rate. */ - UInt32 currentAbsoluteDifference = INT32_MAX; - UInt32 iCurrentClosestRange = (UInt32)-1; - UInt32 iRange; - for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) { - if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) { - *pSampleRateOut = sampleRateIn; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } else { - UInt32 absoluteDifference; - if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) { - absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn; - } else { - absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; - } - - if (currentAbsoluteDifference > absoluteDifference) { - currentAbsoluteDifference = absoluteDifference; - iCurrentClosestRange = iRange; - } - } - } - - MA_ASSERT(iCurrentClosestRange != (UInt32)-1); - - *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - - /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */ - /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/ - /*return MA_ERROR;*/ -} -#endif - -static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut) -{ - AudioObjectPropertyAddress propAddress; - AudioValueRange bufferSizeRange; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pBufferSizeInFramesOut != NULL); - - *pBufferSizeInFramesOut = 0; /* Safety. */ - - propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - dataSize = sizeof(bufferSizeRange); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - /* This is just a clamp. */ - if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { - *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum; - } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) { - *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum; - } else { - *pBufferSizeInFramesOut = bufferSizeInFramesIn; - } - - return MA_SUCCESS; -} - -static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut) -{ - ma_result result; - ma_uint32 chosenBufferSizeInFrames; - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames); - if (result != MA_SUCCESS) { - return result; - } - - /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ - propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); - - /* Get the actual size of the buffer. */ - dataSize = sizeof(*pPeriodSizeInOut); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - *pPeriodSizeInOut = chosenBufferSizeInFrames; - return MA_SUCCESS; -} - -static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID) -{ - AudioObjectPropertyAddress propAddressDefaultDevice; - UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); - AudioObjectID defaultDeviceObjectID; - OSStatus status; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceObjectID != NULL); - - /* Safety. */ - *pDeviceObjectID = 0; - - propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster; - if (deviceType == ma_device_type_playback) { - propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - } else { - propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice; - } - - defaultDeviceObjectIDSize = sizeof(AudioObjectID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); - if (status == noErr) { - *pDeviceObjectID = defaultDeviceObjectID; - return MA_SUCCESS; - } - - /* If we get here it means we couldn't find the device. */ - return MA_NO_DEVICE; -} - -static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceObjectID != NULL); - - /* Safety. */ - *pDeviceObjectID = 0; - - if (pDeviceID == NULL) { - /* Default device. */ - return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID); - } else { - /* Explicit device. */ - UInt32 deviceCount; - AudioObjectID* pDeviceObjectIDs; - ma_result result; - UInt32 iDevice; - - result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); - if (result != MA_SUCCESS) { - return result; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - - char uid[256]; - if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) { - continue; - } - - if (deviceType == ma_device_type_playback) { - if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { - if (strcmp(uid, pDeviceID->coreaudio) == 0) { - *pDeviceObjectID = deviceObjectID; - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } else { - if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { - if (strcmp(uid, pDeviceID->coreaudio) == 0) { - *pDeviceObjectID = deviceObjectID; - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } - } - - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - } - - /* If we get here it means we couldn't find the device. */ - return MA_NO_DEVICE; -} - - -static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat) -{ - UInt32 deviceFormatDescriptionCount; - AudioStreamRangedDescription* pDeviceFormatDescriptions; - ma_result result; - ma_uint32 desiredSampleRate; - ma_uint32 desiredChannelCount; - ma_format desiredFormat; - AudioStreamBasicDescription bestDeviceFormatSoFar; - ma_bool32 hasSupportedFormat; - UInt32 iFormat; - - result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); - if (result != MA_SUCCESS) { - return result; - } - - desiredSampleRate = sampleRate; - if (desiredSampleRate == 0) { - desiredSampleRate = pOrigFormat->mSampleRate; - } - - desiredChannelCount = channels; - if (desiredChannelCount == 0) { - desiredChannelCount = pOrigFormat->mChannelsPerFrame; - } - - desiredFormat = format; - if (desiredFormat == ma_format_unknown) { - result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat); - if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) { - desiredFormat = g_maFormatPriorities[0]; - } - } - - /* - If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next - loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. - */ - MA_ZERO_OBJECT(&bestDeviceFormatSoFar); - - hasSupportedFormat = MA_FALSE; - for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - ma_format format; - ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format); - if (formatResult == MA_SUCCESS && format != ma_format_unknown) { - hasSupportedFormat = MA_TRUE; - bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; - break; - } - } - - if (!hasSupportedFormat) { - ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); - return MA_FORMAT_NOT_SUPPORTED; - } - - - for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; - ma_format thisSampleFormat; - ma_result formatResult; - ma_format bestSampleFormatSoFar; - - /* If the format is not supported by miniaudio we need to skip this one entirely. */ - formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat); - if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) { - continue; /* The format is not supported by miniaudio. Skip. */ - } - - ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); - - /* Getting here means the format is supported by miniaudio which makes this format a candidate. */ - if (thisDeviceFormat.mSampleRate != desiredSampleRate) { - /* - The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format - so far has an equal sample rate we can just ignore this one. - */ - if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) { - continue; /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */ - } else { - /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */ - if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) { - /* This format has a different sample rate _and_ a different channel count. */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - continue; /* No change to the best format. */ - } else { - /* - Both this format and the best so far have different sample rates and different channel counts. Whichever has the - best format is the new best. - */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format. */ - } - } - } else { - /* This format has a different sample rate but the desired channel count. */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } else { - /* This format has the desired channel count, but the best so far does not. We have a new best. */ - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } - } - } - } else { - /* - The sample rates match which makes this format a very high priority contender. If the best format so far has a different - sample rate it needs to be replaced with this one. - */ - if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */ - if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) { - /* - In this case this format has the same channel count as what the client is requesting. If the best format so far has - a different count, this one becomes the new best. - */ - if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */ - if (thisSampleFormat == desiredFormat) { - bestDeviceFormatSoFar = thisDeviceFormat; - break; /* Found the exact match. */ - } else { - /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } - } - } else { - /* - In this case the channel count is different to what the client has requested. If the best so far has the same channel - count as the requested count then it remains the best. - */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - continue; - } else { - /* - This is the case where both have the same sample rate (good) but different channel counts. Right now both have about - the same priority, but we need to compare the format now. - */ - if (thisSampleFormat == bestSampleFormatSoFar) { - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } - } - } - } - } - } - - *pFormat = bestDeviceFormatSoFar; - - ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); - return MA_SUCCESS; -} - -static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) -{ - AudioUnitScope deviceScope; - AudioUnitElement deviceBus; - UInt32 channelLayoutSize; - OSStatus status; - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - - if (deviceType == ma_device_type_playback) { - deviceScope = kAudioUnitScope_Input; - deviceBus = MA_COREAUDIO_OUTPUT_BUS; - } else { - deviceScope = kAudioUnitScope_Output; - deviceBus = MA_COREAUDIO_INPUT_BUS; - } - - status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks); - if (pChannelLayout == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); - if (status != noErr) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return MA_SUCCESS; -} -#endif /* MA_APPLE_DESKTOP */ - - -#if !defined(MA_APPLE_DESKTOP) -static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) -{ - MA_ZERO_OBJECT(pInfo); - ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1); - ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1); -} -#endif - -static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ -#if defined(MA_APPLE_DESKTOP) - UInt32 deviceCount; - AudioObjectID* pDeviceObjectIDs; - AudioObjectID defaultDeviceObjectIDPlayback; - AudioObjectID defaultDeviceObjectIDCapture; - ma_result result; - UInt32 iDevice; - - ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */ - ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */ - - result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); - if (result != MA_SUCCESS) { - return result; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - ma_device_info info; - - MA_ZERO_OBJECT(&info); - if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) { - continue; - } - if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) { - continue; - } - - if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { - if (deviceObjectID == defaultDeviceObjectIDPlayback) { - info.isDefault = MA_TRUE; - } - - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - break; - } - } - if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { - if (deviceObjectID == defaultDeviceObjectIDCapture) { - info.isDefault = MA_TRUE; - } - - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - break; - } - } - } - - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); -#else - ma_device_info info; - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - - for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - return MA_SUCCESS; - } - } - - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - return MA_SUCCESS; - } - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_DESKTOP) - /* Desktop */ - { - AudioObjectID deviceObjectID; - AudioObjectID defaultDeviceObjectID; - UInt32 streamDescriptionCount; - AudioStreamRangedDescription* pStreamDescriptions; - UInt32 iStreamDescription; - UInt32 sampleRateRangeCount; - AudioValueRange* pSampleRateRanges; - - ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */ - - result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); - if (result != MA_SUCCESS) { - return result; - } - - if (deviceObjectID == defaultDeviceObjectID) { - pDeviceInfo->isDefault = MA_TRUE; - } - - /* - There could be a large number of permutations here. Fortunately there is only a single channel count - being reported which reduces this quite a bit. For sample rates we're only reporting those that are - one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into - our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen - if some driver performs software data conversion and therefore reports every possible format and - sample rate. - */ - pDeviceInfo->nativeDataFormatCount = 0; - - /* Formats. */ - { - ma_format uniqueFormats[ma_format_count]; - ma_uint32 uniqueFormatCount = 0; - ma_uint32 channels; - - /* Channels. */ - result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels); - if (result != MA_SUCCESS) { - return result; - } - - /* Formats. */ - result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); - if (result != MA_SUCCESS) { - return result; - } - - for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { - ma_format format; - ma_bool32 hasFormatBeenHandled = MA_FALSE; - ma_uint32 iOutputFormat; - ma_uint32 iSampleRate; - - result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format); - if (result != MA_SUCCESS) { - continue; - } - - MA_ASSERT(format != ma_format_unknown); - - /* Make sure the format isn't already in the output list. */ - for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) { - if (uniqueFormats[iOutputFormat] == format) { - hasFormatBeenHandled = MA_TRUE; - break; - } - } - - /* If we've already handled this format just skip it. */ - if (hasFormatBeenHandled) { - continue; - } - - uniqueFormats[uniqueFormatCount] = format; - uniqueFormatCount += 1; - - /* Sample Rates */ - result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); - if (result != MA_SUCCESS) { - return result; - } - - /* - Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are - between this range. - */ - for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) { - ma_uint32 iStandardSampleRate; - for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; - if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) { - /* We have a new data format. Add it to the list. */ - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - - if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { - break; /* No more room for any more formats. */ - } - } - } - } - - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - - if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { - break; /* No more room for any more formats. */ - } - } - - ma_free(pStreamDescriptions, &pContext->allocationCallbacks); - } - } -#else - /* Mobile */ - { - AudioComponentDescription desc; - AudioComponent component; - AudioUnit audioUnit; - OSStatus status; - AudioUnitScope formatScope; - AudioUnitElement formatElement; - AudioStreamBasicDescription bestFormat; - UInt32 propSize; - - /* We want to ensure we use a consistent device name to device enumeration. */ - if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') { - ma_bool32 found = MA_FALSE; - if (deviceType == ma_device_type_playback) { - NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); - found = MA_TRUE; - break; - } - } - } else { - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); - found = MA_TRUE; - break; - } - } - } - - if (!found) { - return MA_DOES_NOT_EXIST; - } - } else { - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - } - - - /* - Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is - reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to - retrieve from the AVAudioSession shared instance. - */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_RemoteIO; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (component == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; - formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - - propSize = sizeof(bestFormat); - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); - return ma_result_from_OSStatus(status); - } - - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); - audioUnit = NULL; - - /* Only a single format is being reported for iOS. */ - pDeviceInfo->nativeDataFormatCount = 1; - - result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format); - if (result != MA_SUCCESS) { - return result; - } - - pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame; - - /* - It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do - this we just get the shared instance and inspect. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate; - } - } -#endif - - (void)pDeviceInfo; /* Unused. */ - return MA_SUCCESS; -} - -static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks) -{ - AudioBufferList* pBufferList; - UInt32 audioBufferSizeInBytes; - size_t allocationSize; - - MA_ASSERT(sizeInFrames > 0); - MA_ASSERT(format != ma_format_unknown); - MA_ASSERT(channels > 0); - - allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */ - if (layout == ma_stream_layout_interleaved) { - /* Interleaved case. This is the simple case because we just have one buffer. */ - allocationSize += sizeof(AudioBuffer) * 1; - } else { - /* Non-interleaved case. This is the more complex case because there's more than one buffer. */ - allocationSize += sizeof(AudioBuffer) * channels; - } - - allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); - - pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks); - if (pBufferList == NULL) { - return NULL; - } - - audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format)); - - if (layout == ma_stream_layout_interleaved) { - pBufferList->mNumberBuffers = 1; - pBufferList->mBuffers[0].mNumberChannels = channels; - pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels; - pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList); - } else { - ma_uint32 iBuffer; - pBufferList->mNumberBuffers = channels; - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { - pBufferList->mBuffers[iBuffer].mNumberChannels = 1; - pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes; - pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer); - } - } - - return pBufferList; -} - -static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(format != ma_format_unknown); - MA_ASSERT(channels > 0); - - /* Only resize the buffer if necessary. */ - if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { - AudioBufferList* pNewAudioBufferList; - - pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); - if (pNewAudioBufferList == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* At this point we'll have a new AudioBufferList and we can free the old one. */ - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; - } - - /* Getting here means the capacity of the audio is fine. */ - return MA_SUCCESS; -} - - -static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_stream_layout layout; - - MA_ASSERT(pDevice != NULL); - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/ - - /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ - layout = ma_stream_layout_interleaved; - if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) { - layout = ma_stream_layout_deinterleaved; - } - - if (layout == ma_stream_layout_interleaved) { - /* For now we can assume everything is interleaved. */ - UInt32 iBuffer; - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { - if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) { - ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - if (frameCountForThisBuffer > 0) { - ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer); - } - - /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } else { - /* - This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's - not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just - output silence here. - */ - MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - } - } else { - /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */ - - /* - For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something - very strange has happened and we're not going to support it. - */ - if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) { - ma_uint8 tempBuffer[4096]; - UInt32 iBuffer; - - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) { - ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat); - ma_uint32 framesRemaining = frameCountPerBuffer; - - while (framesRemaining > 0) { - void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; - ma_uint32 iChannel; - ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - if (framesToRead > framesRemaining) { - framesToRead = framesRemaining; - } - - ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead); - - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - } - - ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers); - - framesRemaining -= framesToRead; - } - } - } - } - - (void)pActionFlags; - (void)pTimeStamp; - (void)busNumber; - (void)frameCount; - - return noErr; -} - -static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) -{ - ma_device* pDevice = (ma_device*)pUserData; - AudioBufferList* pRenderedBufferList; - ma_result result; - ma_stream_layout layout; - ma_uint32 iBuffer; - OSStatus status; - - MA_ASSERT(pDevice != NULL); - - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; - MA_ASSERT(pRenderedBufferList); - - /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ - layout = ma_stream_layout_interleaved; - if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) { - layout = ma_stream_layout_deinterleaved; - } - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/ - - /* - There has been a situation reported where frame count passed into this function is greater than the capacity of - our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be, - so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the - number of frames requested by this callback. - */ - result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); - return noErr; - } - - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; - MA_ASSERT(pRenderedBufferList); - - /* - When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes - that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer - being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a - problem when a future call to this callback specifies a larger number of frames. - - To work around this we need to explicitly set the size of each buffer to their respective size in bytes. - */ - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; - } - - status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); - if (status != noErr) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status); - return status; - } - - if (layout == ma_stream_layout_interleaved) { - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { - ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount); - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } else { - /* - This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's - not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. - */ - ma_uint8 silentBuffer[4096]; - ma_uint32 framesRemaining; - - MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer)); - - framesRemaining = frameCount; - while (framesRemaining > 0) { - ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - if (framesToSend > framesRemaining) { - framesToSend = framesRemaining; - } - - ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend); - - framesRemaining -= framesToSend; - } - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - } - } else { - /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ - - /* - For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something - very strange has happened and we're not going to support it. - */ - if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) { - ma_uint8 tempBuffer[4096]; - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) { - ma_uint32 framesRemaining = frameCount; - while (framesRemaining > 0) { - void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; - ma_uint32 iChannel; - ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - if (framesToSend > framesRemaining) { - framesToSend = framesRemaining; - } - - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - } - - ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer); - ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend); - - framesRemaining -= framesToSend; - } - } - } - } - - (void)pActionFlags; - (void)pTimeStamp; - (void)busNumber; - (void)frameCount; - (void)pUnusedBufferList; - - return noErr; -} - -static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - /* Don't do anything if it looks like we're just reinitializing due to a device switch. */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - return; - } - - /* - There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like - AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit) - can try waiting on the same lock. I'm going to try working around this by not calling any Core - Audio APIs in the callback when the device has been stopped or uninitialized. - */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_device__on_notification_stopped(pDevice); - } else { - UInt32 isRunning; - UInt32 isRunningSize = sizeof(isRunning); - OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); - if (status != noErr) { - goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ - } - - if (!isRunning) { - /* - The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: - - 1) When the device is unplugged, this will be called _before_ the default device change notification. - 2) When the device is changed via the default device change notification, this will be called _after_ the switch. - - For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. - */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) { - /* - It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device - via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the - device to be seamless to the client (we don't want them receiving the stopped event and thinking that the device has stopped when it - hasn't!). - */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - goto done; - } - - /* - Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio - will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most - likely be successful in switching to the new device. - - TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted. - */ - goto done; - } - - /* Getting here means we need to stop the device. */ - ma_device__on_notification_stopped(pDevice); - } - } - - (void)propertyID; /* Unused. */ - -done: - /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ - ma_event_signal(&pDevice->coreaudio.stopEvent); -} - -#if defined(MA_APPLE_DESKTOP) -static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ -static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; -static ma_mutex g_DeviceTrackingMutex_CoreAudio; -static ma_device** g_ppTrackedDevices_CoreAudio = NULL; -static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; -static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; - -static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) -{ - ma_device_type deviceType; - - /* Not sure if I really need to check this, but it makes me feel better. */ - if (addressCount == 0) { - return noErr; - } - - if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { - deviceType = ma_device_type_playback; - } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { - deviceType = ma_device_type_capture; - } else { - return noErr; /* Should never hit this. */ - } - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - ma_result reinitResult; - ma_device* pDevice; - - pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; - if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { - if (deviceType == ma_device_type_playback) { - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE; - } else { - pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; - } - - if (reinitResult == MA_SUCCESS) { - ma_device__post_init_setup(pDevice, deviceType); - - /* Restart the device if required. If this fails we need to stop the device entirely. */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - OSStatus status; - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } else if (deviceType == ma_device_type_capture) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - } - - ma_device__on_notification_rerouted(pDevice); - } - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - /* Unused parameters. */ - (void)objectID; - (void)pUserData; - - return noErr; -} - -static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - /* Don't do anything if we've already initializd device tracking. */ - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - } - g_DeviceTrackingInitCounter_CoreAudio += 1; - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - if (g_DeviceTrackingInitCounter_CoreAudio > 0) - g_DeviceTrackingInitCounter_CoreAudio -= 1; - - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - /* At this point there should be no tracked devices. If not there's an error somewhere. */ - if (g_ppTrackedDevices_CoreAudio != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - return MA_INVALID_OPERATION; - } - - ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); - } - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__track__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - /* Allocate memory if required. */ - if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { - ma_uint32 newCap; - ma_device** ppNewDevices; - - newCap = g_TrackedDeviceCap_CoreAudio * 2; - if (newCap == 0) { - newCap = 1; - } - - ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); - if (ppNewDevices == NULL) { - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - return MA_OUT_OF_MEMORY; - } - - g_ppTrackedDevices_CoreAudio = ppNewDevices; - g_TrackedDeviceCap_CoreAudio = newCap; - } - - g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; - g_TrackedDeviceCount_CoreAudio += 1; - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { - /* We've found the device. We now need to remove it from the list. */ - ma_uint32 jDevice; - for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { - g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; - } - - g_TrackedDeviceCount_CoreAudio -= 1; - - /* If there's nothing else in the list we need to free memory. */ - if (g_TrackedDeviceCount_CoreAudio == 0) { - ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); - g_ppTrackedDevices_CoreAudio = NULL; - g_TrackedDeviceCap_CoreAudio = 0; - } - - break; - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} -#endif - -#if defined(MA_APPLE_MOBILE) -@interface ma_ios_notification_handler:NSObject { - ma_device* m_pDevice; -} -@end - -@implementation ma_ios_notification_handler --(id)init:(ma_device*)pDevice -{ - self = [super init]; - m_pDevice = pDevice; - - /* For route changes. */ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; - - /* For interruptions. */ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; - - return self; -} - --(void)dealloc -{ - [self remove_handler]; - - #if defined(__has_feature) - #if !__has_feature(objc_arc) - [super dealloc]; - #endif - #endif -} - --(void)remove_handler -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil]; -} - --(void)handle_interruption:(NSNotification*)pNotification -{ - NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; - switch (type) - { - case AVAudioSessionInterruptionTypeBegan: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n"); - - /* - Core Audio will have stopped the internal device automatically, but we need explicitly - stop it at a higher level to ensure miniaudio-specific state is updated for consistency. - */ - ma_device_stop(m_pDevice); - - /* - Fire the notification after the device has been stopped to ensure it's in the correct - state when the notification handler is invoked. - */ - ma_device__on_notification_interruption_began(m_pDevice); - } break; - - case AVAudioSessionInterruptionTypeEnded: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); - ma_device__on_notification_interruption_ended(m_pDevice); - } break; - } -} - --(void)handle_route_change:(NSNotification*)pNotification -{ - AVAudioSession* pSession = [AVAudioSession sharedInstance]; - - NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; - switch (reason) - { - case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); - } break; - - case AVAudioSessionRouteChangeReasonNewDeviceAvailable: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); - } break; - - case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); - } break; - - case AVAudioSessionRouteChangeReasonWakeFromSleep: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); - } break; - - case AVAudioSessionRouteChangeReasonOverride: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); - } break; - - case AVAudioSessionRouteChangeReasonCategoryChange: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); - } break; - - case AVAudioSessionRouteChangeReasonUnknown: - default: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); - } break; - } - - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); - - /* Let the application know about the route change. */ - ma_device__on_notification_rerouted(m_pDevice); -} -@end -#endif - -static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); - -#if defined(MA_APPLE_DESKTOP) - /* - Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll - just gracefully ignore it. - */ - ma_device__untrack__coreaudio(pDevice); -#endif -#if defined(MA_APPLE_MOBILE) - if (pDevice->coreaudio.pNotificationHandler != NULL) { - ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; - [pNotificationHandler remove_handler]; - } -#endif - - if (pDevice->coreaudio.audioUnitCapture != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - if (pDevice->coreaudio.audioUnitPlayback != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -typedef struct -{ - ma_bool32 allowNominalSampleRateChange; - - /* Input. */ - ma_format formatIn; - ma_uint32 channelsIn; - ma_uint32 sampleRateIn; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesIn; - ma_uint32 periodSizeInMillisecondsIn; - ma_uint32 periodsIn; - ma_share_mode shareMode; - ma_performance_profile performanceProfile; - ma_bool32 registerStopEvent; - - /* Output. */ -#if defined(MA_APPLE_DESKTOP) - AudioObjectID deviceObjectID; -#endif - AudioComponent component; - AudioUnit audioUnit; - AudioBufferList* pAudioBufferList; /* Only used for input devices. */ - ma_format formatOut; - ma_uint32 channelsOut; - ma_uint32 sampleRateOut; - ma_channel channelMapOut[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesOut; - ma_uint32 periodsOut; - char deviceName[256]; -} ma_device_init_internal_data__coreaudio; - -static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ -{ - ma_result result; - OSStatus status; - UInt32 enableIOFlag; - AudioStreamBasicDescription bestFormat; - UInt32 actualPeriodSizeInFrames; - AURenderCallbackStruct callbackInfo; -#if defined(MA_APPLE_DESKTOP) - AudioObjectID deviceObjectID; -#endif - - /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pContext != NULL); - MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture); - -#if defined(MA_APPLE_DESKTOP) - pData->deviceObjectID = 0; -#endif - pData->component = NULL; - pData->audioUnit = NULL; - pData->pAudioBufferList = NULL; - -#if defined(MA_APPLE_DESKTOP) - result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); - if (result != MA_SUCCESS) { - return result; - } - - pData->deviceObjectID = deviceObjectID; -#endif - - /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */ - pData->periodsOut = pData->periodsIn; - if (pData->periodsOut == 0) { - pData->periodsOut = MA_DEFAULT_PERIODS; - } - if (pData->periodsOut > 16) { - pData->periodsOut = 16; - } - - - /* Audio unit. */ - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - - /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */ - enableIOFlag = 1; - if (deviceType == ma_device_type_capture) { - enableIOFlag = 0; - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - enableIOFlag = (enableIOFlag == 0) ? 1 : 0; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - - /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */ -#if defined(MA_APPLE_DESKTOP) - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(result); - } -#else - /* - For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change - the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices. - */ - if (pDeviceID != NULL) { - if (deviceType == ma_device_type_capture) { - ma_bool32 found = MA_FALSE; - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil]; - found = MA_TRUE; - break; - } - } - - if (found == MA_FALSE) { - return MA_DOES_NOT_EXIST; - } - } - } -#endif - - /* - Format. This is the hardest part of initialization because there's a few variables to take into account. - 1) The format must be supported by the device. - 2) The format must be supported miniaudio. - 3) There's a priority that miniaudio prefers. - - Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The - most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same - for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely. - - On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. - */ - { - AudioStreamBasicDescription origFormat; - UInt32 origFormatSize = sizeof(origFormat); - AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; - AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); - } else { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); - } - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - #if defined(MA_APPLE_DESKTOP) - result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat); - if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return result; - } - - /* - Technical Note TN2091: Device input using the HAL Output Audio Unit - https://developer.apple.com/library/archive/technotes/tn2091/_index.html - - This documentation says the following: - - The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY - variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate - conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with - another AudioConverter. - - The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We - therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it - safe and apply the same rule to output as well. - - I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender() - returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but - this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. - - Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with - this, however, is that it actually changes the sample rate at the operating system level and not just the application. This - could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a - configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample - rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run - the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is - changed by miniaudio. - */ - if (pData->allowNominalSampleRateChange) { - AudioValueRange sampleRateRange; - AudioObjectPropertyAddress propAddress; - - sampleRateRange.mMinimum = bestFormat.mSampleRate; - sampleRateRange.mMaximum = bestFormat.mSampleRate; - - propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; - - status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); - if (status != noErr) { - bestFormat.mSampleRate = origFormat.mSampleRate; - } - } else { - bestFormat.mSampleRate = origFormat.mSampleRate; - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); - if (status != noErr) { - /* We failed to set the format, so fall back to the current format of the audio unit. */ - bestFormat = origFormat; - } - #else - bestFormat = origFormat; - - /* - Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try - setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since - it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I - can tell, it looks like the sample rate is shared between playback and capture for everything. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; - bestFormat.mSampleRate = pAudioSession.sampleRate; - - /* - I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with - AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. - */ - if (deviceType == ma_device_type_playback) { - bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; - } - if (deviceType == ma_device_type_capture) { - bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; - } - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - #endif - - result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); - if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return result; - } - - if (pData->formatOut == ma_format_unknown) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return MA_FORMAT_NOT_SUPPORTED; - } - - pData->channelsOut = bestFormat.mChannelsPerFrame; - pData->sampleRateOut = bestFormat.mSampleRate; - } - - /* Clamp the channel count for safety. */ - if (pData->channelsOut > MA_MAX_CHANNELS) { - pData->channelsOut = MA_MAX_CHANNELS; - } - - /* - Internal channel map. This is weird in my testing. If I use the AudioObject to get the - channel map, the channel descriptions are set to "Unknown" for some reason. To work around - this it looks like retrieving it from the AudioUnit will work. However, and this is where - it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore - I'm going to fall back to a default assumption in these cases. - */ -#if defined(MA_APPLE_DESKTOP) - result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut); - if (result != MA_SUCCESS) { - #if 0 - /* Try falling back to the channel map from the AudioObject. */ - result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut); - if (result != MA_SUCCESS) { - return result; - } - #else - /* Fall back to default assumptions. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); - #endif - } -#else - /* TODO: Figure out how to get the channel map using AVAudioSession. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); -#endif - - - /* Buffer size. Not allowing this to be configurable on iOS. */ - if (pData->periodSizeInFramesIn == 0) { - if (pData->periodSizeInMillisecondsIn == 0) { - if (pData->performanceProfile == ma_performance_profile_low_latency) { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut); - } else { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut); - } - } else { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut); - } - } else { - actualPeriodSizeInFrames = pData->periodSizeInFramesIn; - } - -#if defined(MA_APPLE_DESKTOP) - result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames); - if (result != MA_SUCCESS) { - return result; - } -#else - /* - On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point - number. I don't trust any potential truncation errors due to converting from float to integer - so I'm going to explicitly set the actual period size to the next power of 2. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; - actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); - } -#endif - - - /* - During testing I discovered that the buffer size can be too big. You'll get an error like this: - - kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 - - Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that - of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. - */ - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames; - - /* We need a buffer list if this is an input device. We render into this in the input callback. */ - if (deviceType == ma_device_type_capture) { - ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; - AudioBufferList* pBufferList; - - pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); - if (pBufferList == NULL) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return MA_OUT_OF_MEMORY; - } - - pData->pAudioBufferList = pBufferList; - } - - /* Callbacks. */ - callbackInfo.inputProcRefCon = pDevice_DoNotReference; - if (deviceType == ma_device_type_playback) { - callbackInfo.inputProc = ma_on_output__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } else { - callbackInfo.inputProc = ma_on_input__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } - - /* We need to listen for stop events. */ - if (pData->registerStopEvent) { - status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } - - /* Initialize the audio unit. */ - status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); - if (status != noErr) { - ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); - pData->pAudioBufferList = NULL; - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - /* Grab the name. */ -#if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); -#else - if (deviceType == ma_device_type_playback) { - ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - } else { - ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME); - } -#endif - - return result; -} - -#if defined(MA_APPLE_DESKTOP) -static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit) -{ - ma_device_init_internal_data__coreaudio data; - ma_result result; - - /* This should only be called for playback or capture, not duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ - - if (deviceType == ma_device_type_capture) { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; - data.registerStopEvent = MA_TRUE; - - if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - } else if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; - data.registerStopEvent = (pDevice->type != ma_device_type_duplex); - - if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - } - data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->coreaudio.originalPeriods; - - /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) { - data.periodsIn = 3; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - return result; - } - - if (deviceType == ma_device_type_capture) { - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; - } else if (deviceType == ma_device_type_playback) { - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; - } - - return MA_SUCCESS; -} -#endif /* MA_APPLE_DESKTOP */ - -static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with the Core Audio backend for now. */ - if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Capture needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; - data.formatIn = pDescriptorCapture->format; - data.channelsIn = pDescriptorCapture->channels; - data.sampleRateIn = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; - data.periodsIn = pDescriptorCapture->periodCount; - data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.registerStopEvent = MA_TRUE; - - /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { - data.periodsIn = 3; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - return result; - } - - pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; - - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; - - #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - - /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly - switch the device in the background. - */ - if (pConfig->capture.pDeviceID == NULL) { - ma_device__track__coreaudio(pDevice); - } - #endif - } - - /* Playback. */ - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; - data.formatIn = pDescriptorPlayback->format; - data.channelsIn = pDescriptorPlayback->channels; - data.sampleRateIn = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - - /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ - if (pConfig->deviceType == ma_device_type_duplex) { - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodsIn = pDescriptorCapture->periodCount; - data.registerStopEvent = MA_FALSE; - } else { - data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; - data.periodsIn = pDescriptorPlayback->periodCount; - data.registerStopEvent = MA_TRUE; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - } - return result; - } - - pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; - - pDescriptorPlayback->format = data.formatOut; - pDescriptorPlayback->channels = data.channelsOut; - pDescriptorPlayback->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorPlayback->periodCount = data.periodsOut; - - #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - - /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly - switch the device in the background. - */ - if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { - ma_device__track__coreaudio(pDevice); - } - #endif - } - - - - /* - When stopping the device, a callback is called on another thread. We need to wait for this callback - before returning from ma_device_stop(). This event is used for this. - */ - ma_event_init(&pDevice->coreaudio.stopEvent); - - /* - We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done - differently on non-Desktop Apple platforms. - */ -#if defined(MA_APPLE_MOBILE) - pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; -#endif - - return MA_SUCCESS; -} - - -static ma_result ma_device_start__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - return ma_result_from_OSStatus(status); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - /* We need to wait for the callback to finish before returning. */ - ma_event_wait(&pDevice->coreaudio.stopEvent); - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_coreaudio); - -#if defined(MA_APPLE_MOBILE) - if (!pContext->coreaudio.noAudioSessionDeactivate) { - if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); -#endif - -#if !defined(MA_APPLE_MOBILE) - ma_context__uninit_device_tracking__coreaudio(pContext); -#endif - - (void)pContext; - return MA_SUCCESS; -} - -#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) -static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) -{ - /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ - MA_ASSERT(category != ma_ios_session_category_default); - MA_ASSERT(category != ma_ios_session_category_none); - - switch (category) { - case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; - case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; - case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; - case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; - case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; - case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; - default: return AVAudioSessionCategoryAmbient; - } -} -#endif - -static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_APPLE_MOBILE) - ma_result result; -#endif - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_MOBILE) - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions; - - MA_ASSERT(pAudioSession != NULL); - - if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) { - /* - I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails - we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. - */ - #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) - options |= AVAudioSessionCategoryOptionDefaultToSpeaker; - #endif - - if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { - /* Using PlayAndRecord */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { - /* Using Playback */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { - /* Using Record */ - } else { - /* Leave as default? */ - } - } else { - if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { - #if defined(__IPHONE_12_0) - if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { - return MA_INVALID_OPERATION; /* Failed to set session category. */ - } - #else - /* Ignore the session category on version 11 and older, but post a warning. */ - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); - #endif - } - } - - if (!pConfig->coreaudio.noAudioSessionActivate) { - if (![pAudioSession setActive:true error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation"); - if (pContext->coreaudio.hCoreFoundation == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); - pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease"); - - - pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio"); - if (pContext->coreaudio.hCoreAudio == NULL) { - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); - pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); - pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); - pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); - - /* - It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still - defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. - The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to - AudioToolbox. - */ - pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { - /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - } - - pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); - pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender"); -#else - pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; - pContext->coreaudio.CFRelease = (ma_proc)CFRelease; - - #if defined(MA_APPLE_DESKTOP) - pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; - pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; - pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData; - pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; - pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; - #endif - - pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; - pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; - pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; - pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart; - pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop; - pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener; - pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo; - pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty; - pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty; - pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize; - pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender; -#endif - - /* Audio component. */ - { - AudioComponentDescription desc; - desc.componentType = kAudioUnitType_Output; - #if defined(MA_APPLE_DESKTOP) - desc.componentSubType = kAudioUnitSubType_HALOutput; - #else - desc.componentSubType = kAudioUnitSubType_RemoteIO; - #endif - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (pContext->coreaudio.component == NULL) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); - #endif - return MA_FAILED_TO_INIT_BACKEND; - } - } - -#if !defined(MA_APPLE_MOBILE) - result = ma_context__init_device_tracking__coreaudio(pContext); - if (result != MA_SUCCESS) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); - #endif - return result; - } -#endif - - pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; - - pCallbacks->onContextInit = ma_context_init__coreaudio; - pCallbacks->onContextUninit = ma_context_uninit__coreaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio; - pCallbacks->onDeviceInit = ma_device_init__coreaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio; - pCallbacks->onDeviceStart = ma_device_start__coreaudio; - pCallbacks->onDeviceStop = ma_device_stop__coreaudio; - pCallbacks->onDeviceRead = NULL; - pCallbacks->onDeviceWrite = NULL; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* Core Audio */ - - - -/****************************************************************************** - -sndio Backend - -******************************************************************************/ -#ifdef MA_HAS_SNDIO -#include - -/* -Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due -to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device -just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's -demand for it or if I can get it tested and debugged more thoroughly. -*/ -#if 0 -#if defined(__NetBSD__) || defined(__OpenBSD__) -#include -#endif -#if defined(__FreeBSD__) || defined(__DragonFly__) -#include -#endif -#endif - -#define MA_SIO_DEVANY "default" -#define MA_SIO_PLAY 1 -#define MA_SIO_REC 2 -#define MA_SIO_NENC 8 -#define MA_SIO_NCHAN 8 -#define MA_SIO_NRATE 16 -#define MA_SIO_NCONF 4 - -struct ma_sio_hdl; /* <-- Opaque */ - -struct ma_sio_par -{ - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - unsigned int rchan; - unsigned int pchan; - unsigned int rate; - unsigned int bufsz; - unsigned int xrun; - unsigned int round; - unsigned int appbufsz; - int __pad[3]; - unsigned int __magic; -}; - -struct ma_sio_enc -{ - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; -}; - -struct ma_sio_conf -{ - unsigned int enc; - unsigned int rchan; - unsigned int pchan; - unsigned int rate; -}; - -struct ma_sio_cap -{ - struct ma_sio_enc enc[MA_SIO_NENC]; - unsigned int rchan[MA_SIO_NCHAN]; - unsigned int pchan[MA_SIO_NCHAN]; - unsigned int rate[MA_SIO_NRATE]; - int __pad[7]; - unsigned int nconf; - struct ma_sio_conf confs[MA_SIO_NCONF]; -}; - -typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int); -typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*); -typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t); -typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t); -typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*); - -static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */ -{ - ma_uint32 i; - for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) { - if (g_maStandardSampleRatePriorities[i] == sampleRate) { - return i; - } - } - - return (ma_uint32)-1; -} - -static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb) -{ - /* We only support native-endian right now. */ - if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) { - return ma_format_unknown; - } - - if (bits == 8 && bps == 1 && sig == 0) { - return ma_format_u8; - } - if (bits == 16 && bps == 2 && sig == 1) { - return ma_format_s16; - } - if (bits == 24 && bps == 3 && sig == 1) { - return ma_format_s24; - } - if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { - /*return ma_format_s24_32;*/ - } - if (bits == 32 && bps == 4 && sig == 1) { - return ma_format_s32; - } - - return ma_format_unknown; -} - -static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps) -{ - ma_format bestFormat; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - - bestFormat = ma_format_unknown; - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format == ma_format_unknown) { - continue; /* Format not supported. */ - } - - if (bestFormat == ma_format_unknown) { - bestFormat = format; - } else { - if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */ - bestFormat = format; - } - } - } - } - - return bestFormat; -} - -static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat) -{ - ma_uint32 maxChannels; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - MA_ASSERT(requiredFormat != ma_format_unknown); - - /* Just pick whatever configuration has the most channels. */ - maxChannels = 0; - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - /* The encoding should be of requiredFormat. */ - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int iChannel; - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format != requiredFormat) { - continue; - } - - /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - - if (deviceType == ma_device_type_playback) { - chan = caps->confs[iConfig].pchan; - } else { - chan = caps->confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps->pchan[iChannel]; - } else { - channels = caps->rchan[iChannel]; - } - - if (maxChannels < channels) { - maxChannels = channels; - } - } - } - } - - return maxChannels; -} - -static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels) -{ - ma_uint32 firstSampleRate; - ma_uint32 bestSampleRate; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - MA_ASSERT(requiredFormat != ma_format_unknown); - MA_ASSERT(requiredChannels > 0); - MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS); - - firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */ - bestSampleRate = 0; - - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - /* The encoding should be of requiredFormat. */ - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int iChannel; - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format != requiredFormat) { - continue; - } - - /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - unsigned int iRate; - - if (deviceType == ma_device_type_playback) { - chan = caps->confs[iConfig].pchan; - } else { - chan = caps->confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps->pchan[iChannel]; - } else { - channels = caps->rchan[iChannel]; - } - - if (channels != requiredChannels) { - continue; - } - - /* Getting here means we have found a compatible encoding/channel pair. */ - for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { - ma_uint32 rate = (ma_uint32)caps->rate[iRate]; - ma_uint32 ratePriority; - - if (firstSampleRate == 0) { - firstSampleRate = rate; - } - - /* Disregard this rate if it's not a standard one. */ - ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate); - if (ratePriority == (ma_uint32)-1) { - continue; - } - - if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */ - bestSampleRate = rate; - } - } - } - } - } - - /* If a standard sample rate was not found just fall back to the first one that was iterated. */ - if (bestSampleRate == 0) { - bestSampleRate = firstSampleRate; - } - - return bestSampleRate; -} - - -static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 isTerminating = MA_FALSE; - struct ma_sio_hdl* handle; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */ - - /* Playback. */ - if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); - if (handle != NULL) { - /* Supports playback. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY); - ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - } - } - - /* Capture. */ - if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); - if (handle != NULL) { - /* Supports capture. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); - ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME); - - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - char devid[256]; - struct ma_sio_hdl* handle; - struct ma_sio_cap caps; - unsigned int iConfig; - - MA_ASSERT(pContext != NULL); - - /* We need to open the device before we can get information about it. */ - if (pDeviceID == NULL) { - ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY); - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME); - } else { - ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); - } - - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); - if (handle == NULL) { - return MA_NO_DEVICE; - } - - if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { - return MA_ERROR; - } - - pDeviceInfo->nativeDataFormatCount = 0; - - for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) { - /* - The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give - preference to some formats over others. - */ - unsigned int iEncoding; - unsigned int iChannel; - unsigned int iRate; - - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps.enc[iEncoding].bits; - bps = caps.enc[iEncoding].bps; - sig = caps.enc[iEncoding].sig; - le = caps.enc[iEncoding].le; - msb = caps.enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format == ma_format_unknown) { - continue; /* Format not supported. */ - } - - - /* Channels. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - - if (deviceType == ma_device_type_playback) { - chan = caps.confs[iConfig].pchan; - } else { - chan = caps.confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps.pchan[iChannel]; - } else { - channels = caps.rchan[iChannel]; - } - - - /* Sample Rates. */ - for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { - if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0); - } - } - } - } - } - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - const char* pDeviceName; - ma_ptr handle; - int openFlags = 0; - struct ma_sio_cap caps; - struct ma_sio_par par; - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture) { - openFlags = MA_SIO_REC; - } else { - openFlags = MA_SIO_PLAY; - } - - pDeviceID = pDescriptor->pDeviceID; - format = pDescriptor->format; - channels = pDescriptor->channels; - sampleRate = pDescriptor->sampleRate; - - pDeviceName = MA_SIO_DEVANY; - if (pDeviceID != NULL) { - pDeviceName = pDeviceID->sndio; - } - - handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); - if (handle == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* We need to retrieve the device caps to determine the most appropriate format to use. */ - if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); - return MA_ERROR; - } - - /* - Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real - way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this - to the requested channels, regardless of whether or not the default channel count is requested. - - For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the - value returned by ma_find_best_channels_from_sio_cap__sndio(). - */ - if (deviceType == ma_device_type_capture) { - if (format == ma_format_unknown) { - format = ma_find_best_format_from_sio_cap__sndio(&caps); - } - - if (channels == 0) { - if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { - channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); - } else { - channels = MA_DEFAULT_CHANNELS; - } - } - } else { - if (format == ma_format_unknown) { - format = ma_find_best_format_from_sio_cap__sndio(&caps); - } - - if (channels == 0) { - if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { - channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); - } else { - channels = MA_DEFAULT_CHANNELS; - } - } - } - - if (sampleRate == 0) { - sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); - } - - - ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); - par.msb = 0; - par.le = ma_is_little_endian(); - - switch (format) { - case ma_format_u8: - { - par.bits = 8; - par.bps = 1; - par.sig = 0; - } break; - - case ma_format_s24: - { - par.bits = 24; - par.bps = 3; - par.sig = 1; - } break; - - case ma_format_s32: - { - par.bits = 32; - par.bps = 4; - par.sig = 1; - } break; - - case ma_format_s16: - case ma_format_f32: - case ma_format_unknown: - default: - { - par.bits = 16; - par.bps = 2; - par.sig = 1; - } break; - } - - if (deviceType == ma_device_type_capture) { - par.rchan = channels; - } else { - par.pchan = channels; - } - - par.rate = sampleRate; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile); - - par.round = internalPeriodSizeInFrames; - par.appbufsz = par.round * pDescriptor->periodCount; - - if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); - return MA_ERROR; - } - - if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); - return MA_ERROR; - } - - internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); - internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan; - internalSampleRate = par.rate; - internalPeriods = par.appbufsz / par.round; - internalPeriodSizeInFrames = par.round; - - if (deviceType == ma_device_type_capture) { - pDevice->sndio.handleCapture = handle; - } else { - pDevice->sndio.handlePlayback = handle; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->sndio); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - From the documentation: - - The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then - stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the - buffer is drained. In no case are samples in the play buffer discarded. - - Therefore, sio_stop() performs all of the necessary draining for us. - */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int result; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (result == 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); - return MA_IO_ERROR; - } - - if (pFramesWritten != NULL) { - *pFramesWritten = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int result; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (result == 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); - return MA_IO_ERROR; - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__sndio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_sndio); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libsndioNames[] = { - "libsndio.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libsndioNames); ++i) { - pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]); - if (pContext->sndio.sndioSO != NULL) { - break; - } - } - - if (pContext->sndio.sndioSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open"); - pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close"); - pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar"); - pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar"); - pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap"); - pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write"); - pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read"); - pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start"); - pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop"); - pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar"); -#else - pContext->sndio.sio_open = sio_open; - pContext->sndio.sio_close = sio_close; - pContext->sndio.sio_setpar = sio_setpar; - pContext->sndio.sio_getpar = sio_getpar; - pContext->sndio.sio_getcap = sio_getcap; - pContext->sndio.sio_write = sio_write; - pContext->sndio.sio_read = sio_read; - pContext->sndio.sio_start = sio_start; - pContext->sndio.sio_stop = sio_stop; - pContext->sndio.sio_initpar = sio_initpar; -#endif - - pCallbacks->onContextInit = ma_context_init__sndio; - pCallbacks->onContextUninit = ma_context_uninit__sndio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio; - pCallbacks->onDeviceInit = ma_device_init__sndio; - pCallbacks->onDeviceUninit = ma_device_uninit__sndio; - pCallbacks->onDeviceStart = ma_device_start__sndio; - pCallbacks->onDeviceStop = ma_device_stop__sndio; - pCallbacks->onDeviceRead = ma_device_read__sndio; - pCallbacks->onDeviceWrite = ma_device_write__sndio; - pCallbacks->onDeviceDataLoop = NULL; - - (void)pConfig; - return MA_SUCCESS; -} -#endif /* sndio */ - - - -/****************************************************************************** - -audio(4) Backend - -******************************************************************************/ -#ifdef MA_HAS_AUDIO4 -#include -#include -#include -#include -#include -#include -#include - -#if defined(__OpenBSD__) - #include - #if defined(OpenBSD) && OpenBSD >= 201709 - #define MA_AUDIO4_USE_NEW_API - #endif -#endif - -static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) -{ - size_t baseLen; - - MA_ASSERT(id != NULL); - MA_ASSERT(idSize > 0); - MA_ASSERT(deviceIndex >= 0); - - baseLen = strlen(base); - MA_ASSERT(idSize > baseLen); - - ma_strcpy_s(id, idSize, base); - ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); -} - -static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut) -{ - size_t idLen; - size_t baseLen; - const char* deviceIndexStr; - - MA_ASSERT(id != NULL); - MA_ASSERT(base != NULL); - MA_ASSERT(pIndexOut != NULL); - - idLen = strlen(id); - baseLen = strlen(base); - if (idLen <= baseLen) { - return MA_ERROR; /* Doesn't look like the id starts with the base. */ - } - - if (strncmp(id, base, baseLen) != 0) { - return MA_ERROR; /* ID does not begin with base. */ - } - - deviceIndexStr = id + baseLen; - if (deviceIndexStr[0] == '\0') { - return MA_ERROR; /* No index specified in the ID. */ - } - - if (pIndexOut) { - *pIndexOut = atoi(deviceIndexStr); - } - - return MA_SUCCESS; -} - - -#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ -static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision) -{ - if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) { - return ma_format_u8; - } else { - if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { - if (precision == 16) { - return ma_format_s16; - } else if (precision == 24) { - return ma_format_s24; - } else if (precision == 32) { - return ma_format_s32; - } - } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { - if (precision == 16) { - return ma_format_s16; - } else if (precision == 24) { - return ma_format_s24; - } else if (precision == 32) { - return ma_format_s32; - } - } - } - - return ma_format_unknown; /* Encoding not supported. */ -} - -static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision) -{ - MA_ASSERT(pEncoding != NULL); - MA_ASSERT(pPrecision != NULL); - - switch (format) - { - case ma_format_u8: - { - *pEncoding = AUDIO_ENCODING_ULINEAR; - *pPrecision = 8; - } break; - - case ma_format_s24: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 24; - } break; - - case ma_format_s32: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 32; - } break; - - case ma_format_s16: - case ma_format_f32: - case ma_format_unknown: - default: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 16; - } break; - } -} - -static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo) -{ - return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); -} - -static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat) -{ - audio_encoding_t encoding; - ma_uint32 iFormat; - int counter = 0; - - /* First check to see if the preferred format is supported. */ - if (preferredFormat != ma_format_unknown) { - counter = 0; - for (;;) { - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { - return preferredFormat; /* Found the preferred format. */ - } - - /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ - counter += 1; - } - } - - /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */ - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { - ma_format format = g_maFormatPriorities[iFormat]; - - counter = 0; - for (;;) { - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { - return format; /* Found a workable format. */ - } - - /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ - counter += 1; - } - } - - /* Getting here means not appropriate format was found. */ - return ma_format_unknown; -} -#else -static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par) -{ - if (par->bits == 8 && par->bps == 1 && par->sig == 0) { - return ma_format_u8; - } - if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_s16; - } - if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_s24; - } - if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_f32; - } - - /* Format not supported. */ - return ma_format_unknown; -} -#endif - -static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo) -{ - audio_device_t fdDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(fd >= 0); - MA_ASSERT(pDeviceInfo != NULL); - - (void)pContext; - (void)deviceType; - - if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { - return MA_ERROR; /* Failed to retrieve device info. */ - } - - /* Name. */ - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name); - - #if !defined(MA_AUDIO4_USE_NEW_API) - { - audio_info_t fdInfo; - int counter = 0; - ma_uint32 channels; - ma_uint32 sampleRate; - - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - return MA_ERROR; - } - - if (deviceType == ma_device_type_playback) { - channels = fdInfo.play.channels; - sampleRate = fdInfo.play.sample_rate; - } else { - channels = fdInfo.record.channels; - sampleRate = fdInfo.record.sample_rate; - } - - /* Supported formats. We get this by looking at the encodings. */ - pDeviceInfo->nativeDataFormatCount = 0; - for (;;) { - audio_encoding_t encoding; - ma_format format; - - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision); - if (format != ma_format_unknown) { - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); - } - - counter += 1; - } - } - #else - { - struct audio_swpar fdPar; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - return MA_ERROR; - } - - format = ma_format_from_swpar__audio4(&fdPar); - if (format == ma_format_unknown) { - return MA_FORMAT_NOT_SUPPORTED; - } - - if (deviceType == ma_device_type_playback) { - channels = fdPar.pchan; - } else { - channels = fdPar.rchan; - } - - sampleRate = fdPar.rate; - - pDeviceInfo->nativeDataFormatCount = 0; - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); - } - #endif - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - const int maxDevices = 64; - char devpath[256]; - int iDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* - Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN" - version here since we can open it even when another process has control of the "/dev/audioN" device. - */ - for (iDevice = 0; iDevice < maxDevices; ++iDevice) { - struct stat st; - int fd; - ma_bool32 isTerminating = MA_FALSE; - - ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); - ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); - - if (stat(devpath, &st) < 0) { - break; - } - - /* The device exists, but we need to check if it's usable as playback and/or capture. */ - - /* Playback. */ - if (!isTerminating) { - fd = open(devpath, O_RDONLY, 0); - if (fd >= 0) { - /* Supports playback. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); - if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) { - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - close(fd); - } - } - - /* Capture. */ - if (!isTerminating) { - fd = open(devpath, O_WRONLY, 0); - if (fd >= 0) { - /* Supports capture. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); - if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) { - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - close(fd); - } - } - - if (isTerminating) { - break; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - int fd = -1; - int deviceIndex = -1; - char ctlid[256]; - ma_result result; - - MA_ASSERT(pContext != NULL); - - /* - We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number - from the device ID which will be in "/dev/audioN" format. - */ - if (pDeviceID == NULL) { - /* Default device. */ - ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); - } else { - /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */ - result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); - if (result != MA_SUCCESS) { - return result; - } - - ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); - } - - fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0); - if (fd == -1) { - return MA_NO_DEVICE; - } - - if (deviceIndex == -1) { - ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); - } else { - ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); - } - - result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); - - close(fd); - return result; -} - -static ma_result ma_device_uninit__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - const char* pDefaultDeviceNames[] = { - "/dev/audio", - "/dev/audio0" - }; - int fd; - int fdFlags = 0; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is open the file. */ - if (deviceType == ma_device_type_capture) { - fdFlags = O_RDONLY; - } else { - fdFlags = O_WRONLY; - } - /*fdFlags |= O_NONBLOCK;*/ - - if (pDescriptor->pDeviceID == NULL) { - /* Default device. */ - size_t iDevice; - for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) { - fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0); - if (fd != -1) { - break; - } - } - } else { - /* Specific device. */ - fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); - } - - if (fd == -1) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device."); - return ma_result_from_errno(errno); - } - - #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ - { - audio_info_t fdInfo; - - /* - The documentation is a little bit unclear to me as to how it handles formats. It says the - following: - - Regardless of formats supported by underlying driver, the audio driver accepts the - following formats. - - By then the next sentence says this: - - `encoding` and `precision` are one of the values obtained by AUDIO_GETENC. - - It sounds like a direct contradiction to me. I'm going to play this safe any only use the - best sample format returned by AUDIO_GETENC. If the requested format is supported we'll - use that, but otherwise we'll just use our standard format priorities to pick an - appropriate one. - */ - AUDIO_INITINFO(&fdInfo); - - /* We get the driver to do as much of the data conversion as possible. */ - if (deviceType == ma_device_type_capture) { - fdInfo.mode = AUMODE_RECORD; - ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision); - - if (pDescriptor->channels != 0) { - fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ - } - - if (pDescriptor->sampleRate != 0) { - fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ - } - } else { - fdInfo.mode = AUMODE_PLAY; - ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision); - - if (pDescriptor->channels != 0) { - fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ - } - - if (pDescriptor->sampleRate != 0) { - fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ - } - } - - if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed."); - return ma_result_from_errno(errno); - } - - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); - return ma_result_from_errno(errno); - } - - if (deviceType == ma_device_type_capture) { - internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record); - internalChannels = fdInfo.record.channels; - internalSampleRate = fdInfo.record.sample_rate; - } else { - internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play); - internalChannels = fdInfo.play.channels; - internalSampleRate = fdInfo.play.sample_rate; - } - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Buffer. */ - { - ma_uint32 internalPeriodSizeInBytes; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); - - internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (internalPeriodSizeInBytes < 16) { - internalPeriodSizeInBytes = 16; - } - - internalPeriods = pDescriptor->periodCount; - if (internalPeriods < 2) { - internalPeriods = 2; - } - - /* What miniaudio calls a period, audio4 calls a block. */ - AUDIO_INITINFO(&fdInfo); - fdInfo.hiwat = internalPeriods; - fdInfo.lowat = internalPeriods-1; - fdInfo.blocksize = internalPeriodSizeInBytes; - if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed."); - return ma_result_from_errno(errno); - } - - internalPeriods = fdInfo.hiwat; - internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels); - } - } - #else - { - struct audio_swpar fdPar; - - /* We need to retrieve the format of the device so we can know the channel count and sample rate. Then we can calculate the buffer size. */ - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters."); - return ma_result_from_errno(errno); - } - - internalFormat = ma_format_from_swpar__audio4(&fdPar); - internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; - internalSampleRate = fdPar.rate; - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Buffer. */ - { - ma_uint32 internalPeriodSizeInBytes; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); - - /* What miniaudio calls a period, audio4 calls a block. */ - internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (internalPeriodSizeInBytes < 16) { - internalPeriodSizeInBytes = 16; - } - - fdPar.nblks = pDescriptor->periodCount; - fdPar.round = internalPeriodSizeInBytes; - - if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters."); - return ma_result_from_errno(errno); - } - - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters."); - return ma_result_from_errno(errno); - } - } - - internalFormat = ma_format_from_swpar__audio4(&fdPar); - internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; - internalSampleRate = fdPar.rate; - internalPeriods = fdPar.nblks; - internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels); - } - #endif - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - if (deviceType == ma_device_type_capture) { - pDevice->audio4.fdCapture = fd; - } else { - pDevice->audio4.fdPlayback = fd; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->audio4); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - pDevice->audio4.fdCapture = -1; - pDevice->audio4.fdPlayback = -1; - - /* - The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD - introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as - I'm aware. - */ -#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000 - /* NetBSD 8.0+ */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } -#else - /* All other flavors. */ -#endif - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdCapture == -1) { - return MA_INVALID_ARGS; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdPlayback == -1) { - return MA_INVALID_ARGS; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) -{ - if (fd == -1) { - return MA_INVALID_ARGS; - } - -#if !defined(MA_AUDIO4_USE_NEW_API) - if (ioctl(fd, AUDIO_FLUSH, 0) < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed."); - return ma_result_from_errno(errno); - } -#else - if (ioctl(fd, AUDIO_STOP, 0) < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed."); - return ma_result_from_errno(errno); - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result; - - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result; - - /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */ - #if !defined(MA_AUDIO4_USE_NEW_API) - ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0); - #endif - - /* Here is where the device is stopped immediately. */ - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int result; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (result < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); - return ma_result_from_errno(errno); - } - - if (pFramesWritten != NULL) { - *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int result; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (result < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); - return ma_result_from_errno(errno); - } - - if (pFramesRead != NULL) { - *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__audio4(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_audio4); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pCallbacks->onContextInit = ma_context_init__audio4; - pCallbacks->onContextUninit = ma_context_uninit__audio4; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4; - pCallbacks->onDeviceInit = ma_device_init__audio4; - pCallbacks->onDeviceUninit = ma_device_uninit__audio4; - pCallbacks->onDeviceStart = ma_device_start__audio4; - pCallbacks->onDeviceStop = ma_device_stop__audio4; - pCallbacks->onDeviceRead = ma_device_read__audio4; - pCallbacks->onDeviceWrite = ma_device_write__audio4; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* audio4 */ - - -/****************************************************************************** - -OSS Backend - -******************************************************************************/ -#ifdef MA_HAS_OSS -#include -#include -#include -#include - -#ifndef SNDCTL_DSP_HALT -#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET -#endif - -#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" - -static int ma_open_temp_device__oss() -{ - /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ - int fd = open("/dev/mixer", O_RDONLY, 0); - if (fd >= 0) { - return fd; - } - - return -1; -} - -static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd) -{ - const char* deviceName; - int flags; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pfd != NULL); - (void)pContext; - - *pfd = -1; - - /* This function should only be called for playback or capture, not duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - deviceName = MA_OSS_DEFAULT_DEVICE_NAME; - if (pDeviceID != NULL) { - deviceName = pDeviceID->oss; - } - - flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY; - if (shareMode == ma_share_mode_exclusive) { - flags |= O_EXCL; - } - - *pfd = open(deviceName, flags, 0); - if (*pfd == -1) { - return ma_result_from_errno(errno); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - int fd; - oss_sysinfo si; - int result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); - return MA_NO_BACKEND; - } - - result = ioctl(fd, SNDCTL_SYSINFO, &si); - if (result != -1) { - int iAudioDevice; - for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { - oss_audioinfo ai; - ai.dev = iAudioDevice; - result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); - if (result != -1) { - if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */ - ma_device_info deviceInfo; - ma_bool32 isTerminating = MA_FALSE; - - MA_ZERO_OBJECT(&deviceInfo); - - /* ID */ - ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1); - - /* - The human readable device name should be in the "ai.handle" variable, but it can - sometimes be empty in which case we just fall back to "ai.name" which is less user - friendly, but usually has a value. - */ - if (ai.handle[0] != '\0') { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1); - } else { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1); - } - - /* The device can be both playback and capture. */ - if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) { - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) { - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - if (isTerminating) { - break; - } - } - } - } - } else { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); - return MA_NO_BACKEND; - } - - close(fd); - return MA_SUCCESS; -} - -static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo) -{ - unsigned int minChannels; - unsigned int maxChannels; - unsigned int iRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pAudioInfo != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* If we support all channels we just report 0. */ - minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - - /* - OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness, - which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which - case we'll need to use min_rate and max_rate and report only standard rates. - */ - if (pAudioInfo->nrates > 0) { - for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) { - unsigned int rate = pAudioInfo->rates[iRate]; - - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ - } else { - unsigned int iChannel; - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0); - } - } - } - } else { - for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate]; - - if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) { - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ - } else { - unsigned int iChannel; - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0); - } - } - } - } - } -} - -static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_bool32 foundDevice; - int fdTemp; - oss_sysinfo si; - int result; - - MA_ASSERT(pContext != NULL); - - /* Handle the default device a little differently. */ - if (pDeviceID == NULL) { - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - return MA_SUCCESS; - } - - - /* If we get here it means we are _not_ using the default device. */ - foundDevice = MA_FALSE; - - fdTemp = ma_open_temp_device__oss(); - if (fdTemp == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); - return MA_NO_BACKEND; - } - - result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); - if (result != -1) { - int iAudioDevice; - for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { - oss_audioinfo ai; - ai.dev = iAudioDevice; - result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai); - if (result != -1) { - if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) { - /* It has the same name, so now just confirm the type. */ - if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) || - (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) { - unsigned int formatMask; - - /* ID */ - ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1); - - /* - The human readable device name should be in the "ai.handle" variable, but it can - sometimes be empty in which case we just fall back to "ai.name" which is less user - friendly, but usually has a value. - */ - if (ai.handle[0] != '\0') { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1); - } - - - pDeviceInfo->nativeDataFormatCount = 0; - - if (deviceType == ma_device_type_playback) { - formatMask = ai.oformats; - } else { - formatMask = ai.iformats; - } - - if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo); - } - if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo); - } - if ((formatMask & AFMT_U8) != 0) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo); - } - - foundDevice = MA_TRUE; - break; - } - } - } - } - } else { - close(fdTemp); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); - return MA_NO_BACKEND; - } - - - close(fdTemp); - - if (!foundDevice) { - return MA_NO_DEVICE; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdPlayback); - } - - return MA_SUCCESS; -} - -static int ma_format_to_oss(ma_format format) -{ - int ossFormat = AFMT_U8; - switch (format) { - case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; - case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; - case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; - case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; - case ma_format_u8: - default: ossFormat = AFMT_U8; break; - } - - return ossFormat; -} - -static ma_format ma_format_from_oss(int ossFormat) -{ - if (ossFormat == AFMT_U8) { - return ma_format_u8; - } else { - if (ma_is_little_endian()) { - switch (ossFormat) { - case AFMT_S16_LE: return ma_format_s16; - case AFMT_S32_LE: return ma_format_s32; - default: return ma_format_unknown; - } - } else { - switch (ossFormat) { - case AFMT_S16_BE: return ma_format_s16; - case AFMT_S32_BE: return ma_format_s32; - default: return ma_format_unknown; - } - } - } - - return ma_format_unknown; -} - -static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - ma_result result; - int ossResult; - int fd; - const ma_device_id* pDeviceID = NULL; - ma_share_mode shareMode; - int ossFormat; - int ossChannels; - int ossSampleRate; - int ossFragment; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - - pDeviceID = pDescriptor->pDeviceID; - shareMode = pDescriptor->shareMode; - ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */ - ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; - ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - - /* - The OSS documantation is very clear about the order we should be initializing the device's properties: - 1) Format - 2) Channels - 3) Sample rate. - */ - - /* Format. */ - ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format."); - return ma_result_from_errno(errno); - } - - /* Channels. */ - ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count."); - return ma_result_from_errno(errno); - } - - /* Sample Rate. */ - ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate."); - return ma_result_from_errno(errno); - } - - /* - Buffer. - - The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if - it should be done before or after format/channels/rate. - - OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual - value. - */ - { - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInBytes; - ma_uint32 ossFragmentSizePower; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile); - - periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels)); - if (periodSizeInBytes < 16) { - periodSizeInBytes = 16; - } - - ossFragmentSizePower = 4; - periodSizeInBytes >>= 4; - while (periodSizeInBytes >>= 1) { - ossFragmentSizePower += 1; - } - - ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower); - ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count."); - return ma_result_from_errno(errno); - } - } - - /* Internal settings. */ - if (deviceType == ma_device_type_capture) { - pDevice->oss.fdCapture = fd; - } else { - pDevice->oss.fdPlayback = fd; - } - - pDescriptor->format = ma_format_from_oss(ossFormat); - pDescriptor->channels = ossChannels; - pDescriptor->sampleRate = ossSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); - pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16); - pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); - - if (pDescriptor->format == ma_format_unknown) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); - return MA_FORMAT_NOT_SUPPORTED; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - - MA_ZERO_OBJECT(&pDevice->oss); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - } - - return MA_SUCCESS; -} - -/* -Note on Starting and Stopping -============================= -In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when -trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will -fail. Instead what we need to do is just not write or read to and from the device when the -device is not running. - -As a result, both the start and stop functions for OSS are just empty stubs. The starting and -stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check -the device state, and if the device is stopped they will simply not do any kind of processing. - -The downside to this technique is that I've noticed a fairly lengthy delay in stopping the -device, up to a second. This is on a virtual machine, and as such might just be due to the -virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for -the moment that's just how it's going to have to be. - -When starting the device, OSS will automatically start it when write() or read() is called. -*/ -static ma_result ma_device_start__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* The device is automatically started with reading and writing. */ - (void)pDevice; - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* See note above on why this is empty. */ - (void)pDevice; - - return MA_SUCCESS; -} - -static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int resultOSS; - ma_uint32 deviceState; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - /* Don't do any processing if the device is stopped. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { - return MA_SUCCESS; - } - - resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (resultOSS < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); - return ma_result_from_errno(errno); - } - - if (pFramesWritten != NULL) { - *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int resultOSS; - ma_uint32 deviceState; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - /* Don't do any processing if the device is stopped. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { - return MA_SUCCESS; - } - - resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (resultOSS < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); - return ma_result_from_errno(errno); - } - - if (pFramesRead != NULL) { - *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__oss(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_oss); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int fd; - int ossVersion; - int result; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - /* Try opening a temporary device first so we can get version information. This is closed at the end. */ - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ - return MA_NO_BACKEND; - } - - /* Grab the OSS version. */ - ossVersion = 0; - result = ioctl(fd, OSS_GETVERSION, &ossVersion); - if (result == -1) { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); - return MA_NO_BACKEND; - } - - /* The file handle to temp device is no longer needed. Close ASAP. */ - close(fd); - - pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); - pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); - - pCallbacks->onContextInit = ma_context_init__oss; - pCallbacks->onContextUninit = ma_context_uninit__oss; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss; - pCallbacks->onDeviceInit = ma_device_init__oss; - pCallbacks->onDeviceUninit = ma_device_uninit__oss; - pCallbacks->onDeviceStart = ma_device_start__oss; - pCallbacks->onDeviceStop = ma_device_stop__oss; - pCallbacks->onDeviceRead = ma_device_read__oss; - pCallbacks->onDeviceWrite = ma_device_write__oss; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* OSS */ - - -/****************************************************************************** - -AAudio Backend - -******************************************************************************/ -#ifdef MA_HAS_AAUDIO - -/*#include */ - -typedef int32_t ma_aaudio_result_t; -typedef int32_t ma_aaudio_direction_t; -typedef int32_t ma_aaudio_sharing_mode_t; -typedef int32_t ma_aaudio_format_t; -typedef int32_t ma_aaudio_stream_state_t; -typedef int32_t ma_aaudio_performance_mode_t; -typedef int32_t ma_aaudio_usage_t; -typedef int32_t ma_aaudio_content_type_t; -typedef int32_t ma_aaudio_input_preset_t; -typedef int32_t ma_aaudio_data_callback_result_t; -typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; -typedef struct ma_AAudioStream_t* ma_AAudioStream; - -#define MA_AAUDIO_UNSPECIFIED 0 - -/* Result codes. miniaudio only cares about the success code. */ -#define MA_AAUDIO_OK 0 - -/* Directions. */ -#define MA_AAUDIO_DIRECTION_OUTPUT 0 -#define MA_AAUDIO_DIRECTION_INPUT 1 - -/* Sharing modes. */ -#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 -#define MA_AAUDIO_SHARING_MODE_SHARED 1 - -/* Formats. */ -#define MA_AAUDIO_FORMAT_PCM_I16 1 -#define MA_AAUDIO_FORMAT_PCM_FLOAT 2 - -/* Stream states. */ -#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 -#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 -#define MA_AAUDIO_STREAM_STATE_OPEN 2 -#define MA_AAUDIO_STREAM_STATE_STARTING 3 -#define MA_AAUDIO_STREAM_STATE_STARTED 4 -#define MA_AAUDIO_STREAM_STATE_PAUSING 5 -#define MA_AAUDIO_STREAM_STATE_PAUSED 6 -#define MA_AAUDIO_STREAM_STATE_FLUSHING 7 -#define MA_AAUDIO_STREAM_STATE_FLUSHED 8 -#define MA_AAUDIO_STREAM_STATE_STOPPING 9 -#define MA_AAUDIO_STREAM_STATE_STOPPED 10 -#define MA_AAUDIO_STREAM_STATE_CLOSING 11 -#define MA_AAUDIO_STREAM_STATE_CLOSED 12 -#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 - -/* Performance modes. */ -#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 -#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 -#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 - -/* Usage types. */ -#define MA_AAUDIO_USAGE_MEDIA 1 -#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2 -#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3 -#define MA_AAUDIO_USAGE_ALARM 4 -#define MA_AAUDIO_USAGE_NOTIFICATION 5 -#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6 -#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10 -#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11 -#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12 -#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13 -#define MA_AAUDIO_USAGE_GAME 14 -#define MA_AAUDIO_USAGE_ASSISTANT 16 -#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000 -#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001 -#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002 -#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003 - -/* Content types. */ -#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1 -#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2 -#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3 -#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4 - -/* Input presets. */ -#define MA_AAUDIO_INPUT_PRESET_GENERIC 1 -#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5 -#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6 -#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7 -#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 -#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 - -/* Callback results. */ -#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 -#define MA_AAUDIO_CALLBACK_RESULT_STOP 1 - - -typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); -typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); - -typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder); -typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); -typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); -typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); -typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); -typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); -typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); -typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); -typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); -typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); -typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream); - -static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) -{ - switch (resultAA) - { - case MA_AAUDIO_OK: return MA_SUCCESS; - default: break; - } - - return MA_ERROR; -} - -static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) -{ - switch (usage) { - case ma_aaudio_usage_announcement: return MA_AAUDIO_USAGE_MEDIA; - case ma_aaudio_usage_emergency: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; - case ma_aaudio_usage_safety: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_USAGE_ALARM; - case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_NOTIFICATION; - case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; - case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; - case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; - case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_GAME; - case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_ASSISTANT; - case ma_aaudio_usage_notification_event: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; - case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; - case ma_aaudio_usage_voice_communication: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; - case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; - default: break; - } - - return MA_AAUDIO_USAGE_MEDIA; -} - -static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) -{ - switch (contentType) { - case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; - case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; - case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; - case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; - default: break; - } - - return MA_AAUDIO_CONTENT_TYPE_SPEECH; -} - -static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset) -{ - switch (inputPreset) { - case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; - case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; - case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; - case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; - case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; - case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; - default: break; - } - - return MA_AAUDIO_INPUT_PRESET_GENERIC; -} - -static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - (void)error; - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); - - /* - From the documentation for AAudio, when a device is disconnected all we can do is stop it. However, we cannot stop it from the callback - we need - to do it from another thread. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely. - */ - if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { - /* We need to post a job to the job thread for processing. This will reroute the device by reinitializing the stream. */ - ma_result result; - ma_job job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); - job.data.device.aaudio.reroute.pDevice = pDevice; - - if (pStream == pDevice->aaudio.pStreamCapture) { - job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; - } else { - job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; - } - - result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); - return; - } - } -} - -static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount); - - (void)pStream; - return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount); - - (void)pStream; - return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) -{ - ma_AAudioStreamBuilder* pBuilder; - ma_aaudio_result_t resultAA; - ma_uint32 bufferCapacityInFrames; - - /* Safety. */ - *ppBuilder = NULL; - - resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - if (pDeviceID != NULL) { - ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); - } - - ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); - ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); - - - /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */ - if (pDescriptor != NULL) { - MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */ - - if (pDescriptor->sampleRate != 0) { - ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); - } - - if (deviceType == ma_device_type_capture) { - if (pDescriptor->channels != 0) { - ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); - } - if (pDescriptor->format != ma_format_unknown) { - ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); - } - } else { - if (pDescriptor->channels != 0) { - ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); - } - if (pDescriptor->format != ma_format_unknown) { - ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); - } - } - - /* - AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you - retrieve the actual sample rate until after you've opened the stream. But you need to configure - the buffer capacity before you open the stream... :/ - - To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. - */ - bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; - - ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); - ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); - - if (deviceType == ma_device_type_capture) { - if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { - ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); - } - - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); - } else { - if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) { - ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage)); - } - - if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) { - ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); - } - - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); - } - - /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); - - /* We need to set an error callback to detect device changes. */ - if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */ - ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); - } - } - - *ppBuilder = pBuilder; - - return MA_SUCCESS; -} - -static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream) -{ - ma_result result; - - result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream)); - ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); - - return result; -} - -static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream) -{ - ma_result result; - ma_AAudioStreamBuilder* pBuilder; - - *ppStream = NULL; - - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); -} - -static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) -{ - ma_result result; - ma_AAudioStreamBuilder* pBuilder; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pDescriptor != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ - - *ppStream = NULL; - - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream); -} - -static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) -{ - return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); -} - -static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType) -{ - /* The only way to know this is to try creating a stream. */ - ma_AAudioStream* pStream; - ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - ma_close_stream__aaudio(pContext, pStream); - return MA_TRUE; -} - -static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState) -{ - ma_aaudio_stream_state_t actualNewState; - ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - if (newState != actualNewState) { - return MA_ERROR; /* Failed to transition into the expected state. */ - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */ - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - - if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) { - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - - if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) { - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } - - return MA_SUCCESS; -} - -static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pStream != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; -} - -static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - /* AAudio supports s16 and f32. */ - ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo); - ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo); -} - -static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream; - ma_result result; - - MA_ASSERT(pContext != NULL); - - /* ID */ - if (pDeviceID != NULL) { - pDeviceInfo->id.aaudio = pDeviceID->aaudio; - } else { - pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED; - } - - /* Name */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - - pDeviceInfo->nativeDataFormatCount = 0; - - /* We'll need to open the device to get accurate sample rate and channel count information. */ - result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream); - if (result != MA_SUCCESS) { - return result; - } - - ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo); - - ma_close_stream__aaudio(pContext, pStream); - pStream = NULL; - - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - pDevice->aaudio.pStreamCapture = NULL; - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - pDevice->aaudio.pStreamPlayback = NULL; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) -{ - ma_result result; - int32_t bufferCapacityInFrames; - int32_t framesPerDataCallback; - ma_AAudioStream* pStream; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDescriptor != NULL); - - *ppStream = NULL; /* Safety. */ - - /* First step is to open the stream. From there we'll be able to extract the internal configuration. */ - result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream); - if (result != MA_SUCCESS) { - return result; /* Failed to open the AAudio stream. */ - } - - /* Now extract the internal configuration. */ - pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; - pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream); - - /* For the channel map we need to be sure we don't overflow any buffers. */ - if (pDescriptor->channels <= MA_MAX_CHANNELS) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */ - } else { - ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ - } - - bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); - framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream); - - if (framesPerDataCallback > 0) { - pDescriptor->periodSizeInFrames = framesPerDataCallback; - pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback; - } else { - pDescriptor->periodSizeInFrames = bufferCapacityInFrames; - pDescriptor->periodCount = 1; - } - - *ppStream = pStream; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - pDevice->aaudio.usage = pConfig->aaudio.usage; - pDevice->aaudio.contentType = pConfig->aaudio.contentType; - pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; - pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) -{ - ma_aaudio_result_t resultAA; - ma_aaudio_stream_state_t currentState; - - MA_ASSERT(pDevice != NULL); - - resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - /* Do we actually need to wait for the device to transition into it's started state? */ - - /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) { - ma_result result; - - if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) { - return MA_ERROR; /* Expecting the stream to be a starting or started state. */ - } - - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) -{ - ma_aaudio_result_t resultAA; - ma_aaudio_stream_state_t currentState; - - MA_ASSERT(pDevice != NULL); - - /* - From the AAudio documentation: - - The stream will stop after all of the data currently buffered has been played. - - This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. - */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { - return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ - } - - resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) { - ma_result result; - - if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) { - return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */ - } - - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - if (pDevice->type == ma_device_type_duplex) { - ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is close the streams. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - pDevice->aaudio.pStreamCapture = NULL; - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - pDevice->aaudio.pStreamPlayback = NULL; - } - - /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ - { - ma_device_config deviceConfig; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - deviceConfig = ma_device_config_init(deviceType); - deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.playback.shareMode = pDevice->playback.shareMode; - deviceConfig.playback.format = pDevice->playback.format; - deviceConfig.playback.channels = pDevice->playback.channels; - deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.capture.shareMode = pDevice->capture.shareMode; - deviceConfig.capture.format = pDevice->capture.format; - deviceConfig.capture.channels = pDevice->capture.channels; - deviceConfig.sampleRate = pDevice->sampleRate; - deviceConfig.aaudio.usage = pDevice->aaudio.usage; - deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; - deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; - deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; - deviceConfig.periods = 1; - - /* Try to get an accurate period size. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - } else { - deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - } - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; - descriptorCapture.shareMode = deviceConfig.capture.shareMode; - descriptorCapture.format = deviceConfig.capture.format; - descriptorCapture.channels = deviceConfig.capture.channels; - descriptorCapture.sampleRate = deviceConfig.sampleRate; - descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorCapture.periodCount = deviceConfig.periods; - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; - descriptorPlayback.shareMode = deviceConfig.playback.shareMode; - descriptorPlayback.format = deviceConfig.playback.format; - descriptorPlayback.channels = deviceConfig.playback.channels; - descriptorPlayback.sampleRate = deviceConfig.sampleRate; - descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorPlayback.periodCount = deviceConfig.periods; - } - - result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_device_uninit__aaudio(pDevice); - return result; - } - - /* We'll only ever do this in response to a reroute. */ - ma_device__on_notification_rerouted(pDevice); - - /* If the device is started, start the streams. Maybe make this configurable? */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { - ma_device_start__aaudio(pDevice); - } else { - ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ - } - } - - return MA_SUCCESS; - } -} - -static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream = NULL; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(type != ma_device_type_duplex); - MA_ASSERT(pDeviceInfo != NULL); - - if (type == ma_device_type_playback) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; - pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - if (type == ma_device_type_capture) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; - pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - - /* Safety. Should never happen. */ - if (pStream == NULL) { - return MA_INVALID_OPERATION; - } - - pDeviceInfo->nativeDataFormatCount = 0; - ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__aaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_aaudio); - - ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); - - ma_dlclose(pContext, pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - - return MA_SUCCESS; -} - -static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - size_t i; - const char* libNames[] = { - "libaaudio.so" - }; - - for (i = 0; i < ma_countof(libNames); ++i) { - pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]); - if (pContext->aaudio.hAAudio != NULL) { - break; - } - } - - if (pContext->aaudio.hAAudio == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); - pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close"); - pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState"); - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); - pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat"); - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); - pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart"); - pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop"); - - - pCallbacks->onContextInit = ma_context_init__aaudio; - pCallbacks->onContextUninit = ma_context_uninit__aaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio; - pCallbacks->onDeviceInit = ma_device_init__aaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__aaudio; - pCallbacks->onDeviceStart = ma_device_start__aaudio; - pCallbacks->onDeviceStop = ma_device_stop__aaudio; - pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; - - - /* We need a job thread so we can deal with rerouting. */ - { - ma_result result; - ma_device_job_thread_config jobThreadConfig; - - jobThreadConfig = ma_device_job_thread_config_init(); - - result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); - if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - return result; - } - } - - - (void)pConfig; - return MA_SUCCESS; -} - -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) -{ - ma_device* pDevice; - - MA_ASSERT(pJob != NULL); - - pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; - MA_ASSERT(pDevice != NULL); - - /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ - return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); -} -#else -/* Getting here means there is no AAudio backend so we need a no-op job implementation. */ -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) -{ - return ma_job_process__noop(pJob); -} -#endif /* AAudio */ - - -/****************************************************************************** - -OpenSL|ES Backend - -******************************************************************************/ -#ifdef MA_HAS_OPENSL -#include -#ifdef MA_ANDROID -#include -#endif - -typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired); - -/* OpenSL|ES has one-per-application objects :( */ -static SLObjectItf g_maEngineObjectSL = NULL; -static SLEngineItf g_maEngineSL = NULL; -static ma_uint32 g_maOpenSLInitCounter = 0; -static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */ - -#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p))) -#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) -#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p))) -#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p))) - -#ifdef MA_ANDROID -#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) -#else -#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) -#endif - -static ma_result ma_result_from_OpenSL(SLuint32 result) -{ - switch (result) - { - case SL_RESULT_SUCCESS: return MA_SUCCESS; - case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR; - case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS; - case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY; - case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA; - case SL_RESULT_RESOURCE_LOST: return MA_ERROR; - case SL_RESULT_IO_ERROR: return MA_IO_ERROR; - case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE; - case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA; - case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED; - case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR; - case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED; - case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED; - case SL_RESULT_INTERNAL_ERROR: return MA_ERROR; - case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR; - case SL_RESULT_OPERATION_ABORTED: return MA_ERROR; - case SL_RESULT_CONTROL_LOST: return MA_ERROR; - default: return MA_ERROR; - } -} - -/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ -static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id) -{ - switch (id) - { - case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; - case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; - case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; - case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */ -static SLuint32 ma_channel_id_to_opensl(ma_uint8 id) -{ - switch (id) - { - case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER; - case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; - case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY; - case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT; - case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER; - case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts a channel mapping to an OpenSL-style channel mask. */ -static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels) -{ - SLuint32 channelMask = 0; - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]); - } - - return channelMask; -} - -/* Converts an OpenSL-style channel mask to a miniaudio channel map. */ -static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap) -{ - if (channels == 1 && channelMask == 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else if (channels == 2 && channelMask == 0) { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else { - /* Just iterate over each bit. */ - ma_uint32 iChannel = 0; - ma_uint32 iBit; - for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { - SLuint32 bitValue = (channelMask & (1UL << iBit)); - if (bitValue != 0) { - /* The bit is set. */ - pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue); - iChannel += 1; - } - } - } - } -} - -static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) -{ - if (samplesPerSec <= SL_SAMPLINGRATE_8) { - return SL_SAMPLINGRATE_8; - } - if (samplesPerSec <= SL_SAMPLINGRATE_11_025) { - return SL_SAMPLINGRATE_11_025; - } - if (samplesPerSec <= SL_SAMPLINGRATE_12) { - return SL_SAMPLINGRATE_12; - } - if (samplesPerSec <= SL_SAMPLINGRATE_16) { - return SL_SAMPLINGRATE_16; - } - if (samplesPerSec <= SL_SAMPLINGRATE_22_05) { - return SL_SAMPLINGRATE_22_05; - } - if (samplesPerSec <= SL_SAMPLINGRATE_24) { - return SL_SAMPLINGRATE_24; - } - if (samplesPerSec <= SL_SAMPLINGRATE_32) { - return SL_SAMPLINGRATE_32; - } - if (samplesPerSec <= SL_SAMPLINGRATE_44_1) { - return SL_SAMPLINGRATE_44_1; - } - if (samplesPerSec <= SL_SAMPLINGRATE_48) { - return SL_SAMPLINGRATE_48; - } - - /* Android doesn't support more than 48000. */ -#ifndef MA_ANDROID - if (samplesPerSec <= SL_SAMPLINGRATE_64) { - return SL_SAMPLINGRATE_64; - } - if (samplesPerSec <= SL_SAMPLINGRATE_88_2) { - return SL_SAMPLINGRATE_88_2; - } - if (samplesPerSec <= SL_SAMPLINGRATE_96) { - return SL_SAMPLINGRATE_96; - } - if (samplesPerSec <= SL_SAMPLINGRATE_192) { - return SL_SAMPLINGRATE_192; - } -#endif - - return SL_SAMPLINGRATE_16; -} - - -static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType) -{ - switch (streamType) { - case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE; - case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM; - case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING; - case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA; - case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM; - case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION; - default: break; - } - - return SL_ANDROID_STREAM_VOICE; -} - -static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset) -{ - switch (recordingPreset) { - case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC; - case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER; - case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; - case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; - case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED; - default: break; - } - - return SL_ANDROID_RECORDING_PRESET_NONE; -} - - -static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - /* - TODO: Test Me. - - This is currently untested, so for now we are just returning default devices. - */ -#if 0 && !defined(MA_ANDROID) - ma_bool32 isTerminated = MA_FALSE; - - SLuint32 pDeviceIDs[128]; - SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); - - SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); - if (resultSL != SL_RESULT_SUCCESS) { - /* The interface may not be supported so just report a default device. */ - goto return_default_device; - } - - /* Playback */ - if (!isTerminated) { - resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = pDeviceIDs[iDevice]; - - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); - if (resultSL == SL_RESULT_SUCCESS) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1); - - ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - isTerminated = MA_TRUE; - break; - } - } - } - } - - /* Capture */ - if (!isTerminated) { - resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = pDeviceIDs[iDevice]; - - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); - if (resultSL == SL_RESULT_SUCCESS) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1); - - ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - isTerminated = MA_TRUE; - break; - } - } - } - } - - return MA_SUCCESS; -#else - goto return_default_device; -#endif - -return_default_device:; - cbResult = MA_TRUE; - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - return MA_SUCCESS; -} - -static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; -} - -static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo) -{ - ma_uint32 minChannels = 1; - ma_uint32 maxChannels = 2; - ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000; - ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000; - ma_uint32 iChannel; - ma_uint32 iSampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* - Each sample format can support mono and stereo, and we'll support a small subset of standard - rates (up to 48000). A better solution would be to somehow find a native sample rate. - */ - for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) { - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; - if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo); - } - } - } -} - -static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - /* - TODO: Test Me. - - This is currently untested, so for now we are just returning default devices. - */ -#if 0 && !defined(MA_ANDROID) - SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); - if (resultSL != SL_RESULT_SUCCESS) { - /* The interface may not be supported so just report a default device. */ - goto return_default_device; - } - - if (deviceType == ma_device_type_playback) { - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1); - } else { - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1); - } - - goto return_detailed_info; -#else - goto return_default_device; -#endif - -return_default_device: - if (pDeviceID != NULL) { - if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) || - (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - } - - /* ID and Name / Description */ - if (deviceType == ma_device_type_playback) { - pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; - - goto return_detailed_info; - - -return_detailed_info: - - /* - For now we're just outputting a set of values that are supported by the API but not necessarily supported - by the device natively. Later on we should work on this so that it more closely reflects the device's - actual native format. - */ - pDeviceInfo->nativeDataFormatCount = 0; -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo); -#endif - ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo); - ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo); - - return MA_SUCCESS; -} - - -#ifdef MA_ANDROID -/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/ -static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - size_t periodSizeInBytes; - ma_uint8* pBuffer; - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - (void)pBufferQueue; - - /* - For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like - OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, - but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( - */ - - /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return; - } - - /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingCapture) { - return; - } - - periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes); - - ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames); - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - return; - } - - pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; -} - -static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - size_t periodSizeInBytes; - ma_uint8* pBuffer; - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - (void)pBufferQueue; - - /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return; - } - - /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingPlayback) { - return; - } - - periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes); - - ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames); - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - return; - } - - pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; -} -#endif - -static ma_result ma_device_uninit__opensl(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioRecorderObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); - } - - ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioPlayerObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); - } - if (pDevice->opensl.pOutputMixObj) { - MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); - } - - ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 -typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM; -#else -typedef SLDataFormat_PCM ma_SLDataFormat_PCM; -#endif - -static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat) -{ - /* We need to convert our format/channels/rate so that they aren't set to default. */ - if (format == ma_format_unknown) { - format = MA_DEFAULT_FORMAT; - } - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - if (format == ma_format_f32) { - pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; - pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; - } else { - pDataFormat->formatType = SL_DATAFORMAT_PCM; - } -#else - pDataFormat->formatType = SL_DATAFORMAT_PCM; -#endif - - pDataFormat->numChannels = channels; - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ - pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; - pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); - pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; - - /* - Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html - - Only mono and stereo is supported. - - Only u8 and s16 formats are supported. - - Maximum sample rate of 48000. - */ -#ifdef MA_ANDROID - if (pDataFormat->numChannels > 2) { - pDataFormat->numChannels = 2; - } -#if __ANDROID_API__ >= 21 - if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { - /* It's floating point. */ - MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); - if (pDataFormat->bitsPerSample > 32) { - pDataFormat->bitsPerSample = 32; - } - } else { - if (pDataFormat->bitsPerSample > 16) { - pDataFormat->bitsPerSample = 16; - } - } -#else - if (pDataFormat->bitsPerSample > 16) { - pDataFormat->bitsPerSample = 16; - } -#endif - if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) { - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48; - } -#endif - - pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */ - - return MA_SUCCESS; -} - -static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_bool32 isFloatingPoint = MA_FALSE; -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { - MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); - isFloatingPoint = MA_TRUE; - } -#endif - if (isFloatingPoint) { - if (pDataFormat->bitsPerSample == 32) { - *pFormat = ma_format_f32; - } - } else { - if (pDataFormat->bitsPerSample == 8) { - *pFormat = ma_format_u8; - } else if (pDataFormat->bitsPerSample == 16) { - *pFormat = ma_format_s16; - } else if (pDataFormat->bitsPerSample == 24) { - *pFormat = ma_format_s24; - } else if (pDataFormat->bitsPerSample == 32) { - *pFormat = ma_format_s32; - } - } - - *pChannels = pDataFormat->numChannels; - *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000; - ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ -#ifdef MA_ANDROID - SLDataLocator_AndroidSimpleBufferQueue queue; - SLresult resultSL; - size_t bufferSizeInBytes; - SLInterfaceID itfIDs[2]; - const SLboolean itfIDsRequired[] = { - SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */ - SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */ - }; -#endif - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* - For now, only supporting Android implementations of OpenSL|ES since that's the only one I've - been able to test with and I currently depend on Android-specific extensions (simple buffer - queues). - */ -#ifdef MA_ANDROID - itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION; - - /* No exclusive mode with OpenSL|ES. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Now we can start initializing the device properly. */ - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->opensl); - - queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_SLDataFormat_PCM pcm; - SLDataLocator_IODevice locatorDevice; - SLDataSource source; - SLDataSink sink; - SLAndroidConfigurationItf pRecorderConfig; - - ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm); - - locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; - locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; - locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ - locatorDevice.device = NULL; - - source.pLocator = &locatorDevice; - source.pFormat = NULL; - - queue.numBuffers = pDescriptorCapture->periodCount; - - sink.pLocator = &queue; - sink.pFormat = (SLDataFormat_PCM*)&pcm; - - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { - /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 1; - ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ - pcm.bitsPerSample = 16; - pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = 0; - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - } - - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder."); - return ma_result_from_OpenSL(resultSL); - } - - - /* Set the recording preset before realizing the player. */ - if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); - if (resultSL == SL_RESULT_SUCCESS) { - SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset); - resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32)); - if (resultSL != SL_RESULT_SUCCESS) { - /* Failed to set the configuration. Just keep going. */ - } - } - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); - return ma_result_from_OpenSL(resultSL); - } - - /* The internal format is determined by the "pcm" object. */ - ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap)); - - /* Buffer. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexCapture = 0; - - bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; - pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferCapture == NULL) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); - return MA_OUT_OF_MEMORY; - } - MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_SLDataFormat_PCM pcm; - SLDataSource source; - SLDataLocator_OutputMix outmixLocator; - SLDataSink sink; - SLAndroidConfigurationItf pPlayerConfig; - - ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm); - - resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); - return ma_result_from_OpenSL(resultSL); - } - - /* Set the output device. */ - if (pDescriptorPlayback->pDeviceID != NULL) { - SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl; - MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); - } - - queue.numBuffers = pDescriptorPlayback->periodCount; - - source.pLocator = &queue; - source.pFormat = (SLDataFormat_PCM*)&pcm; - - outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; - outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; - - sink.pLocator = &outmixLocator; - sink.pFormat = NULL; - - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { - /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 2; - ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; - pcm.bitsPerSample = 16; - pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - } - - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player."); - return ma_result_from_OpenSL(resultSL); - } - - - /* Set the stream type before realizing the player. */ - if (pConfig->opensl.streamType != ma_opensl_stream_type_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); - if (resultSL == SL_RESULT_SUCCESS) { - SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType); - resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); - if (resultSL != SL_RESULT_SUCCESS) { - /* Failed to set the configuration. Just keep going. */ - } - } - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); - return ma_result_from_OpenSL(resultSL); - } - - /* The internal format is determined by the "pcm" object. */ - ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap)); - - /* Buffer. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexPlayback = 0; - - bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; - pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferPlayback == NULL) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); - return MA_OUT_OF_MEMORY; - } - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); - } - - return MA_SUCCESS; -#else - return MA_NO_BACKEND; /* Non-Android implementations are not supported. */ -#endif -} - -static ma_result ma_device_start__opensl(ma_device* pDevice) -{ - SLresult resultSL; - size_t periodSizeInBytes; - ma_uint32 iPeriod; - - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); - return ma_result_from_OpenSL(resultSL); - } - - periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); - return ma_result_from_OpenSL(resultSL); - } - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); - return ma_result_from_OpenSL(resultSL); - } - - /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */ - if (pDevice->type == ma_device_type_duplex) { - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - } else { - ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback); - } - - periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); - return ma_result_from_OpenSL(resultSL); - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType) -{ - SLAndroidSimpleBufferQueueItf pBufferQueue; - - MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); - - if (pDevice->type == ma_device_type_capture) { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture; - pDevice->opensl.isDrainingCapture = MA_TRUE; - } else { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback; - pDevice->opensl.isDrainingPlayback = MA_TRUE; - } - - for (;;) { - SLAndroidSimpleBufferQueueState state; - - MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state); - if (state.count == 0) { - break; - } - - ma_sleep(10); - } - - if (pDevice->type == ma_device_type_capture) { - pDevice->opensl.isDrainingCapture = MA_FALSE; - } else { - pDevice->opensl.isDrainingPlayback = MA_FALSE; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__opensl(ma_device* pDevice) -{ - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_device_drain__opensl(pDevice, ma_device_type_capture); - - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); - return ma_result_from_OpenSL(resultSL); - } - - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_drain__opensl(pDevice, ma_device_type_playback); - - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); - return ma_result_from_OpenSL(resultSL); - } - - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); - } - - /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__opensl(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_opensl); - (void)pContext; - - /* Uninit global data. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ - - g_maOpenSLInitCounter -= 1; - if (g_maOpenSLInitCounter == 0) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - } - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - return MA_SUCCESS; -} - -static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) -{ - /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ - ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName); - if (p == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); - return MA_NO_BACKEND; - } - - *pHandle = *p; - return MA_SUCCESS; -} - -static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) -{ - g_maOpenSLInitCounter += 1; - if (g_maOpenSLInitCounter == 1) { - SLresult resultSL; - - resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - - (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); - - resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); - if (resultSL != SL_RESULT_SUCCESS) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; - -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libOpenSLESNames[] = { - "libOpenSLES.so" - }; -#endif - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#if !defined(MA_NO_RUNTIME_LINKING) - /* - Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One - report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime - and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any - references to the symbols and will hopefully skip the checks. - */ - for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { - pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]); - if (pContext->opensl.libOpenSLES != NULL) { - break; - } - } - - if (pContext->opensl.libOpenSLES == NULL) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); - return MA_NO_BACKEND; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); - if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); - if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); - if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); - if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); - if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); - if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); - if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); - return result; - } - - pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine"); - if (pContext->opensl.slCreateEngine == NULL) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); - return MA_NO_BACKEND; - } -#else - pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; - pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; - pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; - pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; - pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; - pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; - pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; -#endif - - - /* Initialize global data first if applicable. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - result = ma_context_init_engine_nolock__opensl(pContext); - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__opensl; - pCallbacks->onContextUninit = ma_context_uninit__opensl; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl; - pCallbacks->onDeviceInit = ma_device_init__opensl; - pCallbacks->onDeviceUninit = ma_device_uninit__opensl; - pCallbacks->onDeviceStart = ma_device_start__opensl; - pCallbacks->onDeviceStop = ma_device_stop__opensl; - pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* OpenSL|ES */ - - -/****************************************************************************** - -Web Audio Backend - -******************************************************************************/ -#ifdef MA_HAS_WEBAUDIO -#include - -static ma_bool32 ma_is_capture_supported__webaudio() -{ - return EM_ASM_INT({ - return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined); - }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */ -} - -#ifdef __cplusplus -extern "C" { -#endif -void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) -{ - ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); -} - -void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames) -{ - ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount); -} -#ifdef __cplusplus -} -#endif - -static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Only supporting default devices for now. */ - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - if (ma_is_capture_supported__webaudio()) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { - return MA_NO_DEVICE; - } - - MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); - - /* Only supporting default devices for now. */ - (void)pDeviceID; - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - /* Only supporting default devices. */ - pDeviceInfo->isDefault = MA_TRUE; - - /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */ - pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({ - try { - var temp = new (window.AudioContext || window.webkitAudioContext)(); - var sampleRate = temp.sampleRate; - temp.close(); - return sampleRate; - } catch(e) { - return 0; - } - }, 0); /* Must pass in a dummy argument for C99 compatibility. */ - - if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) { - return MA_NO_DEVICE; - } - - pDeviceInfo->nativeDataFormatCount = 1; - - return MA_SUCCESS; -} - - -static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex) -{ - MA_ASSERT(pDevice != NULL); - - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - - /* Make sure all nodes are disconnected and marked for collection. */ - if (device.scriptNode !== undefined) { - device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ - device.scriptNode.disconnect(); - device.scriptNode = undefined; - } - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - /* - Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want - to clear the callback before closing. - */ - device.webaudio.close(); - device.webaudio = undefined; - - /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */ - if (device.intermediaryBuffer !== undefined) { - Module._free(device.intermediaryBuffer); - device.intermediaryBuffer = undefined; - device.intermediaryBufferView = undefined; - device.intermediaryBufferSizeInBytes = undefined; - } - - /* Make sure the device is untracked so the slot can be reused later. */ - miniaudio.untrack_device_by_index($0); - }, deviceIndex, deviceType); -} - -static ma_result ma_device_uninit__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback); - } - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - There have been reports of the default buffer size being too small on some browsers. There have been reports of the default buffer - size being too small on some browsers. If we're using default buffer size, we'll make sure the period size is a big biffer than our - standard defaults. - */ - ma_uint32 periodSizeInFrames; - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate); - } - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - periodSizeInFrames = pDescriptor->periodSizeInFrames; - } - - /* The size of the buffer must be a power of 2 and between 256 and 16384. */ - if (periodSizeInFrames < 256) { - periodSizeInFrames = 256; - } else if (periodSizeInFrames > 16384) { - periodSizeInFrames = 16384; - } else { - periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames); - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - int deviceIndex; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - - if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { - return MA_NO_DEVICE; - } - - /* We're going to calculate some stuff in C just to simplify the JS code. */ - channels = (pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; - sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptor, sampleRate, pConfig->performanceProfile); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "periodSizeInFrames = %d\n", (int)periodSizeInFrames); - - /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */ - deviceIndex = EM_ASM_INT({ - var channels = $0; - var sampleRate = $1; - var bufferSize = $2; /* In PCM frames. */ - var isCapture = $3; - var pDevice = $4; - - if (typeof(window.miniaudio) === 'undefined') { - return -1; /* Context not initialized. */ - } - - var device = {}; - - /* The AudioContext must be created in a suspended state. */ - device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate}); - device.webaudio.suspend(); - device.state = 1; /* ma_device_state_stopped */ - - /* - We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. Because it's passed between - JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free(). - */ - device.intermediaryBufferSizeInBytes = channels * bufferSize * 4; - device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes); - device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); - - /* - Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations. - - ScriptProcessorNode is actually deprecated so this is likely to be temporary. The way this works for playback is very simple. You just set a callback - that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of - something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to - work for years to come, but this may need to change to use AudioSourceBufferNode instead, which I think is what Emscripten uses for it's built-in SDL - implementation. I'll be avoiding that insane AudioWorklet API like the plague... - - For capture it is a bit unintuitive. We use the ScriptProccessorNode _only_ to get the raw PCM data. It is connected to an AudioContext just like the - playback case, however we just output silence to the AudioContext instead of passing any real data. It would make more sense to me to use the - MediaRecorder API, but unfortunately you need to specify a MIME time (Opus, Vorbis, etc.) for the binary blob that's returned to the client, but I've - been unable to figure out how to get this as raw PCM. The closest I can think is to use the MIME type for WAV files and just parse it, but I don't know - how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like - this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know! - */ - device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, (isCapture) ? channels : 0, (isCapture) ? 0 : channels); - - if (isCapture) { - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBuffer === undefined) { - return; /* This means the device has been uninitialized. */ - } - - if (device.intermediaryBufferView.length == 0) { - /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ - device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); - } - - /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */ - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } - - /* There are some situations where we may want to send silence to the client. */ - var sendSilence = false; - if (device.streamNode === undefined) { - sendSilence = true; - } - - /* Sanity check. This will never happen, right? */ - if (e.inputBuffer.numberOfChannels != channels) { - console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence."); - sendSilence = true; - } - - /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */ - var totalFramesProcessed = 0; - while (totalFramesProcessed < e.inputBuffer.length) { - var framesRemaining = e.inputBuffer.length - totalFramesProcessed; - var framesToProcess = framesRemaining; - if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { - framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); - } - - /* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */ - if (sendSilence) { - device.intermediaryBufferView.fill(0.0); - } else { - for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { - for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) { - device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame]; - } - } - } - - /* Send data to the client from our intermediary buffer. */ - ccall("ma_device_process_pcm_frames_capture__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]); - - totalFramesProcessed += framesToProcess; - } - }; - - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - device.streamNode = device.webaudio.createMediaStreamSource(stream); - device.streamNode.connect(device.scriptNode); - device.scriptNode.connect(device.webaudio.destination); - }) - .catch(function(error) { - /* I think this should output silence... */ - device.scriptNode.connect(device.webaudio.destination); - }); - } else { - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBuffer === undefined) { - return; /* This means the device has been uninitialized. */ - } - - if(device.intermediaryBufferView.length == 0) { - /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ - device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); - } - - var outputSilence = false; - - /* Sanity check. This will never happen, right? */ - if (e.outputBuffer.numberOfChannels != channels) { - console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence."); - outputSilence = true; - return; - } - - /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */ - var totalFramesProcessed = 0; - while (totalFramesProcessed < e.outputBuffer.length) { - var framesRemaining = e.outputBuffer.length - totalFramesProcessed; - var framesToProcess = framesRemaining; - if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { - framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); - } - - /* Read data from the client into our intermediary buffer. */ - ccall("ma_device_process_pcm_frames_playback__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]); - - /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */ - if (outputSilence) { - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } - } else { - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - var outputBuffer = e.outputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { - outputBuffer[totalFramesProcessed + iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; - } - } - } - - totalFramesProcessed += framesToProcess; - } - }; - - device.scriptNode.connect(device.webaudio.destination); - } - - return miniaudio.track_device(device); - }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice); - - if (deviceIndex < 0) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - if (deviceType == ma_device_type_capture) { - pDevice->webaudio.indexCapture = deviceIndex; - } else { - pDevice->webaudio.indexPlayback = deviceIndex; - } - - pDescriptor->format = ma_format_f32; - pDescriptor->channels = channels; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); - pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); - pDescriptor->periodSizeInFrames = periodSizeInFrames; - pDescriptor->periodCount = 1; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with Web Audio. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = 2; /* ma_device_state_started */ - }, pDevice->webaudio.indexCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = 2; /* ma_device_state_started */ - }, pDevice->webaudio.indexPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - From the WebAudio API documentation for AudioContext.suspend(): - - Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the - destination, and then allows the system to release its claim on audio hardware. - - I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to - do any kind of explicit draining. - */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = 1; /* ma_device_state_stopped */ - }, pDevice->webaudio.indexCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = 1; /* ma_device_state_stopped */ - }, pDevice->webaudio.indexPlayback); - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__webaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_webaudio); - - /* Nothing needs to be done here. */ - (void)pContext; - - return MA_SUCCESS; -} - -static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int resultFromJS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; /* Unused. */ - - /* Here is where our global JavaScript object is initialized. */ - resultFromJS = EM_ASM_INT({ - if ((window.AudioContext || window.webkitAudioContext) === undefined) { - return 0; /* Web Audio not supported. */ - } - - if (typeof(window.miniaudio) === 'undefined') { - window.miniaudio = {}; - miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ - - miniaudio.track_device = function(device) { - /* Try inserting into a free slot first. */ - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == null) { - miniaudio.devices[iDevice] = device; - return iDevice; - } - } - - /* Getting here means there is no empty slots in the array so we just push to the end. */ - miniaudio.devices.push(device); - return miniaudio.devices.length - 1; - }; - - miniaudio.untrack_device_by_index = function(deviceIndex) { - /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ - miniaudio.devices[deviceIndex] = null; - - /* Trim the array if possible. */ - while (miniaudio.devices.length > 0) { - if (miniaudio.devices[miniaudio.devices.length-1] == null) { - miniaudio.devices.pop(); - } else { - break; - } - } - }; - - miniaudio.untrack_device = function(device) { - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == device) { - return miniaudio.untrack_device_by_index(iDevice); - } - } - }; - - miniaudio.get_device_by_index = function(deviceIndex) { - return miniaudio.devices[deviceIndex]; - }; - - miniaudio.unlock_event_types = (function(){ - return ['touchstart', 'touchend', 'click']; - })(); - - miniaudio.unlock = function() { - for(var i = 0; i < miniaudio.devices.length; ++i) { - var device = miniaudio.devices[i]; - if (device != null && device.webaudio != null && device.state === 2 /* ma_device_state_started */) { - device.webaudio.resume(); - } - } - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - }; - - miniaudio.unlock_event_types.map(function(event_type) { - document.addEventListener(event_type, miniaudio.unlock, true); - }); - } - - return 1; - }, 0); /* Must pass in a dummy argument for C99 compatibility. */ - - if (resultFromJS != 1) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pCallbacks->onContextInit = ma_context_init__webaudio; - pCallbacks->onContextUninit = ma_context_uninit__webaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; - pCallbacks->onDeviceInit = ma_device_init__webaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; - pCallbacks->onDeviceStart = ma_device_start__webaudio; - pCallbacks->onDeviceStop = ma_device_stop__webaudio; - pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* Web Audio */ - - - -static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels) -{ - /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */ - if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) { - ma_uint32 iChannel; - - if (channels == 0 || channels > MA_MAX_CHANNELS) { - return MA_FALSE; /* Channel count out of range. */ - } - - /* A channel cannot be present in the channel map more than once. */ - for (iChannel = 0; iChannel < channels; ++iChannel) { - ma_uint32 jChannel; - for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) { - if (pChannelMap[iChannel] == pChannelMap[jChannel]) { - return MA_FALSE; - } - } - } - } - - return MA_TRUE; -} - - -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (pDevice->capture.format == ma_format_unknown) { - pDevice->capture.format = pDevice->capture.internalFormat; - } - if (pDevice->capture.channels == 0) { - pDevice->capture.channels = pDevice->capture.internalChannels; - } - if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS); - if (pDevice->capture.internalChannels == pDevice->capture.channels) { - ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); - } else { - if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); - } - } - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (pDevice->playback.format == ma_format_unknown) { - pDevice->playback.format = pDevice->playback.internalFormat; - } - if (pDevice->playback.channels == 0) { - pDevice->playback.channels = pDevice->playback.internalChannels; - } - if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS); - if (pDevice->playback.internalChannels == pDevice->playback.channels) { - ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); - } else { - if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); - } - } - } - } - - if (pDevice->sampleRate == 0) { - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - pDevice->sampleRate = pDevice->capture.internalSampleRate; - } else { - pDevice->sampleRate = pDevice->playback.internalSampleRate; - } - } - - /* Data converters. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - /* Converting from internal device format to client format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->capture.internalFormat; - converterConfig.channelsIn = pDevice->capture.internalChannels; - converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; - converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; - converterConfig.formatOut = pDevice->capture.format; - converterConfig.channelsOut = pDevice->capture.channels; - converterConfig.sampleRateOut = pDevice->sampleRate; - converterConfig.pChannelMapOut = pDevice->capture.channelMap; - converterConfig.channelMixMode = pDevice->capture.channelMixMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - - /* Make sure the old converter is uninitialized first. */ - if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); - } - - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); - if (result != MA_SUCCESS) { - return result; - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - /* Converting from client format to device format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->playback.format; - converterConfig.channelsIn = pDevice->playback.channels; - converterConfig.sampleRateIn = pDevice->sampleRate; - converterConfig.pChannelMapIn = pDevice->playback.channelMap; - converterConfig.formatOut = pDevice->playback.internalFormat; - converterConfig.channelsOut = pDevice->playback.internalChannels; - converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; - converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; - converterConfig.channelMixMode = pDevice->playback.channelMixMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - - /* Make sure the old converter is uninitialized first. */ - if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); - } - - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); - if (result != MA_SUCCESS) { - return result; - } - } - - - /* - In playback mode, if the data converter does not support retrieval of the required number of - input frames given a number of output frames, we need to fall back to a heap-allocated cache. - */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - ma_uint64 unused; - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = 0; - - if (deviceType == ma_device_type_duplex || ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) { - /* We need a heap allocated cache. We want to size this based on the period size. */ - void* pNewInputCache; - ma_uint64 newInputCacheCap; - ma_uint64 newInputCacheSizeInBytes; - - newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); - - newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - if (newInputCacheSizeInBytes > MA_SIZE_MAX) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ - } - - pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pNewInputCache == NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - return MA_OUT_OF_MEMORY; - } - - pDevice->playback.pInputCache = pNewInputCache; - pDevice->playback.inputCacheCap = newInputCacheCap; - } else { - /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - /* Capture. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { - return MA_INVALID_ARGS; - } - - pDevice->capture.internalFormat = pDescriptorCapture->format; - pDevice->capture.internalChannels = pDescriptorCapture->channels; - pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; - - if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); - } - } - - /* Playback. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { - return MA_INVALID_ARGS; - } - - pDevice->playback.internalFormat = pDescriptorPlayback->format; - pDevice->playback.internalChannels = pDescriptorPlayback->channels; - pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; - - if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); - } - } - - /* - The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. - For loopback devices, we need to retrieve the name of the playback device. - */ - { - ma_device_info deviceInfo; - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (pDescriptorCapture->pDeviceID == NULL) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); - } - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (pDescriptorPlayback->pDeviceID == NULL) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); - } - } - } - } - - /* Update data conversion. */ - return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ -} - - -static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) -{ - ma_device* pDevice = (ma_device*)pData; - MA_ASSERT(pDevice != NULL); - -#ifdef MA_WIN32 - ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); -#endif - - /* - When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from - ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately - after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker - thread to signal an event to know when the worker thread is ready for action. - */ - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_event_signal(&pDevice->stopEvent); - - for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ - ma_result startResult; - ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */ - - /* We wait on an event to know when something has requested that the device be started and the main loop entered. */ - ma_event_wait(&pDevice->wakeupEvent); - - /* Default result code. */ - pDevice->workResult = MA_SUCCESS; - - /* If the reason for the wake up is that we are terminating, just break from the loop. */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - break; - } - - /* - Getting to this point means the device is wanting to get started. The function that has requested that the device - be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event - in both the success and error case. It's important that the state of the device is set _before_ signaling the event. - */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); - - /* If the device has a start callback, start it now. */ - if (pDevice->pContext->callbacks.onDeviceStart != NULL) { - startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice); - } else { - startResult = MA_SUCCESS; - } - - /* - If starting was not successful we'll need to loop back to the start and wait for something - to happen (pDevice->wakeupEvent). - */ - if (startResult != MA_SUCCESS) { - pDevice->workResult = startResult; - ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */ - continue; - } - - /* Make sure the state is set appropriately. */ - ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ - ma_event_signal(&pDevice->startEvent); - - ma_device__on_notification_started(pDevice); - - if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { - pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); - } else { - /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */ - ma_device_audio_thread__default_read_write(pDevice); - } - - /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */ - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */ - } - - /* - After the device has stopped, make sure an event is posted. Don't post a stopped event if - stopping failed. This can happen on some backends when the underlying stream has been - stopped due to the device being physically unplugged or disabled via an OS setting. - */ - if (stopResult == MA_SUCCESS) { - ma_device__on_notification_stopped(pDevice); - } - - /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_event_signal(&pDevice->stopEvent); - } - -#ifdef MA_WIN32 - ma_CoUninitialize(pDevice->pContext); -#endif - - return (ma_thread_result)0; -} - - -/* Helper for determining whether or not the given device is initialized. */ -static ma_bool32 ma_device__is_initialized(ma_device* pDevice) -{ - if (pDevice == NULL) { - return MA_FALSE; - } - - return ma_device_get_state(pDevice) != ma_device_state_uninitialized; -} - - -#ifdef MA_WIN32 -static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) -{ - /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ -#ifdef MA_WIN32_DESKTOP - ma_CoUninitialize(pContext); - ma_dlclose(pContext, pContext->win32.hUser32DLL); - ma_dlclose(pContext, pContext->win32.hOle32DLL); - ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); -#else - (void)pContext; -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) -{ -#ifdef MA_WIN32_DESKTOP - /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll"); - if (pContext->win32.hOle32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2"); - - - /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll"); - if (pContext->win32.hUser32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow"); - - - /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll"); - if (pContext->win32.hAdvapi32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); -#endif - - ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - return MA_SUCCESS; -} -#else -static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) -{ -#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING) - ma_dlclose(pContext, pContext->posix.pthreadSO); -#else - (void)pContext; -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init_backend_apis__nix(ma_context* pContext) -{ - /* pthread */ -#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING) - const char* libpthreadFileNames[] = { - "libpthread.so", - "libpthread.so.0", - "libpthread.dylib" - }; - size_t i; - - for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) { - pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]); - if (pContext->posix.pthreadSO != NULL) { - break; - } - } - - if (pContext->posix.pthreadSO == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create"); - pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join"); - pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init"); - pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy"); - pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock"); - pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock"); - pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init"); - pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy"); - pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait"); - pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal"); - pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init"); - pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy"); - pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy"); - pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam"); - pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam"); -#else - pContext->posix.pthread_create = (ma_proc)pthread_create; - pContext->posix.pthread_join = (ma_proc)pthread_join; - pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init; - pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy; - pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock; - pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock; - pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init; - pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy; - pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait; - pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal; - pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init; - pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy; -#if !defined(__EMSCRIPTEN__) - pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy; - pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam; - pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam; -#endif -#endif - - return MA_SUCCESS; -} -#endif - -static ma_result ma_context_init_backend_apis(ma_context* pContext) -{ - ma_result result; -#ifdef MA_WIN32 - result = ma_context_init_backend_apis__win32(pContext); -#else - result = ma_context_init_backend_apis__nix(pContext); -#endif - - return result; -} - -static ma_result ma_context_uninit_backend_apis(ma_context* pContext) -{ - ma_result result; -#ifdef MA_WIN32 - result = ma_context_uninit_backend_apis__win32(pContext); -#else - result = ma_context_uninit_backend_apis__nix(pContext); -#endif - - return result; -} - - -static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { - if (pContext->callbacks.onDeviceDataLoop == NULL) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } -} - - -/* The default capacity doesn't need to be too big. */ -#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY -#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 -#endif - -MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void) -{ - ma_device_job_thread_config config; - - MA_ZERO_OBJECT(&config); - config.noThread = MA_FALSE; - config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY; - config.jobQueueFlags = 0; - - return config; -} - - -static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData) -{ - ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData; - MA_ASSERT(pJobThread != NULL); - - for (;;) { - ma_result result; - ma_job job; - - result = ma_device_job_thread_next(pJobThread, &job); - if (result != MA_SUCCESS) { - break; - } - - if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { - break; - } - - ma_job_process(&job); - } - - return (ma_thread_result)0; -} - -MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread) -{ - ma_result result; - ma_job_queue_config jobQueueConfig; - - if (pJobThread == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pJobThread); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - - /* Initialize the job queue before the thread to ensure it's in a valid state. */ - jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); - - result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize job queue. */ - } - - - /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */ - if (pConfig->noThread == MA_FALSE) { - result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks); - if (result != MA_SUCCESS) { - ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); - return result; /* Failed to create the job thread. */ - } - - pJobThread->_hasThread = MA_TRUE; - } else { - pJobThread->_hasThread = MA_FALSE; - } - - - return MA_SUCCESS; -} - -MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pJobThread == NULL) { - return; - } - - /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */ - { - ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); - ma_device_job_thread_post(pJobThread, &job); - } - - /* Wait for the thread to terminate naturally. */ - if (pJobThread->_hasThread) { - ma_thread_wait(&pJobThread->thread); - } - - /* At this point the thread should be terminated so we can safely uninitialize the job queue. */ - ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); -} - -MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob) -{ - if (pJobThread == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_post(&pJobThread->jobQueue, pJob); -} - -MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob) -{ - if (pJob == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pJob); - - if (pJobThread == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_next(&pJobThread->jobQueue, pJob); -} - - - -MA_API ma_context_config ma_context_config_init(void) -{ - ma_context_config config; - MA_ZERO_OBJECT(&config); - - return config; -} - -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) -{ - ma_result result; - ma_context_config defaultConfig; - ma_backend defaultBackends[ma_backend_null+1]; - ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; - - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pContext); - - /* Always make sure the config is set first to ensure properties are available as soon as possible. */ - if (pConfig == NULL) { - defaultConfig = ma_context_config_init(); - pConfig = &defaultConfig; - } - - /* Allocation callbacks need to come first because they'll be passed around to other areas. */ - result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - /* Get a lot set up first so we can start logging ASAP. */ - if (pConfig->pLog != NULL) { - pContext->pLog = pConfig->pLog; - } else { - result = ma_log_init(&pContext->allocationCallbacks, &pContext->log); - if (result == MA_SUCCESS) { - pContext->pLog = &pContext->log; - } else { - pContext->pLog = NULL; /* Logging is not available. */ - } - } - - pContext->threadPriority = pConfig->threadPriority; - pContext->threadStackSize = pConfig->threadStackSize; - pContext->pUserData = pConfig->pUserData; - - /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */ - result = ma_context_init_backend_apis(pContext); - if (result != MA_SUCCESS) { - return result; - } - - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } - - MA_ASSERT(pBackendsToIterate != NULL); - - for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) { - ma_backend backend = pBackendsToIterate[iBackend]; - - /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */ - MA_ZERO_OBJECT(&pContext->callbacks); - - /* These backends are using the new callback system. */ - switch (backend) { - #ifdef MA_HAS_WASAPI - case ma_backend_wasapi: - { - pContext->callbacks.onContextInit = ma_context_init__wasapi; - } break; - #endif - #ifdef MA_HAS_DSOUND - case ma_backend_dsound: - { - pContext->callbacks.onContextInit = ma_context_init__dsound; - } break; - #endif - #ifdef MA_HAS_WINMM - case ma_backend_winmm: - { - pContext->callbacks.onContextInit = ma_context_init__winmm; - } break; - #endif - #ifdef MA_HAS_COREAUDIO - case ma_backend_coreaudio: - { - pContext->callbacks.onContextInit = ma_context_init__coreaudio; - } break; - #endif - #ifdef MA_HAS_SNDIO - case ma_backend_sndio: - { - pContext->callbacks.onContextInit = ma_context_init__sndio; - } break; - #endif - #ifdef MA_HAS_AUDIO4 - case ma_backend_audio4: - { - pContext->callbacks.onContextInit = ma_context_init__audio4; - } break; - #endif - #ifdef MA_HAS_OSS - case ma_backend_oss: - { - pContext->callbacks.onContextInit = ma_context_init__oss; - } break; - #endif - #ifdef MA_HAS_PULSEAUDIO - case ma_backend_pulseaudio: - { - pContext->callbacks.onContextInit = ma_context_init__pulse; - } break; - #endif - #ifdef MA_HAS_ALSA - case ma_backend_alsa: - { - pContext->callbacks.onContextInit = ma_context_init__alsa; - } break; - #endif - #ifdef MA_HAS_JACK - case ma_backend_jack: - { - pContext->callbacks.onContextInit = ma_context_init__jack; - } break; - #endif - #ifdef MA_HAS_AAUDIO - case ma_backend_aaudio: - { - pContext->callbacks.onContextInit = ma_context_init__aaudio; - } break; - #endif - #ifdef MA_HAS_OPENSL - case ma_backend_opensl: - { - pContext->callbacks.onContextInit = ma_context_init__opensl; - } break; - #endif - #ifdef MA_HAS_WEBAUDIO - case ma_backend_webaudio: - { - pContext->callbacks.onContextInit = ma_context_init__webaudio; - } break; - #endif - #ifdef MA_HAS_CUSTOM - case ma_backend_custom: - { - /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */ - pContext->callbacks = pConfig->custom; - } break; - #endif - #ifdef MA_HAS_NULL - case ma_backend_null: - { - pContext->callbacks.onContextInit = ma_context_init__null; - } break; - #endif - - default: break; - } - - if (pContext->callbacks.onContextInit != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); - result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); - } else { - result = MA_NO_BACKEND; - } - - /* If this iteration was successful, return. */ - if (result == MA_SUCCESS) { - result = ma_mutex_init(&pContext->deviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); - } - - result = ma_mutex_init(&pContext->deviceInfoLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); - } - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); - - pContext->backend = backend; - return result; - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); - } - } - - /* If we get here it means an error occurred. */ - MA_ZERO_OBJECT(pContext); /* Safety. */ - return MA_NO_BACKEND; -} - -MA_API ma_result ma_context_uninit(ma_context* pContext) -{ - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextUninit != NULL) { - pContext->callbacks.onContextUninit(pContext); - } - - ma_mutex_uninit(&pContext->deviceEnumLock); - ma_mutex_uninit(&pContext->deviceInfoLock); - ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks); - ma_context_uninit_backend_apis(pContext); - - if (pContext->pLog == &pContext->log) { - ma_log_uninit(&pContext->log); - } - - return MA_SUCCESS; -} - -MA_API size_t ma_context_sizeof() -{ - return sizeof(ma_context); -} - - -MA_API ma_log* ma_context_get_log(ma_context* pContext) -{ - if (pContext == NULL) { - return NULL; - } - - return pContext->pLog; -} - - -MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result; - - if (pContext == NULL || callback == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_INVALID_OPERATION; - } - - ma_mutex_lock(&pContext->deviceEnumLock); - { - result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData); - } - ma_mutex_unlock(&pContext->deviceEnumLock); - - return result; -} - - -static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -{ - /* - We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device - it's just appended to the end. If it's a playback device it's inserted just before the first capture device. - */ - - /* - First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a - simple fixed size increment for buffer expansion. - */ - const ma_uint32 bufferExpansionCount = 2; - const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; - - if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { - ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount; - ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks); - if (pNewInfos == NULL) { - return MA_FALSE; /* Out of memory. */ - } - - pContext->pDeviceInfos = pNewInfos; - pContext->deviceInfoCapacity = newCapacity; - } - - if (deviceType == ma_device_type_playback) { - /* Playback. Insert just before the first capture device. */ - - /* The first thing to do is move all of the capture devices down a slot. */ - ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount; - size_t iCaptureDevice; - for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) { - pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1]; - } - - /* Now just insert where the first capture device was before moving it down a slot. */ - pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo; - pContext->playbackDeviceInfoCount += 1; - } else { - /* Capture. Insert at the end. */ - pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo; - pContext->captureDeviceInfoCount += 1; - } - - (void)pUserData; - return MA_TRUE; -} - -MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount) -{ - ma_result result; - - /* Safety. */ - if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL; - if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0; - if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL; - if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0; - - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_INVALID_OPERATION; - } - - /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */ - ma_mutex_lock(&pContext->deviceEnumLock); - { - /* Reset everything first. */ - pContext->playbackDeviceInfoCount = 0; - pContext->captureDeviceInfoCount = 0; - - /* Now enumerate over available devices. */ - result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL); - if (result == MA_SUCCESS) { - /* Playback devices. */ - if (ppPlaybackDeviceInfos != NULL) { - *ppPlaybackDeviceInfos = pContext->pDeviceInfos; - } - if (pPlaybackDeviceCount != NULL) { - *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount; - } - - /* Capture devices. */ - if (ppCaptureDeviceInfos != NULL) { - *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; /* Capture devices come after playback devices. */ - } - if (pCaptureDeviceCount != NULL) { - *pCaptureDeviceCount = pContext->captureDeviceInfoCount; - } - } - } - ma_mutex_unlock(&pContext->deviceEnumLock); - - return result; -} - -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - ma_device_info deviceInfo; - - /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */ - if (pContext == NULL || pDeviceInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* Help the backend out by copying over the device ID if we have one. */ - if (pDeviceID != NULL) { - MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID)); - } - - if (pContext->callbacks.onContextGetDeviceInfo == NULL) { - return MA_INVALID_OPERATION; - } - - ma_mutex_lock(&pContext->deviceInfoLock); - { - result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo); - } - ma_mutex_unlock(&pContext->deviceInfoLock); - - *pDeviceInfo = deviceInfo; - return result; -} - -MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext) -{ - if (pContext == NULL) { - return MA_FALSE; - } - - return ma_is_loopback_supported(pContext->backend); -} - - -MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) -{ - ma_device_config config; - MA_ZERO_OBJECT(&config); - config.deviceType = deviceType; - config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */ - - return config; -} - -MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) -{ - ma_result result; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - /* The context can be null, in which case we self-manage it. */ - if (pContext == NULL) { - return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice); - } - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDevice); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Check that we have our callbacks defined. */ - if (pContext->callbacks.onDeviceInit == NULL) { - return MA_INVALID_OPERATION; - } - - /* Basic config validation. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - if (pConfig->capture.channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) { - return MA_INVALID_ARGS; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - if (pConfig->playback.channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) { - return MA_INVALID_ARGS; - } - } - - pDevice->pContext = pContext; - - /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ - pDevice->pUserData = pConfig->pUserData; - pDevice->onData = pConfig->dataCallback; - pDevice->onNotification = pConfig->notificationCallback; - pDevice->onStop = pConfig->stopCallback; - - if (pConfig->playback.pDeviceID != NULL) { - MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); - pDevice->playback.pID = &pDevice->playback.id; - } else { - pDevice->playback.pID = NULL; - } - - if (pConfig->capture.pDeviceID != NULL) { - MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id)); - pDevice->capture.pID = &pDevice->capture.id; - } else { - pDevice->capture.pID = NULL; - } - - pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer; - pDevice->noClip = pConfig->noClip; - pDevice->noDisableDenormals = pConfig->noDisableDenormals; - pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; - pDevice->masterVolumeFactor = 1; - - pDevice->type = pConfig->deviceType; - pDevice->sampleRate = pConfig->sampleRate; - pDevice->resampling.algorithm = pConfig->resampling.algorithm; - pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; - pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable; - pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData; - - pDevice->capture.shareMode = pConfig->capture.shareMode; - pDevice->capture.format = pConfig->capture.format; - pDevice->capture.channels = pConfig->capture.channels; - ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); - pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; - - pDevice->playback.shareMode = pConfig->playback.shareMode; - pDevice->playback.format = pConfig->playback.format; - pDevice->playback.channels = pConfig->playback.channels; - ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); - pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; - - - result = ma_mutex_init(&pDevice->startStopLock); - if (result != MA_SUCCESS) { - return result; - } - - /* - When the device is started, the worker thread is the one that does the actual startup of the backend device. We - use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. - - Each of these semaphores is released internally by the worker thread when the work is completed. The start - semaphore is also used to wake up the worker thread. - */ - result = ma_event_init(&pDevice->wakeupEvent); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - result = ma_event_init(&pDevice->startEvent); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - result = ma_event_init(&pDevice->stopEvent); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - - MA_ZERO_OBJECT(&descriptorPlayback); - descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID; - descriptorPlayback.shareMode = pConfig->playback.shareMode; - descriptorPlayback.format = pConfig->playback.format; - descriptorPlayback.channels = pConfig->playback.channels; - descriptorPlayback.sampleRate = pConfig->sampleRate; - ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); - descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; - descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - descriptorPlayback.periodCount = pConfig->periods; - - if (descriptorPlayback.periodCount == 0) { - descriptorPlayback.periodCount = MA_DEFAULT_PERIODS; - } - - - MA_ZERO_OBJECT(&descriptorCapture); - descriptorCapture.pDeviceID = pConfig->capture.pDeviceID; - descriptorCapture.shareMode = pConfig->capture.shareMode; - descriptorCapture.format = pConfig->capture.format; - descriptorCapture.channels = pConfig->capture.channels; - descriptorCapture.sampleRate = pConfig->sampleRate; - ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); - descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; - descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - descriptorCapture.periodCount = pConfig->periods; - - if (descriptorCapture.periodCount == 0) { - descriptorCapture.periodCount = MA_DEFAULT_PERIODS; - } - - - result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - -#if 0 - /* - On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between - the requested format and the internal format. - */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - if (!ma_device_descriptor_is_valid(&descriptorCapture)) { - ma_device_uninit(pDevice); - return MA_INVALID_ARGS; - } - - pDevice->capture.internalFormat = descriptorCapture.format; - pDevice->capture.internalChannels = descriptorCapture.channels; - pDevice->capture.internalSampleRate = descriptorCapture.sampleRate; - ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels); - pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames; - pDevice->capture.internalPeriods = descriptorCapture.periodCount; - - if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate); - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - if (!ma_device_descriptor_is_valid(&descriptorPlayback)) { - ma_device_uninit(pDevice); - return MA_INVALID_ARGS; - } - - pDevice->playback.internalFormat = descriptorPlayback.format; - pDevice->playback.internalChannels = descriptorPlayback.channels; - pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate; - ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels); - pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames; - pDevice->playback.internalPeriods = descriptorPlayback.periodCount; - - if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate); - } - } - - - /* - The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. - For loopback devices, we need to retrieve the name of the playback device. - */ - { - ma_device_info deviceInfo; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (descriptorCapture.pDeviceID == NULL) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); - } - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (descriptorPlayback.pDeviceID == NULL) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); - } - } - } - } - - - ma_device__post_init_setup(pDevice, pConfig->deviceType); -#endif - - result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - - - - /* - If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to - be done after post_init_setup() because we'll need access to the sample rate. - */ - if (pConfig->noFixedSizedCallback == MA_FALSE) { - /* We're using a fixed sized data callback so we'll need an intermediary buffer. */ - ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames; - if (intermediaryBufferCap == 0) { - intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate); - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - ma_uint32 intermediaryBufferSizeInBytes; - - pDevice->capture.intermediaryBufferLen = 0; - pDevice->capture.intermediaryBufferCap = intermediaryBufferCap; - if (pDevice->capture.intermediaryBufferCap == 0) { - pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames; - } - - intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - - pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); - if (pDevice->capture.pIntermediaryBuffer == NULL) { - ma_device_uninit(pDevice); - return MA_OUT_OF_MEMORY; - } - - /* Silence the buffer for safety. */ - ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels); - pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint64 intermediaryBufferSizeInBytes; - - pDevice->playback.intermediaryBufferLen = 0; - if (pConfig->deviceType == ma_device_type_duplex) { - pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ - } else { - pDevice->playback.intermediaryBufferCap = intermediaryBufferCap; - if (pDevice->playback.intermediaryBufferCap == 0) { - pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames; - } - } - - intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - - pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); - if (pDevice->playback.pIntermediaryBuffer == NULL) { - ma_device_uninit(pDevice); - return MA_OUT_OF_MEMORY; - } - - /* Silence the buffer for safety. */ - ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels); - pDevice->playback.intermediaryBufferLen = 0; - } - } else { - /* Not using a fixed sized data callback so no need for an intermediary buffer. */ - } - - - /* Some backends don't require the worker thread. */ - if (!ma_context_is_backend_asynchronous(pContext)) { - /* The worker thread. */ - result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - - /* Wait for the worker thread to put the device into it's stopped state for real. */ - ma_event_wait(&pDevice->stopEvent); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - } else { - /* - If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done - after ma_device__post_init_setup(). - */ - if (ma_context_is_backend_asynchronous(pContext)) { - if (pConfig->deviceType == ma_device_type_duplex) { - result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - } - } - - ma_device__set_state(pDevice, ma_device_state_stopped); - } - - /* Log device information. */ - { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); - } - } - - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - return MA_SUCCESS; -} - -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) -{ - ma_result result; - ma_context* pContext; - ma_backend defaultBackends[ma_backend_null+1]; - ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; - ma_allocation_callbacks allocationCallbacks; - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pContextConfig != NULL) { - result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - } else { - allocationCallbacks = ma_allocation_callbacks_init_default(); - } - - - pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); - if (pContext == NULL) { - return MA_OUT_OF_MEMORY; - } - - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } - - result = MA_NO_BACKEND; - - for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { - result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); - if (result == MA_SUCCESS) { - result = ma_device_init(pContext, pConfig, pDevice); - if (result == MA_SUCCESS) { - break; /* Success. */ - } else { - ma_context_uninit(pContext); /* Failure. */ - } - } - } - - if (result != MA_SUCCESS) { - ma_free(pContext, &allocationCallbacks); - return result; - } - - pDevice->isOwnerOfContext = MA_TRUE; - return result; -} - -MA_API void ma_device_uninit(ma_device* pDevice) -{ - if (!ma_device__is_initialized(pDevice)) { - return; - } - - /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ - if (ma_device_is_started(pDevice)) { - ma_device_stop(pDevice); - } - - /* Putting the device into an uninitialized state will make the worker thread return. */ - ma_device__set_state(pDevice, ma_device_state_uninitialized); - - /* Wake up the worker thread and wait for it to properly terminate. */ - if (!ma_context_is_backend_asynchronous(pDevice->pContext)) { - ma_event_signal(&pDevice->wakeupEvent); - ma_thread_wait(&pDevice->thread); - } - - if (pDevice->pContext->callbacks.onDeviceUninit != NULL) { - pDevice->pContext->callbacks.onDeviceUninit(pDevice); - } - - - ma_event_uninit(&pDevice->stopEvent); - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->playback.pInputCache != NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->capture.pIntermediaryBuffer != NULL) { - ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - } - if (pDevice->playback.pIntermediaryBuffer != NULL) { - ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->isOwnerOfContext) { - ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks; - - ma_context_uninit(pDevice->pContext); - ma_free(pDevice->pContext, &allocationCallbacks); - } - - MA_ZERO_OBJECT(pDevice); -} - -MA_API ma_context* ma_device_get_context(ma_device* pDevice) -{ - if (pDevice == NULL) { - return NULL; - } - - return pDevice->pContext; -} - -MA_API ma_log* ma_device_get_log(ma_device* pDevice) -{ - return ma_context_get_log(ma_device_get_context(pDevice)); -} - -MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - if (pDeviceInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDeviceInfo); - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ - if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { - return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); - } - - /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ - if (type == ma_device_type_playback) { - return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); - } else { - return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); - } -} - -MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) -{ - ma_result result; - ma_device_info deviceInfo; - - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = 0; - } - - if (pName != NULL && nameCap > 0) { - pName[0] = '\0'; - } - - result = ma_device_get_info(pDevice, type, &deviceInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pName != NULL) { - ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); - - /* - For safety, make sure the length is based on the truncated output string rather than the - source. Otherwise the caller might assume the output buffer contains more content than it - actually does. - */ - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = strlen(pName); - } - } else { - /* Name not specified. Just report the length of the source string. */ - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_start(ma_device* pDevice) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - return MA_INVALID_OPERATION; /* Not initialized. */ - } - - if (ma_device_get_state(pDevice) == ma_device_state_started) { - return MA_SUCCESS; /* Already started. */ - } - - ma_mutex_lock(&pDevice->startStopLock); - { - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - - ma_device__set_state(pDevice, ma_device_state_starting); - - /* Asynchronous backends need to be handled differently. */ - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->pContext->callbacks.onDeviceStart != NULL) { - result = pDevice->pContext->callbacks.onDeviceStart(pDevice); - } else { - result = MA_INVALID_OPERATION; - } - - if (result == MA_SUCCESS) { - ma_device__set_state(pDevice, ma_device_state_started); - ma_device__on_notification_started(pDevice); - } - } else { - /* - Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the - thread and then wait for the start event. - */ - ma_event_signal(&pDevice->wakeupEvent); - - /* - Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device - into the started state. Don't call ma_device__set_state() here. - */ - ma_event_wait(&pDevice->startEvent); - result = pDevice->workResult; - } - - /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */ - if (result != MA_SUCCESS) { - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - ma_mutex_unlock(&pDevice->startStopLock); - - return result; -} - -MA_API ma_result ma_device_stop(ma_device* pDevice) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - return MA_INVALID_OPERATION; /* Not initialized. */ - } - - if (ma_device_get_state(pDevice) == ma_device_state_stopped) { - return MA_SUCCESS; /* Already stopped. */ - } - - ma_mutex_lock(&pDevice->startStopLock); - { - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); - - ma_device__set_state(pDevice, ma_device_state_stopping); - - /* Asynchronous backends need to be handled differently. */ - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - /* Asynchronous backends must have a stop operation. */ - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - result = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - result = MA_INVALID_OPERATION; - } - - ma_device__set_state(pDevice, ma_device_state_stopped); - } else { - /* - Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If - the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make - sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super - important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. - */ - MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); - - if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { - pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); - } - - /* - We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be - the one who puts the device into the stopped state. Don't call ma_device__set_state() here. - */ - ma_event_wait(&pDevice->stopEvent); - result = MA_SUCCESS; - } - } - ma_mutex_unlock(&pDevice->startStopLock); - - return result; -} - -MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice) -{ - return ma_device_get_state(pDevice) == ma_device_state_started; -} - -MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) -{ - if (pDevice == NULL) { - return ma_device_state_uninitialized; - } - - return (ma_device_state)c89atomic_load_i32((ma_int32*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ -} - -MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) -{ - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (volume < 0.0f) { - return MA_INVALID_ARGS; - } - - c89atomic_exchange_f32(&pDevice->masterVolumeFactor, volume); - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) -{ - if (pVolume == NULL) { - return MA_INVALID_ARGS; - } - - if (pDevice == NULL) { - *pVolume = 0; - return MA_INVALID_ARGS; - } - - *pVolume = c89atomic_load_f32(&pDevice->masterVolumeFactor); - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB) -{ - if (gainDB > 0) { - return MA_INVALID_ARGS; - } - - return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB)); -} - -MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB) -{ - float factor; - ma_result result; - - if (pGainDB == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_device_get_master_volume(pDevice, &factor); - if (result != MA_SUCCESS) { - *pGainDB = 0; - return result; - } - - *pGainDB = ma_volume_linear_to_db(factor); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) -{ - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (pOutput == NULL && pInput == NULL) { - return MA_INVALID_ARGS; - } - - if (pDevice->type == ma_device_type_duplex) { - if (pInput != NULL) { - ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); - } - - if (pOutput != NULL) { - ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb); - } - } else { - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) { - if (pInput == NULL) { - return MA_INVALID_ARGS; - } - - ma_device__send_frames_to_client(pDevice, frameCount, pInput); - } - - if (pDevice->type == ma_device_type_playback) { - if (pOutput == NULL) { - return MA_INVALID_ARGS; - } - - ma_device__read_frames_from_client(pDevice, frameCount, pOutput); - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - if (pDescriptor == NULL) { - return 0; - } - - /* - We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the - time when the size of the buffer needs to be determined. In this case we need to just take a best - guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll - just fall back to MA_DEFAULT_SAMPLE_RATE. - */ - if (nativeSampleRate == 0) { - nativeSampleRate = pDescriptor->sampleRate; - } - if (nativeSampleRate == 0) { - nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - MA_ASSERT(nativeSampleRate != 0); - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate); - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate); - } - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - return pDescriptor->periodSizeInFrames; - } -} -#endif /* MA_NO_DEVICE_IO */ - - -MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate) -{ - /* Prevent a division by zero. */ - if (sampleRate == 0) { - return 0; - } - - return bufferSizeInFrames*1000 / sampleRate; -} - -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) -{ - /* Prevent a division by zero. */ - if (sampleRate == 0) { - return 0; - } - - return bufferSizeInMilliseconds*sampleRate / 1000; -} - -MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - if (dst == src) { - return; /* No-op. */ - } - - ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels)); -} - -MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - if (format == ma_format_u8) { - ma_uint64 sampleCount = frameCount * channels; - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ((ma_uint8*)p)[iSample] = 128; - } - } else { - ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels)); - } -} - -MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) -{ - return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); -} - -MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) -{ - return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); -} - - -MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_u8(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s16(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - ma_int64 s = ma_clip_s24(pSrc[iSample]); - pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); - pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); - pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); - } -} - -MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s32(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_f32(pSrc[iSample]); - } -} - -MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - ma_uint64 sampleCount; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - sampleCount = frameCount * channels; - - switch (format) { - case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break; - case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break; - case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break; - case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break; - case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break; - - /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ - case ma_format_unknown: - case ma_format_count: - break; - } -} - - -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - ma_uint8* pSamplesOut8; - ma_uint8* pSamplesIn8; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - pSamplesOut8 = (ma_uint8*)pSamplesOut; - pSamplesIn8 = (ma_uint8*)pSamplesIn; - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ma_int32 sampleS32; - - sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24); - sampleS32 = (ma_int32)(sampleS32 * factor); - - pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8); - pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16); - pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - if (factor == 1) { - if (pSamplesOut == pSamplesIn) { - /* In place. No-op. */ - } else { - /* Just a copy. */ - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample]; - } - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample] * factor; - } - } -} - -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) -{ - switch (format) - { - case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return; - case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return; - case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return; - case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return; - case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return; - default: return; /* Do nothing. */ - } -} - -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor); -} - - -MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains) -{ - ma_uint64 iFrame; - - if (channels == 2) { - /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */ - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel]; - } - } -} - - - -static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume) -{ - return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8); -} - -static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume) -{ - return (ma_int32)((x * volume) >> 8); -} - -static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume) -{ - return (ma_int64)((x * volume) >> 8); -} - -static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume) -{ - return (ma_int64)((x * volume) >> 8); -} - -static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume) -{ - return x * volume; -} - - -MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed)); - pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); - pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); - pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */ - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume) -{ - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - if (volume == 1) { - ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */ - } else if (volume == 0) { - ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */ - } else { - ma_uint64 sampleCount = frameCount * channels; - - switch (format) { - case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break; - case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break; - case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; - case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; - case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break; - - /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ - case ma_format_unknown: - case ma_format_count: - break; - } - } -} - - - -MA_API float ma_volume_linear_to_db(float factor) -{ - return 20*ma_log10f(factor); -} - -MA_API float ma_volume_db_to_linear(float gain) -{ - return ma_powf(10, gain/20.0f); -} - - - -/************************************************************************************************************************************************************** - -Format Conversion - -**************************************************************************************************************************************************************/ - -static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x) -{ - return (ma_int16)(x * 32767.0f); -} - -static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x) -{ - return (ma_int16)((ma_int16)x - 128); -} - -static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x) -{ - return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40; /* Make sure the sign bits are maintained. */ -} - -static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24) -{ - s24[0] = (ma_uint8)((x & 0x000000FF) >> 0); - s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8); - s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16); -} - - -/* u8 */ -MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - ma_copy_memory_64(dst, src, count * sizeof(ma_uint8)); -} - - -static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_u8[i]; - x = (ma_int16)(x - 128); - x = (ma_int16)(x << 8); - dst_s16[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_u8_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_u8_to_s16__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_u8[i]; - x = (ma_int16)(x - 128); - - dst_s24[i*3+0] = 0; - dst_s24[i*3+1] = 0; - dst_s24[i*3+2] = (ma_uint8)((ma_int8)x); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_u8_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_u8_to_s24__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_u8[i]; - x = x - 128; - x = x << 24; - dst_s32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_u8_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_u8_to_s32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)src_u8[i]; - x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_u8_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_u8_to_f32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -#ifdef MA_USE_REFERENCE_CONVERSION_APIS -static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8** src_u8 = (const ma_uint8**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; - } - } -} -#else -static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8** src_u8 = (const ma_uint8**)src; - - if (channels == 1) { - ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8)); - } else if (channels == 2) { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - dst_u8[iFrame*2 + 0] = src_u8[0][iFrame]; - dst_u8[iFrame*2 + 1] = src_u8[1][iFrame]; - } - } else { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; - } - } - } -} -#endif - -MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_u8__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8** dst_u8 = (ma_uint8**)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); -#endif -} - - -/* s16 */ -static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_s16[i]; - x = (ma_int16)(x >> 8); - x = (ma_int16)(x + 128); - dst_u8[i] = (ma_uint8)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_s16[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F); - if ((x + dither) <= 0x7FFF) { - x = (ma_int16)(x + dither); - } else { - x = 0x7FFF; - } - - x = (ma_int16)(x >> 8); - x = (ma_int16)(x + 128); - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s16_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s16_to_u8__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - ma_copy_memory_64(dst, src, count * sizeof(ma_int16)); -} - - -static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s24[i*3+0] = 0; - dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF); - dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s16_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s16_to_s24__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s32[i] = src_s16[i] << 16; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s16_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s16_to_s32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)src_s16[i]; - -#if 0 - /* The accurate way. */ - x = x + 32768.0f; /* -32768..32767 to 0..65535 */ - x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ -#else - /* The fast way. */ - x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ -#endif - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s16_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s16_to_f32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_int16** src_s16 = (const ma_int16**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int16** dst_s16 = (ma_int16**)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); -#endif -} - - -/* s24 */ -static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128); - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s24_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s24_to_u8__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]); - ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8); - dst_s16[i] = (ma_int16)(dst_lo | dst_hi); - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } -} - -static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s24_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s24_to_s16__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * 3); -} - - -static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s24_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s24_to_s32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8); - -#if 0 - /* The accurate way. */ - x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */ - x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ -#else - /* The fast way. */ - x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */ -#endif - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s24_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s24_to_f32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst8 = (ma_uint8*)dst; - const ma_uint8** src8 = (const ma_uint8**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0]; - dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1]; - dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8** dst8 = (ma_uint8**)dst; - const ma_uint8* src8 = (const ma_uint8*)src; - - ma_uint32 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0]; - dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1]; - dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); -#endif -} - - - -/* s32 */ -static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s32_to_u8__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } -} - -static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s32_to_s16__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_uint32 x = (ma_uint32)src_s32[i]; - dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8); - dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16); - dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24); - } - - (void)ditherMode; /* No dithering for s32 -> s24. */ -} - -static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s32_to_s24__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * sizeof(ma_int32)); -} - - -static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - double x = src_s32[i]; - -#if 0 - x = x + 2147483648.0; - x = x * 0.0000000004656612873077392578125; - x = x - 1; -#else - x = x / 2147483648.0; -#endif - - dst_f32[i] = (float)x; - } - - (void)ditherMode; /* No dithering for s32 -> f32. */ -} - -static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s32_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s32_to_f32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_int32** src_s32 = (const ma_int32**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int32** dst_s32 = (ma_int32**)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); -#endif -} - - -/* f32 */ -static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - - ma_uint8* dst_u8 = (ma_uint8*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -128; - ditherMax = 1.0f / 127; - } - - for (i = 0; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 127.5f; /* 0..2 to 0..255 */ - - dst_u8[i] = (ma_uint8)x; - } -} - -static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_f32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_f32_to_u8__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - -#ifdef MA_USE_REFERENCE_CONVERSION_APIS -static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - - ma_int16* dst_s16 = (ma_int16*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - for (i = 0; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 32767.5f; /* 0..2 to 0..65535 */ - x = x - 32768.0f; /* 0...65535 to -32768..32767 */ -#else - /* The fast way. */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ -#endif - - dst_s16[i] = (ma_int16)x; - } -} -#else -static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i4; - ma_uint64 count4; - - ma_int16* dst_s16 = (ma_int16*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - /* Unrolled. */ - i = 0; - count4 = count >> 2; - for (i4 = 0; i4 < count4; i4 += 1) { - float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - - float x0 = src_f32[i+0]; - float x1 = src_f32[i+1]; - float x2 = src_f32[i+2]; - float x3 = src_f32[i+3]; - - x0 = x0 + d0; - x1 = x1 + d1; - x2 = x2 + d2; - x3 = x3 + d3; - - x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); - x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); - x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); - x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); - - x0 = x0 * 32767.0f; - x1 = x1 * 32767.0f; - x2 = x2 * 32767.0f; - x3 = x3 * 32767.0f; - - dst_s16[i+0] = (ma_int16)x0; - dst_s16[i+1] = (ma_int16)x1; - dst_s16[i+2] = (ma_int16)x2; - dst_s16[i+3] = (ma_int16)x3; - - i += 4; - } - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i8; - ma_uint64 count8; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - /* Both the input and output buffers need to be aligned to 16 bytes. */ - if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ - count8 = count >> 3; - for (i8 = 0; i8 < count8; i8 += 1) { - __m128 d0; - __m128 d1; - __m128 x0; - __m128 x1; - - if (ditherMode == ma_dither_mode_none) { - d0 = _mm_set1_ps(0); - d1 = _mm_set1_ps(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - d0 = _mm_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - d1 = _mm_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - } else { - d0 = _mm_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - d1 = _mm_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - } - - x0 = *((__m128*)(src_f32 + i) + 0); - x1 = *((__m128*)(src_f32 + i) + 1); - - x0 = _mm_add_ps(x0, d0); - x1 = _mm_add_ps(x1, d1); - - x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f)); - x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); - - _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); - - i += 8; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* SSE2 */ - -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_f32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i16; - ma_uint64 count16; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - /* Both the input and output buffers need to be aligned to 32 bytes. */ - if ((((ma_uintptr)dst & 31) != 0) || (((ma_uintptr)src & 31) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. */ - count16 = count >> 4; - for (i16 = 0; i16 < count16; i16 += 1) { - __m256 d0; - __m256 d1; - __m256 x0; - __m256 x1; - __m256i i0; - __m256i i1; - __m256i p0; - __m256i p1; - __m256i r; - - if (ditherMode == ma_dither_mode_none) { - d0 = _mm256_set1_ps(0); - d1 = _mm256_set1_ps(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - d0 = _mm256_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - d1 = _mm256_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - } else { - d0 = _mm256_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - d1 = _mm256_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - } - - x0 = *((__m256*)(src_f32 + i) + 0); - x1 = *((__m256*)(src_f32 + i) + 1); - - x0 = _mm256_add_ps(x0, d0); - x1 = _mm256_add_ps(x1, d1); - - x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f)); - x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f)); - - /* Computing the final result is a little more complicated for AVX2 than SSE2. */ - i0 = _mm256_cvttps_epi32(x0); - i1 = _mm256_cvttps_epi32(x1); - p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32); - p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48); - r = _mm256_packs_epi32(p0, p1); - - _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r); - - i += 16; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* AVX2 */ - -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i8; - ma_uint64 count8; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - if (!ma_has_neon()) { - return ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - } - - /* Both the input and output buffers need to be aligned to 16 bytes. */ - if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ - count8 = count >> 3; - for (i8 = 0; i8 < count8; i8 += 1) { - float32x4_t d0; - float32x4_t d1; - float32x4_t x0; - float32x4_t x1; - int32x4_t i0; - int32x4_t i1; - - if (ditherMode == ma_dither_mode_none) { - d0 = vmovq_n_f32(0); - d1 = vmovq_n_f32(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - float d0v[4]; - d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0 = vld1q_f32(d0v); - - float d1v[4]; - d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1 = vld1q_f32(d1v); - } else { - float d0v[4]; - d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0 = vld1q_f32(d0v); - - float d1v[4]; - d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1 = vld1q_f32(d1v); - } - - x0 = *((float32x4_t*)(src_f32 + i) + 0); - x1 = *((float32x4_t*)(src_f32 + i) + 1); - - x0 = vaddq_f32(x0, d0); - x1 = vaddq_f32(x1, d1); - - x0 = vmulq_n_f32(x0, 32767.0f); - x1 = vmulq_n_f32(x1, 32767.0f); - - i0 = vcvtq_s32_f32(x0); - i1 = vcvtq_s32_f32(x1); - *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1)); - - i += 8; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* Neon */ -#endif /* MA_USE_REFERENCE_CONVERSION_APIS */ - -MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_f32_to_s16__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const float* src_f32 = (const float*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 r; - float x = src_f32[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 8388607.5f; /* 0..2 to 0..16777215 */ - x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */ -#else - /* The fast way. */ - x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */ -#endif - - r = (ma_int32)x; - dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0); - dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8); - dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16); - } - - (void)ditherMode; /* No dithering for f32 -> s24. */ -} - -static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_f32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_f32_to_s24__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const float* src_f32 = (const float*)src; - - ma_uint32 i; - for (i = 0; i < count; i += 1) { - double x = src_f32[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 2147483647.5; /* 0..2 to 0..4294967295 */ - x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */ -#else - /* The fast way. */ - x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */ -#endif - - dst_s32[i] = (ma_int32)x; - } - - (void)ditherMode; /* No dithering for f32 -> s32. */ -} - -static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_f32_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); -#else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_f32_to_s32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 - if (ma_has_sse2()) { - ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON - if (ma_has_neon()) { - ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * sizeof(float)); -} - - -static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - float* dst_f32 = (float*)dst; - const float** src_f32 = (const float**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; - } - } -} - -static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels); -#endif -} - - -static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - float** dst_f32 = (float**)dst; - const float* src_f32 = (const float*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; - } - } -} - -static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); -#endif -} - - -MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode) -{ - if (formatOut == formatIn) { - ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut)); - return; - } - - switch (formatIn) - { - case ma_format_u8: - { - switch (formatOut) - { - case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s16: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s24: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s32: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_f32: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - default: break; - } -} - -MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode) -{ - ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode); -} - -MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) -{ - if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) { - return; /* Invalid args. */ - } - - /* For efficiency we do this per format. */ - switch (format) { - case ma_format_s16: - { - const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel]; - pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel]; - } - } - } break; - - case ma_format_f32: - { - const float* pSrcF32 = (const float*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel]; - pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel]; - } - } - } break; - - default: - { - ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); - const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); - memcpy(pDst, pSrc, sampleSizeInBytes); - } - } - } break; - } -} - -MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) -{ - switch (format) - { - case ma_format_s16: - { - ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel]; - pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame]; - } - } - } break; - - case ma_format_f32: - { - float* pDstF32 = (float*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel]; - pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame]; - } - } - } break; - - default: - { - ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); - const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); - memcpy(pDst, pSrc, sampleSizeInBytes); - } - } - } break; - } -} - - -/************************************************************************************************************************************************************** - -Biquad Filter - -**************************************************************************************************************************************************************/ -#ifndef MA_BIQUAD_FIXED_POINT_SHIFT -#define MA_BIQUAD_FIXED_POINT_SHIFT 14 -#endif - -static ma_int32 ma_biquad_float_to_fp(double x) -{ - return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT)); -} - -MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2) -{ - ma_biquad_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.b0 = b0; - config.b1 = b1; - config.b2 = b2; - config.a0 = a0; - config.a1 = a1; - config.a2 = a2; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; - size_t r2Offset; -} ma_biquad_heap_layout; - -static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R0 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* R1 */ - pHeapLayout->r2Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_biquad_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_biquad_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ) -{ - ma_result result; - ma_biquad_heap_layout heapLayout; - - if (pBQ == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBQ); - - result = ma_biquad_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pBQ->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset); - - return ma_biquad_reinit(pConfig, pBQ); -} - -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBQ->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pBQ == NULL) { - return; - } - - if (pBQ->_ownsHeap) { - ma_free(pBQ->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) -{ - if (pBQ == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->a0 == 0) { - return MA_INVALID_ARGS; /* Division by zero. */ - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - - pBQ->format = pConfig->format; - pBQ->channels = pConfig->channels; - - /* Normalize. */ - if (pConfig->format == ma_format_f32) { - pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0); - pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0); - pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0); - pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0); - pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0); - } else { - pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0); - pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0); - pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0); - pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0); - pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ) -{ - if (pBQ == NULL) { - return MA_INVALID_ARGS; - } - - if (pBQ->format == ma_format_f32) { - pBQ->pR1->f32 = 0; - pBQ->pR2->f32 = 0; - } else { - pBQ->pR1->s32 = 0; - pBQ->pR2->s32 = 0; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pBQ->channels; - const float b0 = pBQ->b0.f32; - const float b1 = pBQ->b1.f32; - const float b2 = pBQ->b2.f32; - const float a1 = pBQ->a1.f32; - const float a2 = pBQ->a2.f32; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pBQ->pR1[c].f32; - float r2 = pBQ->pR2[c].f32; - float x = pX[c]; - float y; - - y = b0*x + r1; - r1 = b1*x - a1*y + r2; - r2 = b2*x - a2*y; - - pY[c] = y; - pBQ->pR1[c].f32 = r1; - pBQ->pR2[c].f32 = r2; - } -} - -static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX) -{ - ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); -} - -static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pBQ->channels; - const ma_int32 b0 = pBQ->b0.s32; - const ma_int32 b1 = pBQ->b1.s32; - const ma_int32 b2 = pBQ->b2.s32; - const ma_int32 a1 = pBQ->a1.s32; - const ma_int32 a2 = pBQ->a2.s32; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pBQ->pR1[c].s32; - ma_int32 r2 = pBQ->pR2[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - r1 = (b1*x - a1*y + r2); - r2 = (b2*x - a2*y); - - pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); - pBQ->pR1[c].s32 = r1; - pBQ->pR2[c].s32 = r2; - } -} - -static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) -{ - ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); -} - -MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pBQ->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); - pY += pBQ->channels; - pX += pBQ->channels; - } - } else if (pBQ->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); - pY += pBQ->channels; - pX += pBQ->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ) -{ - if (pBQ == NULL) { - return 0; - } - - return 2; -} - - -/************************************************************************************************************************************************************** - -Low-Pass Filter - -**************************************************************************************************************************************************************/ -MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) -{ - ma_lpf1_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = 0.5; - - return config; -} - -MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_lpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; -} ma_lpf1_heap_layout; - -static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R1 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_lpf1_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF) -{ - ma_result result; - ma_lpf1_heap_layout heapLayout; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - - return ma_lpf1_reinit(pConfig, pLPF); -} - -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pLPF == NULL) { - return; - } - - if (pLPF->_ownsHeap) { - ma_free(pLPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) -{ - double a; - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - pLPF->format = pConfig->format; - pLPF->channels = pConfig->channels; - - a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); - if (pConfig->format == ma_format_f32) { - pLPF->a.f32 = (float)a; - } else { - pLPF->a.s32 = ma_biquad_float_to_fp(a); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - if (pLPF->format == ma_format_f32) { - pLPF->a.f32 = 0; - } else { - pLPF->a.s32 = 0; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pLPF->channels; - const float a = pLPF->a.f32; - const float b = 1 - a; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pLPF->pR1[c].f32; - float x = pX[c]; - float y; - - y = b*x + a*r1; - - pY[c] = y; - pLPF->pR1[c].f32 = y; - } -} - -static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pLPF->channels; - const ma_int32 a = pLPF->a.s32; - const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pLPF->pR1[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - - pY[c] = (ma_int16)y; - pLPF->pR1[c].s32 = (ma_int32)y; - } -} - -MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pLPF->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX); - pY += pLPF->channels; - pX += pLPF->channels; - } - } else if (pLPF->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX); - pY += pLPF->channels; - pX += pLPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return 1; -} - - -static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = (1 - c) / 2; - bqConfig.b1 = 1 - c; - bqConfig.b2 = (1 - c) / 2; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_lpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pLPF == NULL) { - return; - } - - ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pLPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - ma_biquad_clear_cache(&pLPF->bq); - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pLPF->bq); -} - - -MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_lpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t lpf1Offset; - size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ -} ma_lpf_heap_layout; - -static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count) -{ - MA_ASSERT(pLPF1Count != NULL); - MA_ASSERT(pLPF2Count != NULL); - - *pLPF1Count = order % 2; - *pLPF2Count = order / 2; -} - -static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); - - pHeapLayout->sizeInBytes = 0; - - /* LPF 1 */ - pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes; - for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { - size_t lpf1HeapSizeInBytes; - ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes; - } - - /* LPF 2*/ - pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes; - for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { - size_t lpf2HeapSizeInBytes; - ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_lpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset); - pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ - } - - for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { - ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - if (isNew) { - size_t lpf1HeapSizeInBytes; - - result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]); - } - } else { - result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jlpf1; - - for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { - ma_lpf2_config lpf2Config; - double q; - double a; - - /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ - if (lpf1Count == 1) { - a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ - } else { - a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ - } - q = 1 / (2*ma_cosd(a)); - - lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t lpf2HeapSizeInBytes; - - result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]); - } - } else { - result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jlpf1; - ma_uint32 jlpf2; - - for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) { - ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - pLPF->lpf1Count = lpf1Count; - pLPF->lpf2Count = lpf2Count; - pLPF->format = pConfig->format; - pLPF->channels = pConfig->channels; - pLPF->sampleRate = pConfig->sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_lpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_lpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return; - } - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks); - } - - if (pLPF->_ownsHeap) { - ma_free(pLPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) -{ - return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]); - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pLPF->format == ma_format_f32); - - MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY); - } -} - -static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pLPF->format == ma_format_s16); - - MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY); - } -} - -MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pLPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32); - pFramesOutF32 += pLPF->channels; - pFramesInF32 += pLPF->channels; - } - } else if (pLPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16); - pFramesOutS16 += pLPF->channels; - pFramesInS16 += pLPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return pLPF->lpf2Count*2 + pLPF->lpf1Count; -} - - -/************************************************************************************************************************************************************** - -High-Pass Filtering - -**************************************************************************************************************************************************************/ -MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) -{ - ma_hpf1_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - - return config; -} - -MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_hpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; -} ma_hpf1_heap_layout; - -static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R1 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_hpf1_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF) -{ - ma_result result; - ma_hpf1_heap_layout heapLayout; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - - return ma_hpf1_reinit(pConfig, pLPF); -} - -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pHPF == NULL) { - return; - } - - if (pHPF->_ownsHeap) { - ma_free(pHPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) -{ - double a; - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - pHPF->format = pConfig->format; - pHPF->channels = pConfig->channels; - - a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); - if (pConfig->format == ma_format_f32) { - pHPF->a.f32 = (float)a; - } else { - pHPF->a.s32 = ma_biquad_float_to_fp(a); - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pHPF->channels; - const float a = 1 - pHPF->a.f32; - const float b = 1 - a; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pHPF->pR1[c].f32; - float x = pX[c]; - float y; - - y = b*x - a*r1; - - pY[c] = y; - pHPF->pR1[c].f32 = y; - } -} - -static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pHPF->channels; - const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); - const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pHPF->pR1[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - - pY[c] = (ma_int16)y; - pHPF->pR1[c].s32 = (ma_int32)y; - } -} - -MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pHPF->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX); - pY += pHPF->channels; - pX += pHPF->channels; - } - } else if (pHPF->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX); - pY += pHPF->channels; - pX += pHPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return 1; -} - - -static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = (1 + c) / 2; - bqConfig.b1 = -(1 + c); - bqConfig.b2 = (1 + c) / 2; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_hpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pHPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pHPF == NULL) { - return; - } - - ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pHPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pHPF->bq); -} - - -MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_hpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t hpf1Offset; - size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ -} ma_hpf_heap_layout; - -static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count) -{ - MA_ASSERT(pHPF1Count != NULL); - MA_ASSERT(pHPF2Count != NULL); - - *pHPF1Count = order % 2; - *pHPF2Count = order / 2; -} - -static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); - - pHeapLayout->sizeInBytes = 0; - - /* HPF 1 */ - pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes; - for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { - size_t hpf1HeapSizeInBytes; - ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes; - } - - /* HPF 2*/ - pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes; - for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { - size_t hpf2HeapSizeInBytes; - ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_hpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pHPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset); - pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ - } - - for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { - ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - if (isNew) { - size_t hpf1HeapSizeInBytes; - - result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]); - } - } else { - result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jhpf1; - - for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { - ma_hpf2_config hpf2Config; - double q; - double a; - - /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ - if (hpf1Count == 1) { - a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ - } else { - a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ - } - q = 1 / (2*ma_cosd(a)); - - hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t hpf2HeapSizeInBytes; - - result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]); - } - } else { - result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jhpf1; - ma_uint32 jhpf2; - - for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) { - ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - pHPF->hpf1Count = hpf1Count; - pHPF->hpf2Count = hpf2Count; - pHPF->format = pConfig->format; - pHPF->channels = pConfig->channels; - pHPF->sampleRate = pConfig->sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_hpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_hpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pHPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - if (pHPF == NULL) { - return; - } - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks); - } - - if (pHPF->_ownsHeap) { - ma_free(pHPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF) -{ - return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pHPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pHPF->channels; - pFramesInF32 += pHPF->channels; - } - } else if (pHPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pHPF->channels; - pFramesInS16 += pHPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return pHPF->hpf2Count*2 + pHPF->hpf1Count; -} - - -/************************************************************************************************************************************************************** - -Band-Pass Filtering - -**************************************************************************************************************************************************************/ -MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_bpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = q * a; - bqConfig.b1 = 0; - bqConfig.b2 = -q * a; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_bpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pBPF == NULL) { - return; - } - - ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pBPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pBPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF) -{ - if (pBPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pBPF->bq); -} - - -MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_bpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t bpf2Offset; -} ma_bpf_heap_layout; - -static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 bpf2Count; - ma_uint32 ibpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - /* We must have an even number of order. */ - if ((pConfig->order & 0x1) != 0) { - return MA_INVALID_ARGS; - } - - bpf2Count = pConfig->channels / 2; - - pHeapLayout->sizeInBytes = 0; - - /* BPF 2 */ - pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes; - for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { - size_t bpf2HeapSizeInBytes; - ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 bpf2Count; - ma_uint32 ibpf2; - ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pBPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - /* We must have an even number of order. */ - if ((pConfig->order & 0x1) != 0) { - return MA_INVALID_ARGS; - } - - bpf2Count = pConfig->order / 2; - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pBPF->bpf2Count != bpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_bpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pBPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); - } - - for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { - ma_bpf2_config bpf2Config; - double q; - - /* TODO: Calculate Q to make this a proper Butterworth filter. */ - q = 0.707107; - - bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t bpf2HeapSizeInBytes; - - result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]); - } - } else { - result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]); - } - - if (result != MA_SUCCESS) { - return result; - } - } - - pBPF->bpf2Count = bpf2Count; - pBPF->format = pConfig->format; - pBPF->channels = pConfig->channels; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_bpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_bpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF) -{ - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBPF); - - return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ibpf2; - - if (pBPF == NULL) { - return; - } - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks); - } - - if (pBPF->_ownsHeap) { - ma_free(pBPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF) -{ - return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ibpf2; - - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pBPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pBPF->channels; - pFramesInF32 += pBPF->channels; - } - } else if (pBPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pBPF->channels; - pFramesInS16 += pBPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF) -{ - if (pBPF == NULL) { - return 0; - } - - return pBPF->bpf2Count*2; -} - - -/************************************************************************************************************************************************************** - -Notching Filter - -**************************************************************************************************************************************************************/ -MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) -{ - ma_notch2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.q = q; - config.frequency = frequency; - - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = 1; - bqConfig.b1 = -2 * c; - bqConfig.b2 = 1; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_notch2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - - -/************************************************************************************************************************************************************** - -Peaking EQ Filter - -**************************************************************************************************************************************************************/ -MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_peak2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.q = q; - config.frequency = frequency; - - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - double A; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - A = ma_powd(10, (pConfig->gainDB / 40)); - - bqConfig.b0 = 1 + (a * A); - bqConfig.b1 = -2 * c; - bqConfig.b2 = 1 - (a * A); - bqConfig.a0 = 1 + (a / A); - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - (a / A); - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_peak2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - -/************************************************************************************************************************************************************** - -Low Shelf Filter - -**************************************************************************************************************************************************************/ -MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) -{ - ma_loshelf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.shelfSlope = shelfSlope; - config.frequency = frequency; - - return config; -} - - -static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double w; - double s; - double c; - double A; - double S; - double a; - double sqrtA; - - MA_ASSERT(pConfig != NULL); - - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - A = ma_powd(10, (pConfig->gainDB / 40)); - S = pConfig->shelfSlope; - a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); - sqrtA = 2*ma_sqrtd(A)*a; - - bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA); - bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c); - bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA); - bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA; - bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c); - bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - -/************************************************************************************************************************************************************** - -High Shelf Filter - -**************************************************************************************************************************************************************/ -MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) -{ - ma_hishelf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.shelfSlope = shelfSlope; - config.frequency = frequency; - - return config; -} - - -static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double w; - double s; - double c; - double A; - double S; - double a; - double sqrtA; - - MA_ASSERT(pConfig != NULL); - - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - A = ma_powd(10, (pConfig->gainDB / 40)); - S = pConfig->shelfSlope; - a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); - sqrtA = 2*ma_sqrtd(A)*a; - - bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA); - bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c); - bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA); - bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA; - bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c); - bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - - -/* -Delay -*/ -MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) -{ - ma_delay_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.sampleRate = sampleRate; - config.delayInFrames = delayInFrames; - config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */ - config.wet = 1; - config.dry = 1; - config.decay = decay; - - return config; -} - - -MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay) -{ - if (pDelay == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDelay); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->decay < 0 || pConfig->decay > 1) { - return MA_INVALID_ARGS; - } - - pDelay->config = *pConfig; - pDelay->bufferSizeInFrames = pConfig->delayInFrames; - pDelay->cursor = 0; - - pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks); - if (pDelay->pBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels); - - return MA_SUCCESS; -} - -MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pDelay == NULL) { - return; - } - - ma_free(pDelay->pBuffer, pAllocationCallbacks); -} - -MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - ma_uint32 iFrame; - ma_uint32 iChannel; - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) { - ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel; - - if (pDelay->config.delayStart) { - /* Delayed start. */ - - /* Read */ - pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; - - /* Feedback */ - pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); - } else { - /* Immediate start */ - - /* Feedback */ - pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); - - /* Read */ - pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; - } - } - - pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames; - - pFramesOutF32 += pDelay->config.channels; - pFramesInF32 += pDelay->config.channels; - } - - return MA_SUCCESS; -} - -MA_API void ma_delay_set_wet(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.wet = value; -} - -MA_API float ma_delay_get_wet(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.wet; -} - -MA_API void ma_delay_set_dry(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.dry = value; -} - -MA_API float ma_delay_get_dry(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.dry; -} - -MA_API void ma_delay_set_decay(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.decay = value; -} - -MA_API float ma_delay_get_decay(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.decay; -} - - -MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames) -{ - ma_gainer_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.smoothTimeInFrames = smoothTimeInFrames; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t oldGainsOffset; - size_t newGainsOffset; -} ma_gainer_heap_layout; - -static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Old gains. */ - pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - - /* New gains. */ - pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - - /* Alignment. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_gainer_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_gainer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer) -{ - ma_result result; - ma_gainer_heap_layout heapLayout; - ma_uint32 iChannel; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pGainer); - - if (pConfig == NULL || pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_gainer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pGainer->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); - pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); - - pGainer->config = *pConfig; - pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pGainer->pOldGains[iChannel] = 1; - pGainer->pNewGains[iChannel] = 1; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap allocation. */ - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pGainer->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pGainer == NULL) { - return; - } - - if (pGainer->_ownsHeap) { - ma_free(pGainer->_pHeap, pAllocationCallbacks); - } -} - -static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel) -{ - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); -} - -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - if (pGainer->t >= pGainer->config.smoothTimeInFrames) { - /* Fast path. No gain calculation required. */ - ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); - - /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = pGainer->config.smoothTimeInFrames; - } - } else { - /* Slow path. Need to interpolate the gain for each channel individually. */ - - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - float d = 1.0f / pGainer->config.smoothTimeInFrames; - ma_uint32 channelCount = pGainer->config.channels; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channelCount; iChannel += 1) { - pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a); - } - - pFramesOutF32 += channelCount; - pFramesInF32 += channelCount; - - a += d; - if (a > 1) { - a = 1; - } - } - } - - pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames); - - #if 0 /* Reference implementation. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel); - } - } - - /* Move interpolation time forward, but don't go beyond our smoothing time. */ - pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames); - } - #endif - } - - return MA_SUCCESS; -} - -static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) -{ - pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); - pGainer->pNewGains[iChannel] = newGain; -} - -static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer) -{ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */ - } else { - pGainer->t = 0; - } -} - -MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain) -{ - ma_uint32 iChannel; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ma_gainer_set_gain_by_index(pGainer, newGain, iChannel); - } - - /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ - ma_gainer_reset_smoothing_time(pGainer); - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) -{ - ma_uint32 iChannel; - - if (pGainer == NULL || pNewGains == NULL) { - return MA_INVALID_ARGS; - } - - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel); - } - - /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ - ma_gainer_reset_smoothing_time(pGainer); - - return MA_SUCCESS; -} - - -MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) -{ - ma_panner_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */ - config.pan = 0; - - return config; -} - - -MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner) -{ - if (pPanner == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pPanner); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pPanner->format = pConfig->format; - pPanner->channels = pConfig->channels; - pPanner->mode = pConfig->mode; - pPanner->pan = pConfig->pan; - - return MA_SUCCESS; -} - -static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) -{ - ma_uint64 iFrame; - - if (pan > 0) { - float factor = 1.0f - pan; - if (pFramesOut == pFramesIn) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1]; - } - } - } else { - float factor = 1.0f + pan; - if (pFramesOut == pFramesIn) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0]; - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; - } - } - } -} - -static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) -{ - if (pan == 0) { - /* Fast path. No panning required. */ - if (pFramesOut == pFramesIn) { - /* No-op */ - } else { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } - - return; - } - - switch (format) { - case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; - - /* Unknown format. Just copy. */ - default: - { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } break; - } -} - - -static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) -{ - ma_uint64 iFrame; - - if (pan > 0) { - float factorL0 = 1.0f - pan; - float factorL1 = 0.0f + pan; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0); - float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1]; - - pFramesOut[iFrame*2 + 0] = sample0; - pFramesOut[iFrame*2 + 1] = sample1; - } - } else { - float factorR0 = 0.0f - pan; - float factorR1 = 1.0f + pan; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0); - float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1); - - pFramesOut[iFrame*2 + 0] = sample0; - pFramesOut[iFrame*2 + 1] = sample1; - } - } -} - -static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) -{ - if (pan == 0) { - /* Fast path. No panning required. */ - if (pFramesOut == pFramesIn) { - /* No-op */ - } else { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } - - return; - } - - switch (format) { - case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; - - /* Unknown format. Just copy. */ - default: - { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } break; - } -} - -MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - if (pPanner->channels == 2) { - /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */ - if (pPanner->mode == ma_pan_mode_balance) { - ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); - } else { - ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); - } - } else { - if (pPanner->channels == 1) { - /* Panning has no effect on mono streams. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); - } else { - /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); - } - } - - return MA_SUCCESS; -} - -MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode) -{ - if (pPanner == NULL) { - return; - } - - pPanner->mode = mode; -} - -MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner) -{ - if (pPanner == NULL) { - return ma_pan_mode_balance; - } - - return pPanner->mode; -} - -MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan) -{ - if (pPanner == NULL) { - return; - } - - pPanner->pan = ma_clamp(pan, -1.0f, 1.0f); -} - -MA_API float ma_panner_get_pan(const ma_panner* pPanner) -{ - if (pPanner == NULL) { - return 0; - } - - return pPanner->pan; -} - - - - -MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_fader_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - - return config; -} - - -MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) -{ - if (pFader == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFader); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only f32 is supported for now. */ - if (pConfig->format != ma_format_f32) { - return MA_INVALID_ARGS; - } - - pFader->config = *pConfig; - pFader->volumeBeg = 1; - pFader->volumeEnd = 1; - pFader->lengthInFrames = 0; - pFader->cursorInFrames = 0; - - return MA_SUCCESS; -} - -MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFader == NULL) { - return MA_INVALID_ARGS; - } - - /* - For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for - the conversion to a float which we use for the linear interpolation. This might be changed later. - */ - if (frameCount + pFader->cursorInFrames > UINT_MAX) { - frameCount = UINT_MAX - pFader->cursorInFrames; - } - - /* Optimized path if volumeBeg and volumeEnd are equal. */ - if (pFader->volumeBeg == pFader->volumeEnd) { - if (pFader->volumeBeg == 1) { - /* Straight copy. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); - } else { - /* Copy with volume. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); - } - } else { - /* Slower path. Volumes are different, so may need to do an interpolation. */ - if (pFader->cursorInFrames >= pFader->lengthInFrames) { - /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); - } else { - /* Slow path. This is where we do the actual fading. */ - ma_uint64 iFrame; - ma_uint32 iChannel; - - /* For now we only support f32. Support for other formats will be added later. */ - if (pFader->config.format == ma_format_f32) { - const float* pFramesInF32 = (const float*)pFramesIn; - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ - float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); - - for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; - } - } - } else { - return MA_NOT_IMPLEMENTED; - } - } - } - - pFader->cursorInFrames += frameCount; - - return MA_SUCCESS; -} - -MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) -{ - if (pFader == NULL) { - return; - } - - if (pFormat != NULL) { - *pFormat = pFader->config.format; - } - - if (pChannels != NULL) { - *pChannels = pFader->config.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pFader->config.sampleRate; - } -} - -MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) -{ - if (pFader == NULL) { - return; - } - - /* If the volume is negative, use current volume. */ - if (volumeBeg < 0) { - volumeBeg = ma_fader_get_current_volume(pFader); - } - - /* - The length needs to be clamped to 32-bits due to how we convert it to a float for linear - interpolation reasons. I might change this requirement later, but for now it's not important. - */ - if (lengthInFrames > UINT_MAX) { - lengthInFrames = UINT_MAX; - } - - pFader->volumeBeg = volumeBeg; - pFader->volumeEnd = volumeEnd; - pFader->lengthInFrames = lengthInFrames; - pFader->cursorInFrames = 0; /* Reset cursor. */ -} - -MA_API float ma_fader_get_current_volume(ma_fader* pFader) -{ - if (pFader == NULL) { - return 0.0f; - } - - /* The current volume depends on the position of the cursor. */ - if (pFader->cursorInFrames == 0) { - return pFader->volumeBeg; - } else if (pFader->cursorInFrames >= pFader->lengthInFrames) { - return pFader->volumeEnd; - } else { - /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */ - return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ - } -} - - - - - -MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z) -{ - ma_vec3f v; - - v.x = x; - v.y = y; - v.z = z; - - return v; -} - -MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_init_3f( - a.x - b.x, - a.y - b.y, - a.z - b.z - ); -} - -MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a) -{ - return ma_vec3f_init_3f( - -a.x, - -a.y, - -a.z - ); -} - -MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b) -{ - return a.x*b.x + a.y*b.y + a.z*b.z; -} - -MA_API float ma_vec3f_len2(ma_vec3f v) -{ - return ma_vec3f_dot(v, v); -} - -MA_API float ma_vec3f_len(ma_vec3f v) -{ - return (float)ma_sqrtd(ma_vec3f_len2(v)); -} - -MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_len(ma_vec3f_sub(a, b)); -} - -MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) -{ - float f; - float l = ma_vec3f_len(v); - if (l == 0) { - return ma_vec3f_init_3f(0, 0, 0); - } - - f = 1 / l; - v.x *= f; - v.y *= f; - v.z *= f; - - return v; -} - -MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_init_3f( - a.y*b.z - a.z*b.y, - a.z*b.x - a.x*b.z, - a.x*b.y - a.y*b.x - ); -} - - - -static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); -static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); - - -#ifndef MA_DEFAULT_SPEED_OF_SOUND -#define MA_DEFAULT_SPEED_OF_SOUND 343.3f -#endif - -/* -These vectors represent the direction that speakers are facing from the center point. They're used -for panning in the spatializer. Must be normalized. -*/ -static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = { - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */ - {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */ - {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */ - {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */ - {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */ - {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */ - {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ - { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */ - {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */ - {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */ - { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */ - {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */ - { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */ - {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */ - {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */ - { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */ - {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */ - { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */ -}; - -static ma_vec3f ma_get_channel_direction(ma_channel channel) -{ - if (channel >= MA_CHANNEL_POSITION_COUNT) { - return ma_vec3f_init_3f(0, 0, -1); - } else { - return g_maChannelDirections[channel]; - } -} - - - -static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance)); -} - -static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance); -} - -static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff); -} - - -/* -Dopper Effect calculation taken from the OpenAL spec, with two main differences: - - 1) The source to listener vector will have already been calcualted at an earlier step so we can - just use that directly. We need only the position of the source relative to the origin. - - 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight - into the resampler directly. -*/ -static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor) -{ - float len; - float vls; - float vss; - - len = ma_vec3f_len(relativePosition); - - /* - There's a case where the position of the source will be right on top of the listener in which - case the length will be 0 and we'll end up with a division by zero. We can just return a ratio - of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary. - */ - if (len == 0) { - return 1.0; - } - - vls = ma_vec3f_dot(relativePosition, listenVelocity) / len; - vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len; - - vls = ma_min(vls, speedOfSound / dopplerFactor); - vss = ma_min(vss, speedOfSound / dopplerFactor); - - return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss); -} - - -static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount) -{ - /* - Special case for stereo. Want to default the left and right speakers to side left and side - right so that they're facing directly down the X axis rather than slightly forward. Not - doing this will result in sounds being quieter when behind the listener. This might - actually be good for some scenerios, but I don't think it's an appropriate default because - it can be a bit unexpected. - */ - if (channelCount == 2) { - pChannelMap[0] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT; - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } -} - - -MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut) -{ - ma_spatializer_listener_config config; - - MA_ZERO_OBJECT(&config); - config.channelsOut = channelsOut; - config.pChannelMapOut = NULL; - config.handedness = ma_handedness_right; - config.worldUp = ma_vec3f_init_3f(0, 1, 0); - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterGain = 0; - config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */ - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapOutOffset; -} ma_spatializer_listener_heap_layout; - -static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel map. We always need this, even for passthroughs. */ - pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_spatializer_listener_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener) -{ - ma_result result; - ma_spatializer_listener_heap_layout heapLayout; - - if (pListener == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pListener); - - result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pListener->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pListener->config = *pConfig; - pListener->position = ma_vec3f_init_3f(0, 0, 0); - pListener->direction = ma_vec3f_init_3f(0, 0, -1); - pListener->velocity = ma_vec3f_init_3f(0, 0, 0); - pListener->isEnabled = MA_TRUE; - - /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ - if (pListener->config.handedness == ma_handedness_left) { - pListener->direction = ma_vec3f_neg(pListener->direction); - } - - - /* We must always have a valid channel map. */ - pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); - - /* Use a slightly different default channel map for stereo. */ - if (pConfig->pChannelMapOut == NULL) { - ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut); - } else { - ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pListener->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pListener == NULL) { - return; - } - - if (pListener->_ownsHeap) { - ma_free(pListener->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return NULL; - } - - return pListener->config.pChannelMapOut; -} - -MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pListener == NULL) { - return; - } - - pListener->config.coneInnerAngleInRadians = innerAngleInRadians; - pListener->config.coneOuterAngleInRadians = outerAngleInRadians; - pListener->config.coneOuterGain = outerGain; -} - -MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pListener == NULL) { - return; - } - - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians; - } - - if (pOuterGain != NULL) { - *pOuterGain = pListener->config.coneOuterGain; - } -} - -MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - pListener->position = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return pListener->position; -} - -MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - pListener->direction = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return pListener->direction; -} - -MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - pListener->velocity = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return pListener->velocity; -} - -MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) -{ - if (pListener == NULL) { - return; - } - - pListener->config.speedOfSound = speedOfSound; -} - -MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return 0; - } - - return pListener->config.speedOfSound; -} - -MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - pListener->config.worldUp = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 1, 0); - } - - return pListener->config.worldUp; -} - -MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled) -{ - if (pListener == NULL) { - return; - } - - pListener->isEnabled = isEnabled; -} - -MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return MA_FALSE; - } - - return pListener->isEnabled; -} - - - - -MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut) -{ - ma_spatializer_config config; - - MA_ZERO_OBJECT(&config); - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = NULL; - config.attenuationModel = ma_attenuation_model_inverse; - config.positioning = ma_positioning_absolute; - config.handedness = ma_handedness_right; - config.minGain = 0; - config.maxGain = 1; - config.minDistance = 1; - config.maxDistance = MA_FLT_MAX; - config.rolloff = 1; - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */ - config.coneOuterGain = 0.0f; - config.dopplerFactor = 1; - config.directionalAttenuationFactor = 1; - config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ - - return config; -} - - -static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames); -} - -static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapInOffset; - size_t newChannelGainsOffset; - size_t gainerOffset; -} ma_spatializer_heap_layout; - -static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_spatializer_validate_config(pConfig); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel map. */ - pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */ - if (pConfig->pChannelMapIn != NULL) { - pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn); - } - - /* New channel gains for output. */ - pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut); - - /* Gainer. */ - { - size_t gainerHeapSizeInBytes; - ma_gainer_config gainerConfig; - - gainerConfig = ma_spatializer_gainer_config_init(pConfig); - - result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_spatializer_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; /* Safety. */ - - result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer) -{ - ma_result result; - ma_spatializer_heap_layout heapLayout; - ma_gainer_config gainerConfig; - - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSpatializer); - - if (pConfig == NULL || pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pSpatializer->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pSpatializer->channelsIn = pConfig->channelsIn; - pSpatializer->channelsOut = pConfig->channelsOut; - pSpatializer->attenuationModel = pConfig->attenuationModel; - pSpatializer->positioning = pConfig->positioning; - pSpatializer->handedness = pConfig->handedness; - pSpatializer->minGain = pConfig->minGain; - pSpatializer->maxGain = pConfig->maxGain; - pSpatializer->minDistance = pConfig->minDistance; - pSpatializer->maxDistance = pConfig->maxDistance; - pSpatializer->rolloff = pConfig->rolloff; - pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians; - pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; - pSpatializer->coneOuterGain = pConfig->coneOuterGain; - pSpatializer->dopplerFactor = pConfig->dopplerFactor; - pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; - pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; - pSpatializer->position = ma_vec3f_init_3f(0, 0, 0); - pSpatializer->direction = ma_vec3f_init_3f(0, 0, -1); - pSpatializer->velocity = ma_vec3f_init_3f(0, 0, 0); - pSpatializer->dopplerPitch = 1; - - /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ - if (pSpatializer->handedness == ma_handedness_left) { - pSpatializer->direction = ma_vec3f_neg(pSpatializer->direction); - } - - /* Channel map. This will be on the heap. */ - if (pConfig->pChannelMapIn != NULL) { - pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); - ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn); - } - - /* New channel gains for output channels. */ - pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset); - - /* Gainer. */ - gainerConfig = ma_spatializer_gainer_config_init(pConfig); - - result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the gainer. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - /* We'll need a heap allocation to retrieve the size. */ - result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pSpatializer->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pSpatializer == NULL) { - return; - } - - ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks); - - if (pSpatializer->_ownsHeap) { - ma_free(pSpatializer->_pHeap, pAllocationCallbacks); - } -} - -static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain) -{ - /* - Angular attenuation. - - Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure - this out for ourselves at the expense of possibly being inconsistent with other implementations. - - To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We - just need to get the direction from the source to the listener and then do a dot product against that and the - direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than - the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. - */ - if (coneInnerAngleInRadians < 6.283185f) { - float angularGain = 1; - float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f); - float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f); - float d; - - d = ma_vec3f_dot(dirA, dirB); - - if (d > cutoffInner) { - /* It's inside the inner angle. */ - angularGain = 1; - } else { - /* It's outside the inner angle. */ - if (d > cutoffOuter) { - /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */ - angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter)); - } else { - /* It's outside the outer angle. */ - angularGain = coneOuterGain; - } - } - - /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/ - return angularGain; - } else { - /* Inner angle is 360 degrees so no need to do any attenuation. */ - return 1; - } -} - -MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn; - ma_channel* pChannelMapOut = pListener->config.pChannelMapOut; - - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - /* If we're not spatializing we need to run an optimized path. */ - if (c89atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { - if (ma_spatializer_listener_is_enabled(pListener)) { - /* No attenuation is required, but we'll need to do some channel conversion. */ - if (pSpatializer->channelsIn == pSpatializer->channelsOut) { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn); - } else { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); /* Safe casts to float* because f32 is the only supported format. */ - } - } else { - /* The listener is disabled. Output silence. */ - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } - - /* - We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is - the correct thinking so might need to review this later. - */ - pSpatializer->dopplerPitch = 1; - } else { - /* - Let's first determine which listener the sound is closest to. Need to keep in mind that we - might not have a world or any listeners, in which case we just spatializer based on the - listener being positioned at the origin (0, 0, 0). - */ - ma_vec3f relativePosNormalized; - ma_vec3f relativePos; /* The position relative to the listener. */ - ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ - ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */ - float speedOfSound; - float distance = 0; - float gain = 1; - ma_uint32 iChannel; - const ma_uint32 channelsOut = pSpatializer->channelsOut; - const ma_uint32 channelsIn = pSpatializer->channelsIn; - float minDistance = ma_spatializer_get_min_distance(pSpatializer); - float maxDistance = ma_spatializer_get_max_distance(pSpatializer); - float rolloff = ma_spatializer_get_rolloff(pSpatializer); - float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer); - - /* - We'll need the listener velocity for doppler pitch calculations. The speed of sound is - defined by the listener, so we'll grab that here too. - */ - if (pListener != NULL) { - listenerVel = pListener->velocity; - speedOfSound = pListener->config.speedOfSound; - } else { - listenerVel = ma_vec3f_init_3f(0, 0, 0); - speedOfSound = MA_DEFAULT_SPEED_OF_SOUND; - } - - if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { - /* There's no listener or we're using relative positioning. */ - relativePos = pSpatializer->position; - relativeDir = pSpatializer->direction; - } else { - /* - We've found a listener and we're using absolute positioning. We need to transform the - sound's position and direction so that it's relative to listener. Later on we'll use - this for determining the factors to apply to each channel to apply the panning effect. - */ - ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); - } - - distance = ma_vec3f_len(relativePos); - - /* We've gathered the data, so now we can apply some spatialization. */ - switch (ma_spatializer_get_attenuation_model(pSpatializer)) { - case ma_attenuation_model_inverse: - { - gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_linear: - { - gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_exponential: - { - gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_none: - default: - { - gain = 1; - } break; - } - - /* Normalize the position. */ - if (distance > 0.001f) { - float distanceInv = 1/distance; - relativePosNormalized = relativePos; - relativePosNormalized.x *= distanceInv; - relativePosNormalized.y *= distanceInv; - relativePosNormalized.z *= distanceInv; - } else { - distance = 0; - relativePosNormalized = ma_vec3f_init_3f(0, 0, 0); - } - - /* - Angular attenuation. - - Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure - this out for ourselves at the expense of possibly being inconsistent with other implementations. - - To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We - just need to get the direction from the source to the listener and then do a dot product against that and the - direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than - the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. - */ - if (distance > 0) { - /* Source anglular gain. */ - float spatializerConeInnerAngle; - float spatializerConeOuterAngle; - float spatializerConeOuterGain; - ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain); - - gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain); - - /* - We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that - are positioned behind the listener. On default settings, this will have no effect. - */ - if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) { - ma_vec3f listenerDirection; - float listenerInnerAngle; - float listenerOuterAngle; - float listenerOuterGain; - - if (pListener->config.handedness == ma_handedness_right) { - listenerDirection = ma_vec3f_init_3f(0, 0, -1); - } else { - listenerDirection = ma_vec3f_init_3f(0, 0, +1); - } - - listenerInnerAngle = pListener->config.coneInnerAngleInRadians; - listenerOuterAngle = pListener->config.coneOuterAngleInRadians; - listenerOuterGain = pListener->config.coneOuterGain; - - gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain); - } - } else { - /* The sound is right on top of the listener. Don't do any angular attenuation. */ - } - - - /* Clamp the gain. */ - gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); - - /* - Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for - when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the - gain to the final output. - */ - /*printf("distance=%f; gain=%f\n", distance, gain);*/ - - /* We must have a valid channel map here to ensure we spatialize properly. */ - MA_ASSERT(pChannelMapOut != NULL); - - /* - We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being - to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that - the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and - seeing how it goes. There might be better ways to do this. - - To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a - direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will - be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized - position of the sound. - */ - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - pSpatializer->pNewChannelGainsOut[iChannel] = gain; - } - - /* - Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore - the whole section of code here because we need to update some internal spatialization state. - */ - if (ma_spatializer_listener_is_enabled(pListener)) { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } - - /* - Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's - relation to the direction of the channel. - */ - if (distance > 0) { - ma_vec3f unitPos = relativePos; - float distanceInv = 1/distance; - unitPos.x *= distanceInv; - unitPos.y *= distanceInv; - unitPos.z *= distanceInv; - - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - ma_channel channelOut; - float d; - float dMin; - - channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); - if (ma_is_spatial_channel_position(channelOut)) { - d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer)); - } else { - d = 1; /* It's not a spatial channel so there's no real notion of direction. */ - } - - /* - In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable. - The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to - 0, panning will be most extreme and any sounds that are positioned on the opposite side of the - speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it - doesn't even remotely represent the real world at all because sounds that come from your right side - are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at - all, which is also not ideal. By setting it to something greater than 0, the spatialization effect - becomes much less dramatic and a lot more bearable. - - Summary: 0 = more extreme panning; 1 = no panning. - */ - dMin = 0.2f; /* TODO: Consider making this configurable. */ - - /* - At this point, "d" will be positive if the sound is on the same side as the channel and negative if - it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to - calculate a panning value. The first is to simply convert it to 0..1, however this has a problem - which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right - in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like - the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front - of the listener. I would intuitively expect that to be played at full volume, or close to it. - - The second idea I think of is to only apply a reduction in gain when the sound is on the opposite - side of the speaker. That is, reduce the gain only when the dot product is negative. The problem - with this is that there will not be any attenuation as the sound sweeps around the 180 degrees - where the dot product is positive. The idea with this option is that you leave the gain at 1 when - the sound is being played on the same side as the speaker and then you just reduce the volume when - the sound is on the other side. - - The summarize, I think the first option should give a better sense of spatialization, but the second - option is better for preserving the sound's power. - - UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a - bit better, but you can also hear the reduction in volume when it's right in front. - */ - #if 1 - { - /* - Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power - by being played at 0.5 gain. - */ - d = (d + 1) * 0.5f; /* -1..1 to 0..1 */ - d = ma_max(d, dMin); - pSpatializer->pNewChannelGainsOut[iChannel] *= d; - } - #else - { - /* - Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more - consistent, but comes at the expense of a worse sense of space and positioning. - */ - if (d < 0) { - d += 1; /* Move into the positive range. */ - d = ma_max(d, dMin); - channelGainsOut[iChannel] *= d; - } - } - #endif - } - } else { - /* Assume the sound is right on top of us. Don't do any panning. */ - } - - /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */ - ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut); - ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount); - - /* - Before leaving we'll want to update our doppler pitch so that the caller can apply some - pitch shifting if they desire. Note that we need to negate the relative position here - because the doppler calculation needs to be source-to-listener, but ours is listener-to- - source. - */ - if (dopplerFactor > 0) { - pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(pListener->position, pSpatializer->position), pSpatializer->velocity, listenerVel, speedOfSound, dopplerFactor); - } else { - pSpatializer->dopplerPitch = 1; - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return pSpatializer->channelsIn; -} - -MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return pSpatializer->channelsOut; -} - -MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel) -{ - if (pSpatializer == NULL) { - return; - } - - c89atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); -} - -MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_attenuation_model_none; - } - - return (ma_attenuation_model)c89atomic_load_i32(&pSpatializer->attenuationModel); -} - -MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) -{ - if (pSpatializer == NULL) { - return; - } - - c89atomic_exchange_i32(&pSpatializer->positioning, positioning); -} - -MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_positioning_absolute; - } - - return (ma_positioning)c89atomic_load_i32(&pSpatializer->positioning); -} - -MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) -{ - if (pSpatializer == NULL) { - return; - } - - c89atomic_exchange_f32(&pSpatializer->rolloff, rolloff); -} - -MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return c89atomic_load_f32(&pSpatializer->rolloff); -} - -MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) -{ - if (pSpatializer == NULL) { - return; - } - - c89atomic_exchange_f32(&pSpatializer->minGain, minGain); -} - -MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return c89atomic_load_f32(&pSpatializer->minGain); -} - -MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) -{ - if (pSpatializer == NULL) { - return; - } - - c89atomic_exchange_f32(&pSpatializer->maxGain, maxGain); -} - -MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return c89atomic_load_f32(&pSpatializer->maxGain); -} - -MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) -{ - if (pSpatializer == NULL) { - return; - } - - c89atomic_exchange_f32(&pSpatializer->minDistance, minDistance); -} - -MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return c89atomic_load_f32(&pSpatializer->minDistance); -} - -MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) -{ - if (pSpatializer == NULL) { - return; - } - - c89atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); -} - -MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return c89atomic_load_f32(&pSpatializer->maxDistance); -} - -MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pSpatializer == NULL) { - return; - } - - c89atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); - c89atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); - c89atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); -} - -MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pSpatializer == NULL) { - return; - } - - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = c89atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = c89atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); - } - - if (pOuterGain != NULL) { - *pOuterGain = c89atomic_load_f32(&pSpatializer->coneOuterGain); - } -} - -MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor) -{ - if (pSpatializer == NULL) { - return; - } - - c89atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); -} - -MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 1; - } - - return c89atomic_load_f32(&pSpatializer->dopplerFactor); -} - -MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) -{ - if (pSpatializer == NULL) { - return; - } - - c89atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); -} - -MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 1; - } - - return c89atomic_load_f32(&pSpatializer->directionalAttenuationFactor); -} - -MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - pSpatializer->position = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return pSpatializer->position; -} - -MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - pSpatializer->direction = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return pSpatializer->direction; -} - -MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - pSpatializer->velocity = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return pSpatializer->velocity; -} - -MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) -{ - if (pRelativePos != NULL) { - pRelativePos->x = 0; - pRelativePos->y = 0; - pRelativePos->z = 0; - } - - if (pRelativeDir != NULL) { - pRelativeDir->x = 0; - pRelativeDir->y = 0; - pRelativeDir->z = -1; - } - - if (pSpatializer == NULL) { - return; - } - - if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { - /* There's no listener or we're using relative positioning. */ - if (pRelativePos != NULL) { - *pRelativePos = pSpatializer->position; - } - if (pRelativeDir != NULL) { - *pRelativeDir = pSpatializer->direction; - } - } else { - ma_vec3f v; - ma_vec3f axisX; - ma_vec3f axisY; - ma_vec3f axisZ; - float m[4][4]; - - /* - We need to calcualte the right vector from our forward and up vectors. This is done with - a cross product. - */ - axisZ = ma_vec3f_normalize(pListener->direction); /* Normalization required here because we can't trust the caller. */ - axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ - - /* - The calculation of axisX above can result in a zero-length vector if the listener is - looking straight up on the Y axis. We'll need to fall back to a +X in this case so that - the calculations below don't fall apart. This is where a quaternion based listener and - sound orientation would come in handy. - */ - if (ma_vec3f_len2(axisX) == 0) { - axisX = ma_vec3f_init_3f(1, 0, 0); - } - - axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ - - /* - We need to swap the X axis if we're left handed because otherwise the cross product above - will have resulted in it pointing in the wrong direction (right handed was assumed in the - cross products above). - */ - if (pListener->config.handedness == ma_handedness_left) { - axisX = ma_vec3f_neg(axisX); - } - - /* Lookat. */ - m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, pListener->position); - m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, pListener->position); - m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), pListener->position); - m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; - - /* - Multiply the lookat matrix by the spatializer position to transform it to listener - space. This allows calculations to work based on the sound being relative to the - origin which makes things simpler. - */ - if (pRelativePos != NULL) { - v = pSpatializer->position; - pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; - pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; - pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; - } - - /* - The direction of the sound needs to also be transformed so that it's relative to the - rotation of the listener. - */ - if (pRelativeDir != NULL) { - v = pSpatializer->direction; - pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; - pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; - pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; - } - } -} - - - - -/************************************************************************************************************************************************************** - -Resampling - -**************************************************************************************************************************************************************/ -MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_linear_resampler_config config; - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - config.lpfNyquistFactor = 1; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t x0Offset; - size_t x1Offset; - size_t lpfOffset; -} ma_linear_resampler_heap_layout; - - -static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut) -{ - /* - So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will - be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate. - */ - ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */ - ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut; - - pResampler->inTimeFrac = - (oldRateTimeWhole * newSampleRateOut) + - ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut); - - /* Make sure the fractional part is less than the output sample rate. */ - pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut; - pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut; -} - -static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) -{ - ma_result result; - ma_uint32 gcf; - ma_uint32 lpfSampleRate; - double lpfCutoffFrequency; - ma_lpf_config lpfConfig; - ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */ - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (sampleRateIn == 0 || sampleRateOut == 0) { - return MA_INVALID_ARGS; - } - - oldSampleRateOut = pResampler->config.sampleRateOut; - - pResampler->config.sampleRateIn = sampleRateIn; - pResampler->config.sampleRateOut = sampleRateOut; - - /* Simplify the sample rate. */ - gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut); - pResampler->config.sampleRateIn /= gcf; - pResampler->config.sampleRateOut /= gcf; - - /* Always initialize the low-pass filter, even when the order is 0. */ - if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut)); - lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor); - - lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); - - /* - If the resampler is alreay initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames - getting cleared. Instead we re-initialize the filter which will maintain any cached frames. - */ - if (isResamplerAlreadyInitialized) { - result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf); - } else { - result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf); - } - - if (result != MA_SUCCESS) { - return result; - } - - - pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut; - pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut; - - /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */ - ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut); - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* x0 */ - pHeapLayout->x0Offset = pHeapLayout->sizeInBytes; - if (pConfig->format == ma_format_f32) { - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - } else { - pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; - } - - /* x1 */ - pHeapLayout->x1Offset = pHeapLayout->sizeInBytes; - if (pConfig->format == ma_format_f32) { - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - } else { - pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; - } - - /* LPF */ - pHeapLayout->lpfOffset = pHeapLayout->sizeInBytes; - { - ma_result result; - size_t lpfHeapSizeInBytes; - ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ - - result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += lpfHeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_linear_resampler_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler) -{ - ma_result result; - ma_linear_resampler_heap_layout heapLayout; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResampler); - - result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->config = *pConfig; - - pResampler->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - if (pConfig->format == ma_format_f32) { - pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset); - pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset); - } else { - pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset); - pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); - } - - /* Setting the rate will set up the filter and time advances for us. */ - result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ - pResampler->inTimeFrac = 0; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pResampler->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pResampler == NULL) { - return; - } - - ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks); - - if (pResampler->_ownsHeap) { - ma_free(pResampler->_pHeap, pAllocationCallbacks); - } -} - -static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift) -{ - ma_int32 b; - ma_int32 c; - ma_int32 r; - - MA_ASSERT(a <= (1<> shift); -} - -static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut) -{ - ma_uint32 c; - ma_uint32 a; - const ma_uint32 channels = pResampler->config.channels; - const ma_uint32 shift = 12; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameOut != NULL); - - a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift); - pFrameOut[c] = s; - } -} - - -static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut) -{ - ma_uint32 c; - float a; - const ma_uint32 channels = pResampler->config.channels; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameOut != NULL); - - a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a); - pFrameOut[c] = s; - } -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const ma_int16* pFramesInS16; - /* */ ma_int16* pFramesOutS16; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInS16 = (const ma_int16*)pFramesIn; - pFramesOutS16 = ( ma_int16*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInS16 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; - } - pFramesInS16 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = 0; - } - } - - /* Filter. */ - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ - if (pFramesOutS16 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - - pFramesOutS16 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const ma_int16* pFramesInS16; - /* */ ma_int16* pFramesOutS16; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInS16 = (const ma_int16*)pFramesIn; - pFramesOutS16 = ( ma_int16*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInS16 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; - } - pFramesInS16 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = 0; - } - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and we can generate the next output frame. */ - if (pFramesOutS16 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - - /* Filter. */ - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); - - pFramesOutS16 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { - return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - - -static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const float* pFramesInF32; - /* */ float* pFramesOutF32; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInF32 = (const float*)pFramesIn; - pFramesOutF32 = ( float*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInF32 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; - } - pFramesInF32 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = 0; - } - } - - /* Filter. */ - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ - if (pFramesOutF32 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - - pFramesOutF32 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const float* pFramesInF32; - /* */ float* pFramesOutF32; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInF32 = (const float*)pFramesIn; - pFramesOutF32 = ( float*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInF32 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; - } - pFramesInF32 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = 0; - } - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and we can generate the next output frame. */ - if (pFramesOutF32 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - - /* Filter. */ - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); - - pFramesOutF32 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { - return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - - -MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* */ if (pResampler->config.format == ma_format_s16) { - return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else if (pResampler->config.format == ma_format_f32) { - return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Should never get here. Getting here means the format is not supported and you didn't check the return value of ma_linear_resampler_init(). */ - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; - } -} - - -MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); -} - -MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut) -{ - ma_uint32 n; - ma_uint32 d; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (ratioInOut <= 0) { - return MA_INVALID_ARGS; - } - - d = 1000; - n = (ma_uint32)(ratioInOut * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_linear_resampler_set_rate(pResampler, n, d); -} - -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - return 1 + ma_lpf_get_latency(&pResampler->lpf); -} - -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; -} - -MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - ma_uint64 inputFrameCount; - - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (outputFrameCount == 0) { - return MA_SUCCESS; - } - - /* Any whole input frames are consumed before the first output frame is generated. */ - inputFrameCount = pResampler->inTimeInt; - outputFrameCount -= 1; - - /* The rest of the output frames can be calculated in constant time. */ - inputFrameCount += outputFrameCount * pResampler->inAdvanceInt; - inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut; - - *pInputFrameCount = inputFrameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - ma_uint64 outputFrameCount; - ma_uint64 preliminaryInputFrameCountFromFrac; - ma_uint64 preliminaryInputFrameCount; - - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* - The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to - determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't - be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation - of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames. - */ - outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn; - - /* - We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is - used in the logic below to determine whether or not we need to add an extra output frame. - */ - preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut; - preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; - - /* - If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than - the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data - to actually process. Otherwise we need to add the extra output frame. - */ - if (preliminaryInputFrameCount <= inputFrameCount) { - outputFrameCount += 1; - } - - *pOutputFrameCount = outputFrameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) -{ - ma_uint32 iChannel; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* Timers need to be cleared back to zero. */ - pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ - pResampler->inTimeFrac = 0; - - /* Cached samples need to be cleared. */ - if (pResampler->config.format == ma_format_f32) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = 0; - pResampler->x1.f32[iChannel] = 0; - } - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = 0; - pResampler->x1.s16[iChannel] = 0; - } - } - - /* The low pass filter needs to have it's cache reset. */ - ma_lpf_clear_cache(&pResampler->lpf); - - return MA_SUCCESS; -} - - - -/* Linear resampler backend vtable. */ -static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig) -{ - ma_linear_resampler_config linearConfig; - - linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); - linearConfig.lpfOrder = pConfig->linear.lpfOrder; - - return linearConfig; -} - -static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_linear_resampler_config linearConfig; - - (void)pUserData; - - linearConfig = ma_resampling_backend_get_config__linear(pConfig); - - return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes); -} - -static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend) -{ - ma_resampler* pResampler = (ma_resampler*)pUserData; - ma_result result; - ma_linear_resampler_config linearConfig; - - (void)pUserData; - - linearConfig = ma_resampling_backend_get_config__linear(pConfig); - - result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear); - if (result != MA_SUCCESS) { - return result; - } - - *ppBackend = &pResampler->state.linear; - - return MA_SUCCESS; -} - -static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - (void)pUserData; - - ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks); -} - -static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - (void)pUserData; - - return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - (void)pUserData; - - return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut); -} - -static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend); -} - -static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); -} - -static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - (void)pUserData; - - return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount); -} - -static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - (void)pUserData; - - return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount); -} - -static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_reset((ma_linear_resampler*)pBackend); -} - -static ma_resampling_backend_vtable g_ma_linear_resampler_vtable = -{ - ma_resampling_backend_get_heap_size__linear, - ma_resampling_backend_init__linear, - ma_resampling_backend_uninit__linear, - ma_resampling_backend_process__linear, - ma_resampling_backend_set_rate__linear, - ma_resampling_backend_get_input_latency__linear, - ma_resampling_backend_get_output_latency__linear, - ma_resampling_backend_get_required_input_frame_count__linear, - ma_resampling_backend_get_expected_output_frame_count__linear, - ma_resampling_backend_reset__linear -}; - - - -MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) -{ - ma_resampler_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - config.algorithm = algorithm; - - /* Linear. */ - config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - - return config; -} - -static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData) -{ - MA_ASSERT(pConfig != NULL); - MA_ASSERT(ppVTable != NULL); - MA_ASSERT(ppUserData != NULL); - - /* Safety. */ - *ppVTable = NULL; - *ppUserData = NULL; - - switch (pConfig->algorithm) - { - case ma_resample_algorithm_linear: - { - *ppVTable = &g_ma_linear_resampler_vtable; - *ppUserData = pResampler; - } break; - - case ma_resample_algorithm_custom: - { - *ppVTable = pConfig->pBackendVTable; - *ppUserData = pConfig->pBackendUserData; - } break; - - default: return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_resampling_backend_vtable* pVTable; - void* pVTableUserData; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData); - if (result != MA_SUCCESS) { - return result; - } - - if (pVTable == NULL || pVTable->onGetHeapSize == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler) -{ - ma_result result; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResampler); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pResampler->_pHeap = pHeap; - pResampler->format = pConfig->format; - pResampler->channels = pConfig->channels; - pResampler->sampleRateIn = pConfig->sampleRateIn; - pResampler->sampleRateOut = pConfig->sampleRateOut; - - result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData); - if (result != MA_SUCCESS) { - return result; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) { - return MA_NOT_IMPLEMENTED; /* onInit not implemented. */ - } - - result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pResampler == NULL) { - return; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) { - return; - } - - pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks); - - if (pResampler->_ownsHeap) { - ma_free(pResampler->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pFrameCountOut == NULL && pFrameCountIn == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_result result; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (sampleRateIn == 0 || sampleRateOut == 0) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->sampleRateIn = sampleRateIn; - pResampler->sampleRateOut = sampleRateOut; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio) -{ - ma_uint32 n; - ma_uint32 d; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (ratio <= 0) { - return MA_INVALID_ARGS; - } - - d = 1000; - n = (ma_uint32)(ratio * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_resampler_set_rate(pResampler, n, d); -} - -MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) { - return 0; - } - - return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend); -} - -MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) { - return 0; - } - - return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); -} - -MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount); -} - -MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount); -} - -MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend); -} - -/************************************************************************************************************************************************************** - -Channel Conversion - -**************************************************************************************************************************************************************/ -#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT -#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12 -#endif - -#define MA_PLANE_LEFT 0 -#define MA_PLANE_RIGHT 1 -#define MA_PLANE_FRONT 2 -#define MA_PLANE_BACK 3 -#define MA_PLANE_BOTTOM 4 -#define MA_PLANE_TOP 5 - -static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = { - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */ - { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */ - { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */ - { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */ - { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */ - { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */ - { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */ - { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ - { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */ - { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */ - { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */ - { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */ - { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */ - { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */ - { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */ - { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */ - { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */ -}; - -static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB) -{ - /* - Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to - the following output configuration: - - - front/left - - side/left - - back/left - - The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount - of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. - - Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left - speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted - from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would - receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between - the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works - across 3 spatial dimensions. - - The first thing to do is figure out how each speaker's volume is spread over each of plane: - - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane - - side/left: 1 plane (left only) = 1/1 = entire volume from left plane - - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane - - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane - - The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other - channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be - taken by the other to produce the final contribution. - */ - - /* Contribution = Sum(Volume to Give * Volume to Take) */ - float contribution = - g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] + - g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] + - g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] + - g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] + - g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] + - g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5]; - - return contribution; -} - -MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode) -{ - ma_channel_converter_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = pChannelMapIn; - config.pChannelMapOut = pChannelMapOut; - config.mixingMode = mixingMode; - - return config; -} - -static ma_int32 ma_channel_converter_float_to_fixed(float x) -{ - return (ma_int32)(x * (1<= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) { - return MA_FALSE; - } - - for (i = 0; i < 6; ++i) { /* Each side of a cube. */ - if (g_maChannelPlaneRatios[channelPosition][i] != 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut) -{ - if (channelsOut == channelsIn) { - return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut); - } else { - return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */ - } -} - -static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode) -{ - if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) { - return ma_channel_conversion_path_passthrough; - } - - if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) { - return ma_channel_conversion_path_mono_out; - } - - if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) { - return ma_channel_conversion_path_mono_in; - } - - if (mode == ma_channel_mix_mode_custom_weights) { - return ma_channel_conversion_path_weights; - } - - /* - We can use a simple shuffle if both channel maps have the same channel count and all channel - positions are present in both. - */ - if (channelsIn == channelsOut) { - ma_uint32 iChannelIn; - ma_bool32 areAllChannelPositionsPresent = MA_TRUE; - for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { - ma_bool32 isInputChannelPositionInOutput = MA_FALSE; - if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) { - isInputChannelPositionInOutput = MA_TRUE; - break; - } - - if (!isInputChannelPositionInOutput) { - areAllChannelPositionsPresent = MA_FALSE; - break; - } - } - - if (areAllChannelPositionsPresent) { - return ma_channel_conversion_path_shuffle; - } - } - - /* Getting here means we'll need to use weights. */ - return ma_channel_conversion_path_weights; -} - - -static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable) -{ - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) { - return MA_INVALID_ARGS; - } - - /* - When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the - input channel has more than one occurance of a channel position, the second one will be ignored. - */ - for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { - ma_channel channelOut; - - /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */ - pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL; - - channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut); - for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) { - ma_channel channelIn; - - channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn); - if (channelOut == channelIn) { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - break; - } - - /* - Getting here means the channels don't exactly match, but we are going to support some - relaxed matching for practicality. If, for example, there are two stereo channel maps, - but one uses front left/right and the other uses side left/right, it makes logical - sense to just map these. The way we'll do it is we'll check if there is a logical - corresponding mapping, and if so, apply it, but we will *not* break from the loop, - thereby giving the loop a chance to find an exact match later which will take priority. - */ - switch (channelOut) - { - /* Left channels. */ - case MA_CHANNEL_FRONT_LEFT: - case MA_CHANNEL_SIDE_LEFT: - { - switch (channelIn) { - case MA_CHANNEL_FRONT_LEFT: - case MA_CHANNEL_SIDE_LEFT: - { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - } break; - } - } break; - - /* Right channels. */ - case MA_CHANNEL_FRONT_RIGHT: - case MA_CHANNEL_SIDE_RIGHT: - { - switch (channelIn) { - case MA_CHANNEL_FRONT_RIGHT: - case MA_CHANNEL_SIDE_RIGHT: - { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - } break; - } - } break; - - default: break; - } - } - } - - return MA_SUCCESS; -} - - -static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0]; - pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1]; - pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2]; - } else { - pFramesOut[iChannelOut*3 + 0] = 0; - } pFramesOut[iChannelOut*3 + 1] = 0; - } pFramesOut[iChannelOut*3 + 2] = 0; - - pFramesOut += channelsOut*3; - pFramesIn += channelsIn*3; - } -} - -static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format) -{ - if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) { - return MA_INVALID_ARGS; - } - - switch (format) - { - case ma_format_u8: - { - ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s16: - { - ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s24: - { - ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s32: - { - ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_f32: - { - ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - default: return MA_INVALID_ARGS; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannelIn; - ma_uint32 accumulationCount; - - if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) { - return MA_INVALID_ARGS; - } - - /* In this case the output stream needs to be the average of all channels, ignoring NONE. */ - - /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */ - accumulationCount = 0; - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) { - accumulationCount += 1; - } - } - - if (accumulationCount > 0) { /* <-- Prevent a division by zero. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float accumulation = 0; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - if (channelIn != MA_CHANNEL_NONE) { - accumulation += pFramesIn[iChannelIn]; - } - } - - pFramesOut[0] = accumulation / accumulationCount; - pFramesOut += 1; - pFramesIn += channelsIn; - } - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1); - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_map_apply_mono_in_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */ - switch (monoExpansionMode) - { - case ma_mono_expansion_mode_average: - { - float weight; - ma_uint32 validChannelCount = 0; - - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - validChannelCount += 1; - } - } - - weight = 1.0f / validChannelCount; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iChannelOut] = pFramesIn[0] * weight; - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - } break; - - case ma_mono_expansion_mode_stereo_only: - { - if (channelsOut >= 2) { - ma_uint32 iChannelLeft = (ma_uint32)-1; - ma_uint32 iChannelRight = (ma_uint32)-1; - - /* - We first need to find our stereo channels. We prefer front-left and front-right, but - if they're not available, we'll also try side-left and side-right. If neither are - available we'll fall through to the default case below. - */ - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut == MA_CHANNEL_SIDE_LEFT) { - iChannelLeft = iChannelOut; - } - if (channelOut == MA_CHANNEL_SIDE_RIGHT) { - iChannelRight = iChannelOut; - } - } - - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut == MA_CHANNEL_FRONT_LEFT) { - iChannelLeft = iChannelOut; - } - if (channelOut == MA_CHANNEL_FRONT_RIGHT) { - iChannelRight = iChannelOut; - } - } - - - if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) { - /* We found our stereo channels so we can duplicate the signal across those channels. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) { - pFramesOut[iChannelOut] = pFramesIn[0]; - } else { - pFramesOut[iChannelOut] = 0.0f; - } - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - - break; /* Get out of the switch. */ - } else { - /* Fallthrough. Does not have left and right channels. */ - goto default_handler; - } - } else { - /* Fallthrough. Does not have stereo channels. */ - goto default_handler; - } - }; /* Fallthrough. See comments above. */ - - case ma_mono_expansion_mode_duplicate: - default: - { - default_handler: - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iChannelOut] = pFramesIn[0]; - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - } - } break; - } - - return MA_SUCCESS; -} - -static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode) -{ - ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode); - - /* Optimized Path: Passthrough */ - if (conversionPath == ma_channel_conversion_path_passthrough) { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut); - return; - } - - /* Special Path: Mono Output. */ - if (conversionPath == ma_channel_conversion_path_mono_out) { - ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount); - return; - } - - /* Special Path: Mono Input. */ - if (conversionPath == ma_channel_conversion_path_mono_in) { - ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode); - return; - } - - /* Getting here means we aren't running on an optimized conversion path. */ - if (channelsOut <= MA_MAX_CHANNELS) { - ma_result result; - - if (mode == ma_channel_mix_mode_simple) { - ma_channel shuffleTable[MA_MAX_CHANNELS]; - - result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable); - if (result != MA_SUCCESS) { - return; - } - - result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32); - if (result != MA_SUCCESS) { - return; - } - } else { - ma_uint32 iFrame; - ma_uint32 iChannelOut; - ma_uint32 iChannelIn; - float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */ - - /* - If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to - fall back to a slower path because otherwise we'll run out of stack space. - */ - if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) { - /* Pre-compute weights. */ - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); - } - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - float accumulation = 0; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation += pFramesIn[iChannelIn] * weights[iChannelOut][iChannelIn]; - } - - pFramesOut[iChannelOut] = accumulation; - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } - } else { - /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - float accumulation = 0; - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - accumulation += pFramesIn[iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); - } - - pFramesOut[iChannelOut] = accumulation; - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } - } - } - } else { - /* Fall back to silence. If you hit this, what are you doing with so many channels?! */ - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut); - } -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapInOffset; - size_t channelMapOutOffset; - size_t shuffleTableOffset; - size_t weightsOffset; -} ma_channel_converter_heap_layout; - -static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig) -{ - return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode); -} - -static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout) -{ - ma_channel_conversion_path conversionPath; - - MA_ASSERT(pHeapLayout != NULL); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */ - pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; - if (pConfig->pChannelMapIn != NULL) { - pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn; - } - - /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */ - pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; - if (pConfig->pChannelMapOut != NULL) { - pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut; - } - - /* Alignment for the next section. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */ - conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); - - /* Shuffle table */ - pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes; - if (conversionPath == ma_channel_conversion_path_shuffle) { - pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut; - } - - /* Weights */ - pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes; - if (conversionPath == ma_channel_conversion_path_weights) { - pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn; - pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_channel_converter_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter) -{ - ma_result result; - ma_channel_converter_heap_layout heapLayout; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pConverter); - - result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->_pHeap = pHeap; - MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes); - - pConverter->format = pConfig->format; - pConverter->channelsIn = pConfig->channelsIn; - pConverter->channelsOut = pConfig->channelsOut; - pConverter->mixingMode = pConfig->mixingMode; - - if (pConfig->pChannelMapIn != NULL) { - pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); - ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn); - } else { - pConverter->pChannelMapIn = NULL; /* Use default channel map. */ - } - - if (pConfig->pChannelMapOut != NULL) { - pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); - ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); - } else { - pConverter->pChannelMapOut = NULL; /* Use default channel map. */ - } - - pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); - - if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) { - pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset); - ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable); - } - - if (pConverter->conversionPath == ma_channel_conversion_path_weights) { - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset); - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn))); - } - } else { - pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset); - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn))); - } - } - - /* Silence our weights by default. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = 0; - } - } - } - - /* - We now need to fill out our weights table. This is determined by the mixing mode. - */ - switch (pConverter->mixingMode) - { - case ma_channel_mix_mode_custom_weights: - { - if (pConfig->ppWeights == NULL) { - return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */ - } - - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { - float weight = pConfig->ppWeights[iChannelIn][iChannelOut]; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } break; - - case ma_channel_mix_mode_simple: - { - /* In simple mode, excess channels need to be silenced or dropped. */ - ma_uint32 iChannel; - for (iChannel = 0; iChannel < ma_min(pConverter->channelsIn, pConverter->channelsOut); iChannel += 1) { - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannel][iChannel] == 0) { - pConverter->weights.f32[iChannel][iChannel] = 1; - } - } else { - if (pConverter->weights.s16[iChannel][iChannel] == 0) { - pConverter->weights.s16[iChannel][iChannel] = ma_channel_converter_float_to_fixed(1); - } - } - } - } break; - - case ma_channel_mix_mode_rectangular: - default: - { - /* Unmapped input channels. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->pChannelMapIn[iChannelIn]; - - if (ma_is_spatial_channel_position(channelPosIn)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->pChannelMapOut[iChannelOut]; - - if (ma_is_spatial_channel_position(channelPosOut)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - } - } - } - - /* Unmapped output channels. */ - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->pChannelMapOut[iChannelOut]; - - if (ma_is_spatial_channel_position(channelPosOut)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->pChannelMapIn[iChannelIn]; - - if (ma_is_spatial_channel_position(channelPosIn)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - } - } - } - } break; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pConverter->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pConverter == NULL) { - return; - } - - if (pConverter->_ownsHeap) { - ma_free(pConverter->_pHeap, pAllocationCallbacks); - } -} - -static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut); - - return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format); -} - -static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == 1); - - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame]; - } - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - if (pConverter->channelsOut == 2) { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame]; - pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame]; - } - } else { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame]; - } - } - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel; - ma_uint64 iSampleIn = iFrame; - pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0]; - pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1]; - pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2]; - } - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame]; - } - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pConverter->channelsOut == 2) { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame]; - pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame]; - } - } else { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame]; - } - } - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsOut == 1); - - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int32 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]); - } - - pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut); - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int32 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn); - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]); - } - - ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]); - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn); - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - float t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutF32[iFrame] = t / pConverter->channelsIn; - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 iFrame; - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - - /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */ - - /* Clear. */ - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - - /* Accumulate. */ - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]); - ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]); - ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127); - pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s); - } - } - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut]; - s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; - - pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767); - } - } - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); - ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]); - ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607); - ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); - } - } - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut]; - s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; - - pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s); - } - } - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut]; - } - } - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesIn == NULL) { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - return MA_SUCCESS; - } - - switch (pConverter->conversionPath) - { - case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_weights: - default: - { - return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); - } - } -} - -MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn); - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut); - - return MA_SUCCESS; -} - - -/************************************************************************************************************************************************************** - -Data Conversion - -**************************************************************************************************************************************************************/ -MA_API ma_data_converter_config ma_data_converter_config_init_default() -{ - ma_data_converter_config config; - MA_ZERO_OBJECT(&config); - - config.ditherMode = ma_dither_mode_none; - config.resampling.algorithm = ma_resample_algorithm_linear; - config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ - - /* Linear resampling defaults. */ - config.resampling.linear.lpfOrder = 1; - - return config; -} - -MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_data_converter_config config = ma_data_converter_config_init_default(); - config.formatIn = formatIn; - config.formatOut = formatOut; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelConverterOffset; - size_t resamplerOffset; -} ma_data_converter_heap_layout; - -static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut; -} - -static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - /* - We want to avoid as much data conversion as possible. The channel converter and linear - resampler both support s16 and f32 natively. We need to decide on the format to use for this - stage. We call this the mid format because it's used in the middle stage of the conversion - pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it - will do the same thing for the input format. If it's neither we just use f32. If we are using a - custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced - to use that if resampling is required. - */ - if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) { - return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */ - } else { - /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) { - return pConfig->formatOut; - } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) { - return pConfig->formatIn; - } else { - return ma_format_f32; - } - } -} - -static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) -{ - ma_channel_converter_config channelConverterConfig; - - MA_ASSERT(pConfig != NULL); - - channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); - channelConverterConfig.ppWeights = pConfig->ppChannelWeights; - - return channelConverterConfig; -} - -static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) -{ - ma_resampler_config resamplerConfig; - ma_uint32 resamplerChannels; - - MA_ASSERT(pConfig != NULL); - - /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ - if (pConfig->channelsIn < pConfig->channelsOut) { - resamplerChannels = pConfig->channelsIn; - } else { - resamplerChannels = pConfig->channelsOut; - } - - resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm); - resamplerConfig.linear = pConfig->resampling.linear; - resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable; - resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData; - - return resamplerConfig; -} - -static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel converter. */ - pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes; - { - size_t heapSizeInBytes; - ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - - result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += heapSizeInBytes; - } - - /* Resampler. */ - pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; - if (ma_data_converter_config_is_resampler_required(pConfig)) { - size_t heapSizeInBytes; - ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - - result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += heapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_data_converter_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter) -{ - ma_result result; - ma_data_converter_heap_layout heapLayout; - ma_format midFormat; - ma_bool32 isResamplingRequired; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pConverter); - - result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pConverter->formatIn = pConfig->formatIn; - pConverter->formatOut = pConfig->formatOut; - pConverter->channelsIn = pConfig->channelsIn; - pConverter->channelsOut = pConfig->channelsOut; - pConverter->sampleRateIn = pConfig->sampleRateIn; - pConverter->sampleRateOut = pConfig->sampleRateOut; - pConverter->ditherMode = pConfig->ditherMode; - - /* - Determine if resampling is required. We need to do this so we can determine an appropriate - mid format to use. If resampling is required, the mid format must be ma_format_f32 since - that is the only one that is guaranteed to supported by custom resampling backends. - */ - isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig); - midFormat = ma_data_converter_config_get_mid_format(pConfig); - - - /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */ - { - ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - - result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter); - if (result != MA_SUCCESS) { - return result; - } - - /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */ - if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) { - pConverter->hasChannelConverter = MA_TRUE; - } - } - - - /* Resampler. */ - if (isResamplingRequired) { - ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - - result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->hasResampler = MA_TRUE; - } - - - /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */ - if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { - /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */ - if (pConverter->formatIn == pConverter->formatOut) { - /* The formats are the same so we can just pass through. */ - pConverter->hasPreFormatConversion = MA_FALSE; - pConverter->hasPostFormatConversion = MA_FALSE; - } else { - /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */ - pConverter->hasPreFormatConversion = MA_FALSE; - pConverter->hasPostFormatConversion = MA_TRUE; - } - } else { - /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */ - if (pConverter->formatIn != midFormat) { - pConverter->hasPreFormatConversion = MA_TRUE; - } - if (pConverter->formatOut != midFormat) { - pConverter->hasPostFormatConversion = MA_TRUE; - } - } - - /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */ - if (pConverter->hasPreFormatConversion == MA_FALSE && - pConverter->hasPostFormatConversion == MA_FALSE && - pConverter->hasChannelConverter == MA_FALSE && - pConverter->hasResampler == MA_FALSE) { - pConverter->isPassthrough = MA_TRUE; - } - - - /* We now need to determine our execution path. */ - if (pConverter->isPassthrough) { - pConverter->executionPath = ma_data_converter_execution_path_passthrough; - } else { - if (pConverter->channelsIn < pConverter->channelsOut) { - /* Do resampling first, if necessary. */ - MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); - - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_resample_first; - } else { - pConverter->executionPath = ma_data_converter_execution_path_channels_only; - } - } else { - /* Do channel conversion first, if necessary. */ - if (pConverter->hasChannelConverter) { - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_channels_first; - } else { - pConverter->executionPath = ma_data_converter_execution_path_channels_only; - } - } else { - /* Channel routing not required. */ - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_resample_only; - } else { - pConverter->executionPath = ma_data_converter_execution_path_format_only; - } - } - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pConverter->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pConverter == NULL) { - return; - } - - if (pConverter->hasResampler) { - ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks); - } - - ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks); - - if (pConverter->_ownsHeap) { - ma_free(pConverter->_pHeap, pAllocationCallbacks); - } -} - -static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pFramesOut != NULL) { - if (pFramesIn != NULL) { - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pFramesOut != NULL) { - if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode); - } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - - -static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result = MA_SUCCESS; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - const void* pFramesInThisIteration; - /* */ void* pFramesOutThisIteration; - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - - if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } else { - pFramesInThisIteration = NULL; - } - - if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - pFramesOutThisIteration = NULL; - } - - /* Do a pre format conversion if necessary. */ - if (pConverter->hasPreFormatConversion) { - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountInThisIteration > tempBufferOutCap) { - frameCountInThisIteration = tempBufferOutCap; - } - } - - if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - } else { - MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); - } - - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - - if (pConverter->hasPostFormatConversion) { - /* Both input and output conversion required. Output to the temp buffer. */ - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); - } else { - /* Only pre-format required. Output straight to the output buffer. */ - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration); - } - - if (result != MA_SUCCESS) { - break; - } - } else { - /* No pre-format required. Just read straight from the input buffer. */ - MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); - - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - break; - } - } - - /* If we are doing a post format conversion we need to do that now. */ - if (pConverter->hasPostFormatConversion) { - if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode); - } - } - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return result; -} - -static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pConverter != NULL); - - if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { - /* Neither pre- nor post-format required. This is simple case where only resampling is required. */ - return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Format conversion required. */ - return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - -static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { - /* No format conversion required. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } else { - /* Format conversion required. */ - ma_uint64 framesProcessed = 0; - - while (framesProcessed < frameCount) { - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - const void* pFramesInThisIteration; - /* */ void* pFramesOutThisIteration; - ma_uint64 frameCountThisIteration; - - if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } else { - pFramesInThisIteration = NULL; - } - - if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - pFramesOutThisIteration = NULL; - } - - /* Do a pre format conversion if necessary. */ - if (pConverter->hasPreFormatConversion) { - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); - - frameCountThisIteration = (frameCount - framesProcessed); - if (frameCountThisIteration > tempBufferInCap) { - frameCountThisIteration = tempBufferInCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountThisIteration > tempBufferOutCap) { - frameCountThisIteration = tempBufferOutCap; - } - } - - if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode); - } else { - MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); - } - - if (pConverter->hasPostFormatConversion) { - /* Both input and output conversion required. Output to the temp buffer. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration); - } else { - /* Only pre-format required. Output straight to the output buffer. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration); - } - - if (result != MA_SUCCESS) { - break; - } - } else { - /* No pre-format required. Just read straight from the input buffer. */ - MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); - - frameCountThisIteration = (frameCount - framesProcessed); - if (frameCountThisIteration > tempBufferOutCap) { - frameCountThisIteration = tempBufferOutCap; - } - - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration); - if (result != MA_SUCCESS) { - break; - } - } - - /* If we are doing a post format conversion we need to do that now. */ - if (pConverter->hasPostFormatConversion) { - if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); - } - } - - framesProcessed += frameCountThisIteration; - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ - ma_uint64 tempBufferInCap; - ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ - ma_uint64 tempBufferMidCap; - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ - ma_uint64 tempBufferOutCap; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn); - MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - - while (framesProcessedOut < frameCountOut) { - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - const void* pRunningFramesIn = NULL; - void* pRunningFramesOut = NULL; - const void* pResampleBufferIn; - void* pChannelsBufferOut; - - if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } - if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - - /* Run input data through the resampler and output it to the temporary buffer. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - - if (pConverter->hasPreFormatConversion) { - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - } - - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - /* We can't read more frames than can fit in the output buffer. */ - if (pConverter->hasPostFormatConversion) { - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - } - - /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ - - /* - We need to try to predict how many input frames will be required for the resampler. If the - resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further - off we are from this, the more wasted format conversions we'll end up doing. - */ - #if 1 - { - ma_uint64 requiredInputFrameCount; - - result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); - if (result != MA_SUCCESS) { - /* Fall back to a best guess. */ - requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; - } - - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } - #endif - - if (pConverter->hasPreFormatConversion) { - if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - pResampleBufferIn = pTempBufferIn; - } else { - pResampleBufferIn = NULL; - } - } else { - pResampleBufferIn = pRunningFramesIn; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* - The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do - this part if we have an output buffer. - */ - if (pFramesOut != NULL) { - if (pConverter->hasPostFormatConversion) { - pChannelsBufferOut = pTempBufferOut; - } else { - pChannelsBufferOut = pRunningFramesOut; - } - - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - /* Finally we do post format conversion. */ - if (pConverter->hasPostFormatConversion) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); - } - } - - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ - ma_uint64 tempBufferInCap; - ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ - ma_uint64 tempBufferMidCap; - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ - ma_uint64 tempBufferOutCap; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut); - MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - - while (framesProcessedOut < frameCountOut) { - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - const void* pRunningFramesIn = NULL; - void* pRunningFramesOut = NULL; - const void* pChannelsBufferIn; - void* pResampleBufferOut; - - if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } - if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - - /* - Before doing any processing we need to determine how many frames we should try processing - this iteration, for both input and output. The resampler requires us to perform format and - channel conversion before passing any data into it. If we get our input count wrong, we'll - end up peforming redundant pre-processing. This isn't the end of the world, but it does - result in some inefficiencies proportionate to how far our estimates are off. - - If the resampler has a means to calculate exactly how much we'll need, we'll use that. - Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output - frame count first. - */ - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - } - - /* Now that we have the output frame count we can determine the input frame count. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - if (pConverter->hasPreFormatConversion) { - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - } - - if (frameCountInThisIteration > tempBufferMidCap) { - frameCountInThisIteration = tempBufferMidCap; - } - - #if 1 - { - ma_uint64 requiredInputFrameCount; - - result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); - if (result != MA_SUCCESS) { - /* Fall back to a best guess. */ - requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; - } - - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } - #endif - - - /* Pre format conversion. */ - if (pConverter->hasPreFormatConversion) { - if (pRunningFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - pChannelsBufferIn = pTempBufferIn; - } else { - pChannelsBufferIn = NULL; - } - } else { - pChannelsBufferIn = pRunningFramesIn; - } - - - /* Channel conversion. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* Resampling. */ - if (pConverter->hasPostFormatConversion) { - pResampleBufferOut = pTempBufferOut; - } else { - pResampleBufferOut = pRunningFramesOut; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* Post format conversion. */ - if (pConverter->hasPostFormatConversion) { - if (pRunningFramesOut != NULL) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode); - } - } - - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - switch (pConverter->executionPath) - { - case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - default: return MA_INVALID_OPERATION; /* Should never hit this. */ - } -} - -MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler == MA_FALSE) { - return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ - } - - return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut); -} - -MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler == MA_FALSE) { - return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ - } - - return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut); -} - -MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_input_latency(&pConverter->resampler); - } - - return 0; /* No latency without a resampler. */ -} - -MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_output_latency(&pConverter->resampler); - } - - return 0; /* No latency without a resampler. */ -} - -MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount); - } else { - *pInputFrameCount = outputFrameCount; /* 1:1 */ - return MA_SUCCESS; - } -} - -MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount); - } else { - *pOutputFrameCount = inputFrameCount; /* 1:1 */ - return MA_SUCCESS; - } -} - -MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasChannelConverter) { - ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasChannelConverter) { - ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - /* There's nothing to do if we're not resampling. */ - if (pConverter->hasResampler == MA_FALSE) { - return MA_SUCCESS; - } - - return ma_resampler_reset(&pConverter->resampler); -} - - - -/************************************************************************************************************************************************************** - -Channel Maps - -**************************************************************************************************************************************************************/ -static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); - -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (pChannelMap == NULL) { - return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex); - } else { - if (channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - return pChannelMap[channelIndex]; - } -} - -MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels) -{ - if (pChannelMap == NULL) { - return; - } - - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); -} - - -static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (channelCount == 0 || channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: /* No defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP - /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */ - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_CENTER; - #else - /* Quad. */ - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - #endif - } - } break; - - case 5: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_SIDE_LEFT; - case 5: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 7: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_CENTER; - case 5: return MA_CHANNEL_SIDE_LEFT; - case 6: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - case 6: return MA_CHANNEL_BACK_CENTER; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_CENTER; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_SIDE_LEFT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_FRONT_RIGHT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - } - } break; - } - - if (channelCount > 6) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_CENTER; - case 5: return MA_CHANNEL_SIDE_LEFT; - case 6: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - case 6: return MA_CHANNEL_LFE; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_LEFT; - case 6: return MA_CHANNEL_BACK_RIGHT; - case 7: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - case 6: return MA_CHANNEL_LFE; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_LEFT; - case 6: return MA_CHANNEL_BACK_RIGHT; - case 7: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: /* No defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 6: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 6) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - - -static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (channelCount == 0 || channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - switch (standardChannelMap) - { - case ma_standard_channel_map_alsa: - { - return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_rfc3551: - { - return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_flac: - { - return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_vorbis: - { - return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_sound4: - { - return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_sndio: - { - return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_microsoft: /* Also default. */ - /*case ma_standard_channel_map_default;*/ - default: - { - return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex); - } break; - } -} - -MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels) -{ - ma_uint32 iChannel; - - if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) { - return; - } - - for (iChannel = 0; iChannel < channels; iChannel += 1) { - if (channelMapCap == 0) { - break; /* Ran out of room. */ - } - - pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel); - pChannelMap += 1; - channelMapCap -= 1; - } -} - -MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) -{ - if (pOut != NULL && pIn != NULL && channels > 0) { - MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels); - } -} - -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels) -{ - if (pOut == NULL || channels == 0) { - return; - } - - if (pIn != NULL) { - ma_channel_map_copy(pOut, pIn, channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels); - } -} - -MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels) -{ - /* A channel count of 0 is invalid. */ - if (channels == 0) { - return MA_FALSE; - } - - /* It does not make sense to have a mono channel when there is more than 1 channel. */ - if (channels > 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) { - return MA_FALSE; - } - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels) -{ - ma_uint32 iChannel; - - if (pChannelMapA == pChannelMapB) { - return MA_TRUE; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels) -{ - ma_uint32 iChannel; - - /* A null channel map is equivalent to the default channel map. */ - if (pChannelMap == NULL) { - return MA_FALSE; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (pChannelMap[iChannel] != MA_CHANNEL_NONE) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) -{ - ma_uint32 iChannel; - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - - -/************************************************************************************************************************************************************** - -Conversion Helpers - -**************************************************************************************************************************************************************/ -MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn) -{ - ma_data_converter_config config; - - config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); - config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - - return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config); -} - -MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig) -{ - ma_result result; - ma_data_converter converter; - - if (frameCountIn == 0 || pConfig == NULL) { - return 0; - } - - result = ma_data_converter_init(pConfig, NULL, &converter); - if (result != MA_SUCCESS) { - return 0; /* Failed to initialize the data converter. */ - } - - if (pOut == NULL) { - result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); - if (result != MA_SUCCESS) { - if (result == MA_NOT_IMPLEMENTED) { - /* No way to calculate the number of frames, so we'll need to brute force it and loop. */ - frameCountOut = 0; - - while (frameCountIn > 0) { - ma_uint64 framesProcessedIn = frameCountIn; - ma_uint64 framesProcessedOut = 0xFFFFFFFF; - - result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); - if (result != MA_SUCCESS) { - break; - } - - frameCountIn -= framesProcessedIn; - } - } - } - } else { - result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut); - if (result != MA_SUCCESS) { - frameCountOut = 0; - } - } - - ma_data_converter_uninit(&converter, NULL); - return frameCountOut; -} - - -/************************************************************************************************************************************************************** - -Ring Buffer - -**************************************************************************************************************************************************************/ -static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset) -{ - return encodedOffset & 0x7FFFFFFF; -} - -static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset) -{ - return encodedOffset & 0x80000000; -} - -static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedReadOffset))); -} - -static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedWriteOffset))); -} - -static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) -{ - return offsetLoopFlag | offsetInBytes; -} - -static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag) -{ - MA_ASSERT(pOffsetInBytes != NULL); - MA_ASSERT(pOffsetLoopFlag != NULL); - - *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset); - *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset); -} - - -MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) -{ - ma_result result; - const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1); - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - if (subbufferSizeInBytes == 0 || subbufferCount == 0) { - return MA_INVALID_ARGS; - } - - if (subbufferSizeInBytes > maxSubBufferSize) { - return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */ - } - - - MA_ZERO_OBJECT(pRB); - - result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes; - pRB->subbufferCount = (ma_uint32)subbufferCount; - - if (pOptionalPreallocatedBuffer != NULL) { - pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes; - pRB->pBuffer = pOptionalPreallocatedBuffer; - } else { - size_t bufferSizeInBytes; - - /* - Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this - we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. - */ - pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; - - bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; - pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); - if (pRB->pBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes); - pRB->ownsBuffer = MA_TRUE; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) -{ - return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); -} - -MA_API void ma_rb_uninit(ma_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - if (pRB->ownsBuffer) { - ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks); - } -} - -MA_API void ma_rb_reset(ma_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - c89atomic_exchange_32(&pRB->encodedReadOffset, 0); - c89atomic_exchange_32(&pRB->encodedWriteOffset, 0); -} - -MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) -{ - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - size_t bytesAvailable; - size_t bytesRequested; - - if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { - return MA_INVALID_ARGS; - } - - /* The returned buffer should never move ahead of the write pointer. */ - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - /* - The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we - can only read up to the write pointer. If not, we can only read up to the end of the buffer. - */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - bytesAvailable = writeOffsetInBytes - readOffsetInBytes; - } else { - bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes; - } - - bytesRequested = *pSizeInBytes; - if (bytesRequested > bytesAvailable) { - bytesRequested = bytesAvailable; - } - - *pSizeInBytes = bytesRequested; - (*ppBufferOut) = ma_rb__get_read_ptr(pRB); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 newReadOffsetInBytes; - ma_uint32 newReadOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes); - if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ - } - - /* Move the read pointer back to the start if necessary. */ - newReadOffsetLoopFlag = readOffsetLoopFlag; - if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) { - newReadOffsetInBytes = 0; - newReadOffsetLoopFlag ^= 0x80000000; - } - - c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); - - if (ma_rb_pointer_distance(pRB) == 0) { - return MA_AT_END; - } else { - return MA_SUCCESS; - } -} - -MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - size_t bytesAvailable; - size_t bytesRequested; - - if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { - return MA_INVALID_ARGS; - } - - /* The returned buffer should never overtake the read buffer. */ - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - /* - In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only - write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should - never overtake the read pointer. - */ - if (writeOffsetLoopFlag == readOffsetLoopFlag) { - bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes; - } else { - bytesAvailable = readOffsetInBytes - writeOffsetInBytes; - } - - bytesRequested = *pSizeInBytes; - if (bytesRequested > bytesAvailable) { - bytesRequested = bytesAvailable; - } - - *pSizeInBytes = bytesRequested; - *ppBufferOut = ma_rb__get_write_ptr(pRB); - - /* Clear the buffer if desired. */ - if (pRB->clearOnWriteAcquire) { - MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) -{ - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newWriteOffsetInBytes; - ma_uint32 newWriteOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes); - if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ - } - - /* Move the read pointer back to the start if necessary. */ - newWriteOffsetLoopFlag = writeOffsetLoopFlag; - if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) { - newWriteOffsetInBytes = 0; - newWriteOffsetLoopFlag ^= 0x80000000; - } - - c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); - - if (ma_rb_pointer_distance(pRB) == 0) { - return MA_AT_END; - } else { - return MA_SUCCESS; - } -} - -MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newReadOffsetInBytes; - ma_uint32 newReadOffsetLoopFlag; - - if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; - } - - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - newReadOffsetLoopFlag = readOffsetLoopFlag; - - /* We cannot go past the write buffer. */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) { - newReadOffsetInBytes = writeOffsetInBytes; - } else { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); - } - } else { - /* May end up looping. */ - if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; - newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ - } else { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); - } - } - - c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newWriteOffsetInBytes; - ma_uint32 newWriteOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - newWriteOffsetLoopFlag = writeOffsetLoopFlag; - - /* We cannot go past the write buffer. */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - /* May end up looping. */ - if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; - newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ - } else { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); - } - } else { - if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) { - newWriteOffsetInBytes = readOffsetInBytes; - } else { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); - } - } - - c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); - return MA_SUCCESS; -} - -MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - - if (pRB == NULL) { - return 0; - } - - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - return writeOffsetInBytes - readOffsetInBytes; - } else { - return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes); - } -} - -MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB) -{ - ma_int32 dist; - - if (pRB == NULL) { - return 0; - } - - dist = ma_rb_pointer_distance(pRB); - if (dist < 0) { - return 0; - } - - return dist; -} - -MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB)); -} - -MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->subbufferSizeInBytes; -} - -MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - if (pRB->subbufferStrideInBytes == 0) { - return (size_t)pRB->subbufferSizeInBytes; - } - - return (size_t)pRB->subbufferStrideInBytes; -} - -MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex) -{ - if (pRB == NULL) { - return 0; - } - - return subbufferIndex * ma_rb_get_subbuffer_stride(pRB); -} - -MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer) -{ - if (pRB == NULL) { - return NULL; - } - - return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex)); -} - - - -static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - - return ma_get_bytes_per_frame(pRB->format, pRB->channels); -} - -MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) -{ - ma_uint32 bpf; - ma_result result; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pRB); - - bpf = ma_get_bytes_per_frame(format, channels); - if (bpf == 0) { - return MA_INVALID_ARGS; - } - - result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb); - if (result != MA_SUCCESS) { - return result; - } - - pRB->format = format; - pRB->channels = channels; - - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) -{ - return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); -} - -MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_rb_uninit(&pRB->rb); -} - -MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_rb_reset(&pRB->rb); -} - -MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) -{ - size_t sizeInBytes; - ma_result result; - - if (pRB == NULL || pSizeInFrames == NULL) { - return MA_INVALID_ARGS; - } - - sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); - - result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut); - if (result != MA_SUCCESS) { - return result; - } - - *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB)); - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) -{ - size_t sizeInBytes; - ma_result result; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); - - result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut); - if (result != MA_SUCCESS) { - return result; - } - - *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB)); - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer) -{ - if (pRB == NULL) { - return NULL; - } - - return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); -} - - - -MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) -{ - ma_result result; - ma_uint32 sizeInFrames; - - sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5); - if (sizeInFrames == 0) { - return MA_INVALID_ARGS; - } - - result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb); - if (result != MA_SUCCESS) { - return result; - } - - /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */ - ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2); - - return MA_SUCCESS; -} - -MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB) -{ - ma_pcm_rb_uninit((ma_pcm_rb*)pRB); - return MA_SUCCESS; -} - - - -/************************************************************************************************************************************************************** - -Miscellaneous Helpers - -**************************************************************************************************************************************************************/ -MA_API const char* ma_result_description(ma_result result) -{ - switch (result) - { - case MA_SUCCESS: return "No error"; - case MA_ERROR: return "Unknown error"; - case MA_INVALID_ARGS: return "Invalid argument"; - case MA_INVALID_OPERATION: return "Invalid operation"; - case MA_OUT_OF_MEMORY: return "Out of memory"; - case MA_OUT_OF_RANGE: return "Out of range"; - case MA_ACCESS_DENIED: return "Permission denied"; - case MA_DOES_NOT_EXIST: return "Resource does not exist"; - case MA_ALREADY_EXISTS: return "Resource already exists"; - case MA_TOO_MANY_OPEN_FILES: return "Too many open files"; - case MA_INVALID_FILE: return "Invalid file"; - case MA_TOO_BIG: return "Too large"; - case MA_PATH_TOO_LONG: return "Path too long"; - case MA_NAME_TOO_LONG: return "Name too long"; - case MA_NOT_DIRECTORY: return "Not a directory"; - case MA_IS_DIRECTORY: return "Is a directory"; - case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty"; - case MA_AT_END: return "At end"; - case MA_NO_SPACE: return "No space available"; - case MA_BUSY: return "Device or resource busy"; - case MA_IO_ERROR: return "Input/output error"; - case MA_INTERRUPT: return "Interrupted"; - case MA_UNAVAILABLE: return "Resource unavailable"; - case MA_ALREADY_IN_USE: return "Resource already in use"; - case MA_BAD_ADDRESS: return "Bad address"; - case MA_BAD_SEEK: return "Illegal seek"; - case MA_BAD_PIPE: return "Broken pipe"; - case MA_DEADLOCK: return "Deadlock"; - case MA_TOO_MANY_LINKS: return "Too many links"; - case MA_NOT_IMPLEMENTED: return "Not implemented"; - case MA_NO_MESSAGE: return "No message of desired type"; - case MA_BAD_MESSAGE: return "Invalid message"; - case MA_NO_DATA_AVAILABLE: return "No data available"; - case MA_INVALID_DATA: return "Invalid data"; - case MA_TIMEOUT: return "Timeout"; - case MA_NO_NETWORK: return "Network unavailable"; - case MA_NOT_UNIQUE: return "Not unique"; - case MA_NOT_SOCKET: return "Socket operation on non-socket"; - case MA_NO_ADDRESS: return "Destination address required"; - case MA_BAD_PROTOCOL: return "Protocol wrong type for socket"; - case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available"; - case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported"; - case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported"; - case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported"; - case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported"; - case MA_CONNECTION_RESET: return "Connection reset"; - case MA_ALREADY_CONNECTED: return "Already connected"; - case MA_NOT_CONNECTED: return "Not connected"; - case MA_CONNECTION_REFUSED: return "Connection refused"; - case MA_NO_HOST: return "No host"; - case MA_IN_PROGRESS: return "Operation in progress"; - case MA_CANCELLED: return "Operation cancelled"; - case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped"; - - case MA_FORMAT_NOT_SUPPORTED: return "Format not supported"; - case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported"; - case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported"; - case MA_NO_BACKEND: return "No backend"; - case MA_NO_DEVICE: return "No device"; - case MA_API_NOT_FOUND: return "API not found"; - case MA_INVALID_DEVICE_CONFIG: return "Invalid device config"; - - case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized"; - case MA_DEVICE_NOT_STARTED: return "Device not started"; - - case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend"; - case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device"; - case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device"; - case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device"; - - default: return "Unknown error"; - } -} - -MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } else { - return NULL; /* Do not fall back to the default implementation. */ - } - } else { - return ma__malloc_default(sz, NULL); - } -} - -MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - void* p = ma_malloc(sz, pAllocationCallbacks); - if (p != NULL) { - MA_ZERO_MEMORY(p, sz); - } - - return p; -} - -MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData); - } else { - return NULL; /* Do not fall back to the default implementation. */ - } - } else { - return ma__realloc_default(p, sz, NULL); - } -} - -MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL) { - return; - } - - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } else { - return; /* Do no fall back to the default implementation. */ - } - } else { - ma__free_default(p, NULL); - } -} - -MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t extraBytes; - void* pUnaligned; - void* pAligned; - - if (alignment == 0) { - return 0; - } - - extraBytes = alignment-1 + sizeof(void*); - - pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks); - if (pUnaligned == NULL) { - return NULL; - } - - pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1))); - ((void**)pAligned)[-1] = pUnaligned; - - return pAligned; -} - -MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_free(((void**)p)[-1], pAllocationCallbacks); -} - -MA_API const char* ma_get_format_name(ma_format format) -{ - switch (format) - { - case ma_format_unknown: return "Unknown"; - case ma_format_u8: return "8-bit Unsigned Integer"; - case ma_format_s16: return "16-bit Signed Integer"; - case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)"; - case ma_format_s32: return "32-bit Signed Integer"; - case ma_format_f32: return "32-bit IEEE Floating Point"; - default: return "Invalid"; - } -} - -MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels) -{ - ma_uint32 i; - for (i = 0; i < channels; ++i) { - pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor); - } -} - - -MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format) -{ - ma_uint32 sizes[] = { - 0, /* unknown */ - 1, /* u8 */ - 2, /* s16 */ - 3, /* s24 */ - 4, /* s32 */ - 4, /* f32 */ - }; - return sizes[format]; -} - - - -MA_API ma_data_source_config ma_data_source_config_init(void) -{ - ma_data_source_config config; - - MA_ZERO_OBJECT(&config); - - return config; -} - - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSourceBase); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->vtable = pConfig->vtable; - pDataSourceBase->rangeBegInFrames = 0; - pDataSourceBase->rangeEndInFrames = ~((ma_uint64)0); - pDataSourceBase->loopBegInFrames = 0; - pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); - pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */ - pDataSourceBase->pNext = NULL; - pDataSourceBase->onGetNext = NULL; - - return MA_SUCCESS; -} - -MA_API void ma_data_source_uninit(ma_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return; - } - - /* - This is placeholder in case we need this later. Data sources need to call this in their - uninitialization routine to ensure things work later on if something is added here. - */ -} - -static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource) -{ - ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource; - - MA_ASSERT(pDataSource != NULL); - MA_ASSERT(ppCurrentDataSource != NULL); - - if (pCurrentDataSource->pCurrent == NULL) { - /* - The current data source is NULL. If we're using this in the context of a chain we need to return NULL - here so that we don't end up looping. Otherwise we just return the data source itself. - */ - if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) { - pCurrentDataSource = NULL; - } else { - pCurrentDataSource = (ma_data_source_base*)pDataSource; /* Not being used in a chain. Make sure we just always read from the data source itself at all times. */ - } - } else { - pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent; - } - - *ppCurrentDataSource = pCurrentDataSource; - - return MA_SUCCESS; -} - -static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 framesRead = 0; - ma_bool32 loop = ma_data_source_is_looping(pDataSource); - - if (pDataSourceBase == NULL) { - return MA_AT_END; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { - /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); - } else { - /* Need to clamp to within the range. */ - ma_uint64 cursor; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &cursor); - if (result != MA_SUCCESS) { - /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); - } else { - ma_uint64 rangeEnd; - - /* We have the cursor. We need to make sure we don't read beyond our range. */ - rangeEnd = pDataSourceBase->rangeEndInFrames; - - /* If looping, make sure we're within range. */ - if (loop) { - if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); - } - } - - if (frameCount > (rangeEnd - cursor) && rangeEnd != ~((ma_uint64)0)) { - frameCount = (rangeEnd - cursor); - } - - /* - If the cursor is sitting on the end of the range the frame count will be set to 0 which can - result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return - MA_AT_END so the higher level function can know about it. - */ - if (frameCount > 0) { - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); - } else { - result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_data_source_base* pCurrentDataSource; - void* pRunningFramesOut = pFramesOut; - ma_uint64 totalFramesProcessed = 0; - ma_format format; - ma_uint32 channels; - ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */ - ma_bool32 loop; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - loop = ma_data_source_is_looping(pDataSource); - - /* - We need to know the data format so we can advance the output buffer as we read frames. If this - fails, chaining will not work and we'll just read as much as we can from the current source. - */ - if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) { - result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); - if (result != MA_SUCCESS) { - return result; - } - - return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead); - } - - /* - Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and - only the current data source will be read from. - */ - - /* Keep reading until we've read as many frames as possible. */ - while (totalFramesProcessed < frameCount) { - ma_uint64 framesProcessed; - ma_uint64 framesRemaining = frameCount - totalFramesProcessed; - - /* We need to resolve the data source that we'll actually be reading from. */ - result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); - if (result != MA_SUCCESS) { - break; - } - - if (pCurrentDataSource == NULL) { - break; - } - - result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); - totalFramesProcessed += framesProcessed; - - /* - If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is - not necessarily considered an error. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } - - /* - We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned - MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame. - */ - if (result == MA_AT_END) { - /* - The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't - accidentally return MA_AT_END when data has been read in prior loop iterations. at the - end of this function, the result will be checked for MA_SUCCESS, and if the total - number of frames processed is 0, will be explicitly set to MA_AT_END. - */ - result = MA_SUCCESS; - - /* - We reached the end. If we're looping, we just loop back to the start of the current - data source. If we're not looping we need to check if we have another in the chain, and - if so, switch to it. - */ - if (loop) { - if (framesProcessed == 0) { - emptyLoopCounter += 1; - if (emptyLoopCounter > 1) { - break; /* Infinite loop detected. Get out. */ - } - } else { - emptyLoopCounter = 0; - } - - result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames); - if (result != MA_SUCCESS) { - break; /* Failed to loop. Abort. */ - } - - /* Don't return MA_AT_END for looping sounds. */ - result = MA_SUCCESS; - } else { - if (pCurrentDataSource->pNext != NULL) { - pDataSourceBase->pCurrent = pCurrentDataSource->pNext; - } else if (pCurrentDataSource->onGetNext != NULL) { - pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource); - if (pDataSourceBase->pCurrent == NULL) { - break; /* Our callback did not return a next data source. We're done. */ - } - } else { - /* Reached the end of the chain. We're done. */ - break; - } - - /* The next data source needs to be rewound to ensure data is read in looping scenarios. */ - result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); - if (result != MA_SUCCESS) { - break; - } - } - } - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels)); - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */ - - if (result == MA_SUCCESS && totalFramesProcessed == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked) -{ - return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked); -} - -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSourceBase == NULL) { - return MA_SUCCESS; - } - - if (pDataSourceBase->vtable->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - if (frameIndex > pDataSourceBase->rangeEndInFrames) { - return MA_INVALID_OPERATION; /* Trying to seek to far forward. */ - } - - return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); -} - -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - - /* Initialize to defaults for safety just in case the data source does not implement this callback. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - if (pDataSourceBase->vtable->onGetDataFormat == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - return result; - } - - if (pFormat != NULL) { - *pFormat = format; - } - if (pChannels != NULL) { - *pChannels = channels; - } - if (pSampleRate != NULL) { - *pSampleRate = sampleRate; - } - - /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */ - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 cursor; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pDataSourceBase == NULL) { - return MA_SUCCESS; - } - - if (pDataSourceBase->vtable->onGetCursor == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor); - if (result != MA_SUCCESS) { - return result; - } - - /* The cursor needs to be made relative to the start of the range. */ - if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */ - *pCursor = 0; - } else { - *pCursor = cursor - pDataSourceBase->rangeBegInFrames; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - /* - If we have a range defined we'll use that to determine the length. This is one of rare times - where we'll actually trust the caller. If they've set the range, I think it's mostly safe to - assume they've set it based on some higher level knowledge of the structure of the sound bank. - */ - if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) { - *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames; - return MA_SUCCESS; - } - - /* - Getting here means a range is not defined so we'll need to get the data source itself to tell - us the length. - */ - if (pDataSourceBase->vtable->onGetLength == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pDataSourceBase->vtable->onGetLength(pDataSource, pLength); -} - -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor) -{ - ma_result result; - ma_uint64 cursorInPCMFrames; - ma_uint32 sampleRate; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - *pCursor = cursorInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength) -{ - ma_result result; - ma_uint64 lengthInPCMFrames; - ma_uint32 sampleRate; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - *pLength = lengthInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - c89atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); - - /* If there's no callback for this just treat it as a successful no-op. */ - if (pDataSourceBase->vtable->onSetLooping == NULL) { - return MA_SUCCESS; - } - - return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping); -} - -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_FALSE; - } - - return c89atomic_load_32(&pDataSourceBase->isLooping); -} - -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 cursor; - ma_uint64 loopBegAbsolute; - ma_uint64 loopEndAbsolute; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if (rangeEndInFrames < rangeBegInFrames) { - return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */ - } - - /* - The loop points need to be updated. We'll be storing the loop points relative to the range. We'll update - these so that they maintain their absolute positioning. The loop points will then be clamped to the range. - */ - loopBegAbsolute = pDataSourceBase->loopBegInFrames + pDataSourceBase->rangeBegInFrames; - loopEndAbsolute = pDataSourceBase->loopEndInFrames + ((pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) ? pDataSourceBase->rangeBegInFrames : 0); - - pDataSourceBase->rangeBegInFrames = rangeBegInFrames; - pDataSourceBase->rangeEndInFrames = rangeEndInFrames; - - /* Make the loop points relative again, and make sure they're clamped to within the range. */ - if (loopBegAbsolute > pDataSourceBase->rangeBegInFrames) { - pDataSourceBase->loopBegInFrames = loopBegAbsolute - pDataSourceBase->rangeBegInFrames; - } else { - pDataSourceBase->loopBegInFrames = 0; - } - - if (pDataSourceBase->loopBegInFrames > pDataSourceBase->rangeEndInFrames) { - pDataSourceBase->loopBegInFrames = pDataSourceBase->rangeEndInFrames; - } - - /* Only need to update the loop end point if it's not -1. */ - if (loopEndAbsolute != ~((ma_uint64)0)) { - if (loopEndAbsolute > pDataSourceBase->rangeBegInFrames) { - pDataSourceBase->loopEndInFrames = loopEndAbsolute - pDataSourceBase->rangeBegInFrames; - } else { - pDataSourceBase->loopEndInFrames = 0; - } - - if (pDataSourceBase->loopEndInFrames > pDataSourceBase->rangeEndInFrames && pDataSourceBase->loopEndInFrames) { - pDataSourceBase->loopEndInFrames = pDataSourceBase->rangeEndInFrames; - } - } - - - /* If the new range is past the current cursor position we need to seek to it. */ - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); - if (result == MA_SUCCESS) { - /* Seek to within range. Note that our seek positions here are relative to the new range. */ - if (cursor < rangeBegInFrames) { - ma_data_source_seek_to_pcm_frame(pDataSource, 0); - } else if (cursor > rangeEndInFrames) { - ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames); - } - } else { - /* We failed to get the cursor position. Probably means the data source has no notion of a cursor such a noise data source. Just pretend the seeking worked. */ - } - - return MA_SUCCESS; -} - -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return; - } - - if (pRangeBegInFrames != NULL) { - *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames; - } - - if (pRangeEndInFrames != NULL) { - *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames; - } -} - -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if (loopEndInFrames < loopBegInFrames) { - return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */ - } - - if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) { - return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */ - } - - pDataSourceBase->loopBegInFrames = loopBegInFrames; - pDataSourceBase->loopEndInFrames = loopEndInFrames; - - /* The end cannot exceed the range. */ - if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames); - } - - return MA_SUCCESS; -} - -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return; - } - - if (pLoopBegInFrames != NULL) { - *pLoopBegInFrames = pDataSourceBase->loopBegInFrames; - } - - if (pLoopEndInFrames != NULL) { - *pLoopEndInFrames = pDataSourceBase->loopEndInFrames; - } -} - -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->pCurrent = pCurrentDataSource; - - return MA_SUCCESS; -} - -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->pCurrent; -} - -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->pNext = pNextDataSource; - - return MA_SUCCESS; -} - -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->pNext; -} - -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->onGetNext = onGetNext; - - return MA_SUCCESS; -} - -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->onGetNext; -} - - -static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE); - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (framesRead < frameCount || framesRead == 0) { - return MA_AT_END; - } - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex); -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pFormat = pAudioBufferRef->format; - *pChannels = pAudioBufferRef->channels; - *pSampleRate = pAudioBufferRef->sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels); - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pCursor = pAudioBufferRef->cursor; - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pLength = pAudioBufferRef->sizeInFrames; - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable = -{ - ma_audio_buffer_ref__data_source_on_read, - ma_audio_buffer_ref__data_source_on_seek, - ma_audio_buffer_ref__data_source_on_get_data_format, - ma_audio_buffer_ref__data_source_on_get_cursor, - ma_audio_buffer_ref__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pAudioBufferRef); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds); - if (result != MA_SUCCESS) { - return result; - } - - pAudioBufferRef->format = format; - pAudioBufferRef->channels = channels; - pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ - pAudioBufferRef->cursor = 0; - pAudioBufferRef->sizeInFrames = sizeInFrames; - pAudioBufferRef->pData = pData; - - return MA_SUCCESS; -} - -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef) -{ - if (pAudioBufferRef == NULL) { - return; - } - - ma_data_source_uninit(&pAudioBufferRef->ds); -} - -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames) -{ - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - pAudioBufferRef->cursor = 0; - pAudioBufferRef->sizeInFrames = sizeInFrames; - pAudioBufferRef->pData = pData; - - return MA_SUCCESS; -} - -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) -{ - ma_uint64 totalFramesRead = 0; - - if (pAudioBufferRef == NULL) { - return 0; - } - - if (frameCount == 0) { - return 0; - } - - while (totalFramesRead < frameCount) { - ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - ma_uint64 framesRemaining = frameCount - totalFramesRead; - ma_uint64 framesToRead; - - framesToRead = framesRemaining; - if (framesToRead > framesAvailable) { - framesToRead = framesAvailable; - } - - if (pFramesOut != NULL) { - ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); - } - - totalFramesRead += framesToRead; - - pAudioBufferRef->cursor += framesToRead; - if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { - if (loop) { - pAudioBufferRef->cursor = 0; - } else { - break; /* We've reached the end and we're not looping. Done. */ - } - } - - MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames); - } - - return totalFramesRead; -} - -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex) -{ - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - if (frameIndex > pAudioBufferRef->sizeInFrames) { - return MA_INVALID_ARGS; - } - - pAudioBufferRef->cursor = (size_t)frameIndex; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_uint64 framesAvailable; - ma_uint64 frameCount = 0; - - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; /* Safety. */ - } - - if (pFrameCount != NULL) { - frameCount = *pFrameCount; - *pFrameCount = 0; /* Safety. */ - } - - if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - if (frameCount > framesAvailable) { - frameCount = framesAvailable; - } - - *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)); - *pFrameCount = frameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount) -{ - ma_uint64 framesAvailable; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - if (frameCount > framesAvailable) { - return MA_INVALID_ARGS; /* The frame count was too big. This should never happen in an unmapping. Need to make sure the caller is aware of this. */ - } - - pAudioBufferRef->cursor += frameCount; - - if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { - return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */ - } else { - return MA_SUCCESS; - } -} - -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef) -{ - if (pAudioBufferRef == NULL) { - return MA_FALSE; - } - - return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames; -} - -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pAudioBufferRef->cursor; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = pAudioBufferRef->sizeInFrames; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) { - *pAvailableFrames = 0; - } else { - *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - } - - return MA_SUCCESS; -} - - - - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_audio_buffer_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ - config.sizeInFrames = sizeInFrames; - config.pData = pData; - ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks); - - return config; -} - -static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer) -{ - ma_result result; - - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */ - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->sizeInFrames == 0) { - return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */ - } - - result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref); - if (result != MA_SUCCESS) { - return result; - } - - /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */ - pAudioBuffer->ref.sampleRate = pConfig->sampleRate; - - ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks); - - if (doCopy) { - ma_uint64 allocationSizeInBytes; - void* pData; - - allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels); - if (allocationSizeInBytes > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ - if (pData == NULL) { - return MA_OUT_OF_MEMORY; - } - - if (pConfig->pData != NULL) { - ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } else { - ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } - - ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames); - pAudioBuffer->ownsData = MA_TRUE; - } else { - ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames); - pAudioBuffer->ownsData = MA_FALSE; - } - - return MA_SUCCESS; -} - -static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree) -{ - if (pAudioBuffer == NULL) { - return; - } - - if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) { - ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ - } - - if (doFree) { - ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks); - } - - ma_audio_buffer_ref_uninit(&pAudioBuffer->ref); -} - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) -{ - return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer); -} - -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) -{ - return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer); -} - -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer) -{ - ma_result result; - ma_audio_buffer* pAudioBuffer; - ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */ - ma_uint64 allocationSizeInBytes; - - if (ppAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - *ppAudioBuffer = NULL; /* Safety. */ - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - innerConfig = *pConfig; - ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks); - - allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels)); - if (allocationSizeInBytes > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ - if (pAudioBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - if (pConfig->pData != NULL) { - ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } else { - ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } - - innerConfig.pData = &pAudioBuffer->_pExtraData[0]; - - result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer); - if (result != MA_SUCCESS) { - ma_free(pAudioBuffer, &innerConfig.allocationCallbacks); - return result; - } - - *ppAudioBuffer = pAudioBuffer; - - return MA_SUCCESS; -} - -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer) -{ - ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE); -} - -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer) -{ - ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE); -} - -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) -{ - if (pAudioBuffer == NULL) { - return 0; - } - - return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop); -} - -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex); -} - -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount) -{ - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; /* Safety. */ - } - - if (pAudioBuffer == NULL) { - if (pFrameCount != NULL) { - *pFrameCount = 0; - } - - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount); -} - -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount); -} - -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer) -{ - if (pAudioBuffer == NULL) { - return MA_FALSE; - } - - return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref); -} - -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor); -} - -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength); -} - -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames); -} - - - - - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pData); - - pData->format = format; - pData->channels = channels; - pData->pTail = &pData->head; - - return MA_SUCCESS; -} - -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_paged_audio_buffer_page* pPage; - - if (pData == NULL) { - return; - } - - /* All pages need to be freed. */ - pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); - while (pPage != NULL) { - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext); - - ma_free(pPage, pAllocationCallbacks); - pPage = pNext; - } -} - -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return NULL; - } - - return &pData->head; -} - -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return NULL; - } - - return pData->pTail; -} - -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength) -{ - ma_paged_audio_buffer_page* pPage; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - /* Calculate the length from the linked list. */ - for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { - *pLength += pPage->sizeInFrames; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) -{ - ma_paged_audio_buffer_page* pPage; - ma_uint64 allocationSize; - - if (ppPage == NULL) { - return MA_INVALID_ARGS; - } - - *ppPage = NULL; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); - if (allocationSize > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ - if (pPage == NULL) { - return MA_OUT_OF_MEMORY; - } - - pPage->pNext = NULL; - pPage->sizeInFrames = pageSizeInFrames; - - if (pInitialData != NULL) { - ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels); - } - - *ppPage = pPage; - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* It's assumed the page is not attached to the list. */ - ma_free(pPage, pAllocationCallbacks); - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */ - - /* First thing to do is update the tail. */ - for (;;) { - ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->pTail); - ma_paged_audio_buffer_page* pNewTail = pPage; - - if (c89atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { - /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ - c89atomic_exchange_ptr(&pOldTail->pNext, pPage); - break; /* Done. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_paged_audio_buffer_page* pPage; - - result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage); - if (result != MA_SUCCESS) { - return result; - } - - return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */ -} - - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData) -{ - ma_paged_audio_buffer_config config; - - MA_ZERO_OBJECT(&config); - config.pData = pData; - - return config; -} - - -static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex); -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource; - - *pFormat = pPagedAudioBuffer->pData->format; - *pChannels = pPagedAudioBuffer->pData->channels; - *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); - - return MA_SUCCESS; -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor); -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable = -{ - ma_paged_audio_buffer__data_source_on_read, - ma_paged_audio_buffer__data_source_on_seek, - ma_paged_audio_buffer__data_source_on_get_data_format, - ma_paged_audio_buffer__data_source_on_get_cursor, - ma_paged_audio_buffer__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pPagedAudioBuffer); - - /* A config is required for the format and channel count. */ - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pData == NULL) { - return MA_INVALID_ARGS; /* No underlying data specified. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds); - if (result != MA_SUCCESS) { - return result; - } - - pPagedAudioBuffer->pData = pConfig->pData; - pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData); - pPagedAudioBuffer->relativeCursor = 0; - pPagedAudioBuffer->absoluteCursor = 0; - - return MA_SUCCESS; -} - -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer) -{ - if (pPagedAudioBuffer == NULL) { - return; - } - - /* Nothing to do. The data needs to be deleted separately. */ -} - -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesRead = 0; - ma_format format; - ma_uint32 channels; - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - format = pPagedAudioBuffer->pData->format; - channels = pPagedAudioBuffer->pData->channels; - - while (totalFramesRead < frameCount) { - /* Read from the current page. The buffer should never be in a state where this is NULL. */ - ma_uint64 framesRemainingInCurrentPage; - ma_uint64 framesRemainingToRead = frameCount - totalFramesRead; - ma_uint64 framesToReadThisIteration; - - MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL); - - framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor; - - framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead); - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels); - totalFramesRead += framesToReadThisIteration; - - pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration; - pPagedAudioBuffer->relativeCursor += framesToReadThisIteration; - - /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */ - MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames); - - if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { - /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); - if (pNext == NULL) { - result = MA_AT_END; - break; /* We've reached the end. */ - } else { - pPagedAudioBuffer->pCurrent = pNext; - pPagedAudioBuffer->relativeCursor = 0; - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex) -{ - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (frameIndex == pPagedAudioBuffer->absoluteCursor) { - return MA_SUCCESS; /* Nothing to do. */ - } - - if (frameIndex < pPagedAudioBuffer->absoluteCursor) { - /* Moving backwards. Need to move the cursor back to the start, and then move forward. */ - pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData); - pPagedAudioBuffer->absoluteCursor = 0; - pPagedAudioBuffer->relativeCursor = 0; - - /* Fall through to the forward seeking section below. */ - } - - if (frameIndex > pPagedAudioBuffer->absoluteCursor) { - /* Moving forward. */ - ma_paged_audio_buffer_page* pPage; - ma_uint64 runningCursor = 0; - - for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { - ma_uint64 pageRangeBeg = runningCursor; - ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; - - if (frameIndex >= pageRangeBeg) { - if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)c89atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ - /* We found the page. */ - pPagedAudioBuffer->pCurrent = pPage; - pPagedAudioBuffer->absoluteCursor = frameIndex; - pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg; - return MA_SUCCESS; - } - } - - runningCursor = pageRangeEnd; - } - - /* Getting here means we tried seeking too far forward. Don't change any state. */ - return MA_BAD_SEEK; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pPagedAudioBuffer->absoluteCursor; - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength) -{ - return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength); -} - - - -/************************************************************************************************************************************************************** - -VFS - -**************************************************************************************************************************************************************/ -MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pVFS == NULL || pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onOpen == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile); -} - -MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pVFS == NULL || pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onOpenW == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile); -} - -MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onClose == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onClose(pVFS, file); -} - -MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - ma_result result; - size_t bytesRead; - - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - if (pVFS == NULL || file == NULL || pDst == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead); - - if (pBytesRead != NULL) { - *pBytesRead = bytesRead; - } - - if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pBytesWritten != NULL) { - *pBytesWritten = 0; - } - - if (pVFS == NULL || file == NULL || pSrc == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -} - -MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onSeek(pVFS, file, offset, origin); -} - -MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onTell == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onTell(pVFS, file, pCursor); -} - -MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pInfo); - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onInfo == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onInfo(pVFS, file, pInfo); -} - - -static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_vfs_file file; - ma_file_info info; - void* pData; - size_t bytesRead; - - if (ppData != NULL) { - *ppData = NULL; - } - if (pSize != NULL) { - *pSize = 0; - } - - if (ppData == NULL) { - return MA_INVALID_ARGS; - } - - if (pFilePath != NULL) { - result = ma_vfs_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - } else { - result = ma_vfs_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); - } - if (result != MA_SUCCESS) { - return result; - } - - result = ma_vfs_info(pVFS, file, &info); - if (result != MA_SUCCESS) { - ma_vfs_close(pVFS, file); - return result; - } - - if (info.sizeInBytes > MA_SIZE_MAX) { - ma_vfs_close(pVFS, file); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ - if (pData == NULL) { - ma_vfs_close(pVFS, file); - return result; - } - - result = ma_vfs_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ - ma_vfs_close(pVFS, file); - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - if (pSize != NULL) { - *pSize = bytesRead; - } - - MA_ASSERT(ppData != NULL); - *ppData = pData; - - return MA_SUCCESS; -} - -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); -} - -MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); -} - - -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) -static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) -{ - *pDesiredAccess = 0; - if ((openMode & MA_OPEN_MODE_READ) != 0) { - *pDesiredAccess |= GENERIC_READ; - } - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - *pDesiredAccess |= GENERIC_WRITE; - } - - *pShareMode = 0; - if ((openMode & MA_OPEN_MODE_READ) != 0) { - *pShareMode |= FILE_SHARE_READ; - } - - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */ - } else { - *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */ - } -} - -static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - - (void)pVFS; - - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - - hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - - (void)pVFS; - - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - - hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) -{ - (void)pVFS; - - if (CloseHandle((HANDLE)file) == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - ma_result result = MA_SUCCESS; - size_t totalBytesRead; - - (void)pVFS; - - totalBytesRead = 0; - while (totalBytesRead < sizeInBytes) { - size_t bytesRemaining; - DWORD bytesToRead; - DWORD bytesRead; - BOOL readResult; - - bytesRemaining = sizeInBytes - totalBytesRead; - if (bytesRemaining >= 0xFFFFFFFF) { - bytesToRead = 0xFFFFFFFF; - } else { - bytesToRead = (DWORD)bytesRemaining; - } - - readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL); - if (readResult == 1 && bytesRead == 0) { - result = MA_AT_END; - break; /* EOF */ - } - - totalBytesRead += bytesRead; - - if (bytesRead < bytesToRead) { - break; /* EOF */ - } - - if (readResult == 0) { - result = ma_result_from_GetLastError(GetLastError()); - break; - } - } - - if (pBytesRead != NULL) { - *pBytesRead = totalBytesRead; - } - - return result; -} - -static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - ma_result result = MA_SUCCESS; - size_t totalBytesWritten; - - (void)pVFS; - - totalBytesWritten = 0; - while (totalBytesWritten < sizeInBytes) { - size_t bytesRemaining; - DWORD bytesToWrite; - DWORD bytesWritten; - BOOL writeResult; - - bytesRemaining = sizeInBytes - totalBytesWritten; - if (bytesRemaining >= 0xFFFFFFFF) { - bytesToWrite = 0xFFFFFFFF; - } else { - bytesToWrite = (DWORD)bytesRemaining; - } - - writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL); - totalBytesWritten += bytesWritten; - - if (writeResult == 0) { - result = ma_result_from_GetLastError(GetLastError()); - break; - } - } - - if (pBytesWritten != NULL) { - *pBytesWritten = totalBytesWritten; - } - - return result; -} - - -static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - LARGE_INTEGER liDistanceToMove; - DWORD dwMoveMethod; - BOOL result; - - (void)pVFS; - - liDistanceToMove.QuadPart = offset; - - /* */ if (origin == ma_seek_origin_current) { - dwMoveMethod = FILE_CURRENT; - } else if (origin == ma_seek_origin_end) { - dwMoveMethod = FILE_END; - } else { - dwMoveMethod = FILE_BEGIN; - } - -#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) - /* No SetFilePointerEx() so restrict to 31 bits. */ - if (origin > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); -#else - result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); -#endif - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - LARGE_INTEGER liZero; - LARGE_INTEGER liTell; - BOOL result; -#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) - LONG tell; -#endif - - (void)pVFS; - - liZero.QuadPart = 0; - -#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) - result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); - liTell.QuadPart = tell; -#else - result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); -#endif - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - if (pCursor != NULL) { - *pCursor = liTell.QuadPart; - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - BY_HANDLE_FILE_INFORMATION fi; - BOOL result; - - (void)pVFS; - - result = GetFileInformationByHandle((HANDLE)file, &fi); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); - - return MA_SUCCESS; -} -#else -static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_result result; - FILE* pFileStd; - const char* pOpenModeStr; - - MA_ASSERT(pFilePath != NULL); - MA_ASSERT(openMode != 0); - MA_ASSERT(pFile != NULL); - - (void)pVFS; - - if ((openMode & MA_OPEN_MODE_READ) != 0) { - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - pOpenModeStr = "r+"; - } else { - pOpenModeStr = "rb"; - } - } else { - pOpenModeStr = "wb"; - } - - result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr); - if (result != MA_SUCCESS) { - return result; - } - - *pFile = pFileStd; - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_result result; - FILE* pFileStd; - const wchar_t* pOpenModeStr; - - MA_ASSERT(pFilePath != NULL); - MA_ASSERT(openMode != 0); - MA_ASSERT(pFile != NULL); - - (void)pVFS; - - if ((openMode & MA_OPEN_MODE_READ) != 0) { - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - pOpenModeStr = L"r+"; - } else { - pOpenModeStr = L"rb"; - } - } else { - pOpenModeStr = L"wb"; - } - - result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL); - if (result != MA_SUCCESS) { - return result; - } - - *pFile = pFileStd; - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file) -{ - MA_ASSERT(file != NULL); - - (void)pVFS; - - fclose((FILE*)file); - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - size_t result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pDst != NULL); - - (void)pVFS; - - result = fread(pDst, 1, sizeInBytes, (FILE*)file); - - if (pBytesRead != NULL) { - *pBytesRead = result; - } - - if (result != sizeInBytes) { - if (result == 0 && feof((FILE*)file)) { - return MA_AT_END; - } else { - return ma_result_from_errno(ferror((FILE*)file)); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - size_t result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pSrc != NULL); - - (void)pVFS; - - result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file); - - if (pBytesWritten != NULL) { - *pBytesWritten = result; - } - - if (result != sizeInBytes) { - return ma_result_from_errno(ferror((FILE*)file)); - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - int result; - int whence; - - MA_ASSERT(file != NULL); - - (void)pVFS; - - if (origin == ma_seek_origin_start) { - whence = SEEK_SET; - } else if (origin == ma_seek_origin_end) { - whence = SEEK_END; - } else { - whence = SEEK_CUR; - } - -#if defined(_WIN32) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _fseeki64((FILE*)file, offset, whence); - #else - /* No _fseeki64() so restrict to 31 bits. */ - if (origin > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = fseek((FILE*)file, (int)offset, whence); - #endif -#else - result = fseek((FILE*)file, (long int)offset, whence); -#endif - if (result != 0) { - return MA_ERROR; - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - ma_int64 result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pCursor != NULL); - - (void)pVFS; - -#if defined(_WIN32) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _ftelli64((FILE*)file); - #else - result = ftell((FILE*)file); - #endif -#else - result = ftell((FILE*)file); -#endif - - *pCursor = result; - - return MA_SUCCESS; -} - -#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD) -int fileno(FILE *stream); -#endif - -static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - int fd; - struct stat info; - - MA_ASSERT(file != NULL); - MA_ASSERT(pInfo != NULL); - - (void)pVFS; - -#if defined(_MSC_VER) - fd = _fileno((FILE*)file); -#else - fd = fileno((FILE*)file); -#endif - - if (fstat(fd, &info) != 0) { - return ma_result_from_errno(errno); - } - - pInfo->sizeInBytes = info.st_size; - - return MA_SUCCESS; -} -#endif - - -static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) - return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); -#else - return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); -#endif -} - -static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) - return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); -#else - return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); -#endif -} - -static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) -{ - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) - return ma_default_vfs_close__win32(pVFS, file); -#else - return ma_default_vfs_close__stdio(pVFS, file); -#endif -} - -static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - if (file == NULL || pDst == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) - return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); -#else - return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); -#endif -} - -static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - if (pBytesWritten != NULL) { - *pBytesWritten = 0; - } - - if (file == NULL || pSrc == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) - return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -#else - return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -#endif -} - -static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) - return ma_default_vfs_seek__win32(pVFS, file, offset, origin); -#else - return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); -#endif -} - -static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) - return ma_default_vfs_tell__win32(pVFS, file, pCursor); -#else - return ma_default_vfs_tell__stdio(pVFS, file, pCursor); -#endif -} - -static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - if (pInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pInfo); - - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) - return ma_default_vfs_info__win32(pVFS, file, pInfo); -#else - return ma_default_vfs_info__stdio(pVFS, file, pInfo); -#endif -} - - -MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pVFS == NULL) { - return MA_INVALID_ARGS; - } - - pVFS->cb.onOpen = ma_default_vfs_open; - pVFS->cb.onOpenW = ma_default_vfs_open_w; - pVFS->cb.onClose = ma_default_vfs_close; - pVFS->cb.onRead = ma_default_vfs_read; - pVFS->cb.onWrite = ma_default_vfs_write; - pVFS->cb.onSeek = ma_default_vfs_seek; - pVFS->cb.onTell = ma_default_vfs_tell; - pVFS->cb.onInfo = ma_default_vfs_info; - ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pVFS != NULL) { - return ma_vfs_open(pVFS, pFilePath, openMode, pFile); - } else { - return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile); - } -} - -MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pVFS != NULL) { - return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile); - } else { - return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile); - } -} - -MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file) -{ - if (pVFS != NULL) { - return ma_vfs_close(pVFS, file); - } else { - return ma_default_vfs_close(pVFS, file); - } -} - -MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - if (pVFS != NULL) { - return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); - } else { - return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); - } -} - -MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - if (pVFS != NULL) { - return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); - } else { - return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); - } -} - -MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - if (pVFS != NULL) { - return ma_vfs_seek(pVFS, file, offset, origin); - } else { - return ma_default_vfs_seek(pVFS, file, offset, origin); - } -} - -MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - if (pVFS != NULL) { - return ma_vfs_tell(pVFS, file, pCursor); - } else { - return ma_default_vfs_tell(pVFS, file, pCursor); - } -} - -MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - if (pVFS != NULL) { - return ma_vfs_info(pVFS, file, pInfo); - } else { - return ma_default_vfs_info(pVFS, file, pInfo); - } -} - - - -/************************************************************************************************************************************************************** - -Decoding and Encoding Headers. These are auto-generated from a tool. - -**************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -/* dr_wav_h begin */ -#ifndef dr_wav_h -#define dr_wav_h -#ifdef __cplusplus -extern "C" { -#endif -#define DRWAV_STRINGIFY(x) #x -#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) -#define DRWAV_VERSION_MAJOR 0 -#define DRWAV_VERSION_MINOR 13 -#define DRWAV_VERSION_REVISION 6 -#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) -#include -typedef signed char drwav_int8; -typedef unsigned char drwav_uint8; -typedef signed short drwav_int16; -typedef unsigned short drwav_uint16; -typedef signed int drwav_int32; -typedef unsigned int drwav_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 drwav_int64; - typedef unsigned __int64 drwav_uint64; -#else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long drwav_int64; - typedef unsigned long long drwav_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif -#endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) - typedef drwav_uint64 drwav_uintptr; -#else - typedef drwav_uint32 drwav_uintptr; -#endif -typedef drwav_uint8 drwav_bool8; -typedef drwav_uint32 drwav_bool32; -#define DRWAV_TRUE 1 -#define DRWAV_FALSE 0 -#if !defined(DRWAV_API) - #if defined(DRWAV_DLL) - #if defined(_WIN32) - #define DRWAV_DLL_IMPORT __declspec(dllimport) - #define DRWAV_DLL_EXPORT __declspec(dllexport) - #define DRWAV_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define DRWAV_DLL_IMPORT __attribute__((visibility("default"))) - #define DRWAV_DLL_EXPORT __attribute__((visibility("default"))) - #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define DRWAV_DLL_IMPORT - #define DRWAV_DLL_EXPORT - #define DRWAV_DLL_PRIVATE static - #endif - #endif - #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) - #define DRWAV_API DRWAV_DLL_EXPORT - #else - #define DRWAV_API DRWAV_DLL_IMPORT - #endif - #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE - #else - #define DRWAV_API extern - #define DRWAV_PRIVATE static - #endif -#endif -typedef drwav_int32 drwav_result; -#define DRWAV_SUCCESS 0 -#define DRWAV_ERROR -1 -#define DRWAV_INVALID_ARGS -2 -#define DRWAV_INVALID_OPERATION -3 -#define DRWAV_OUT_OF_MEMORY -4 -#define DRWAV_OUT_OF_RANGE -5 -#define DRWAV_ACCESS_DENIED -6 -#define DRWAV_DOES_NOT_EXIST -7 -#define DRWAV_ALREADY_EXISTS -8 -#define DRWAV_TOO_MANY_OPEN_FILES -9 -#define DRWAV_INVALID_FILE -10 -#define DRWAV_TOO_BIG -11 -#define DRWAV_PATH_TOO_LONG -12 -#define DRWAV_NAME_TOO_LONG -13 -#define DRWAV_NOT_DIRECTORY -14 -#define DRWAV_IS_DIRECTORY -15 -#define DRWAV_DIRECTORY_NOT_EMPTY -16 -#define DRWAV_END_OF_FILE -17 -#define DRWAV_NO_SPACE -18 -#define DRWAV_BUSY -19 -#define DRWAV_IO_ERROR -20 -#define DRWAV_INTERRUPT -21 -#define DRWAV_UNAVAILABLE -22 -#define DRWAV_ALREADY_IN_USE -23 -#define DRWAV_BAD_ADDRESS -24 -#define DRWAV_BAD_SEEK -25 -#define DRWAV_BAD_PIPE -26 -#define DRWAV_DEADLOCK -27 -#define DRWAV_TOO_MANY_LINKS -28 -#define DRWAV_NOT_IMPLEMENTED -29 -#define DRWAV_NO_MESSAGE -30 -#define DRWAV_BAD_MESSAGE -31 -#define DRWAV_NO_DATA_AVAILABLE -32 -#define DRWAV_INVALID_DATA -33 -#define DRWAV_TIMEOUT -34 -#define DRWAV_NO_NETWORK -35 -#define DRWAV_NOT_UNIQUE -36 -#define DRWAV_NOT_SOCKET -37 -#define DRWAV_NO_ADDRESS -38 -#define DRWAV_BAD_PROTOCOL -39 -#define DRWAV_PROTOCOL_UNAVAILABLE -40 -#define DRWAV_PROTOCOL_NOT_SUPPORTED -41 -#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define DRWAV_SOCKET_NOT_SUPPORTED -44 -#define DRWAV_CONNECTION_RESET -45 -#define DRWAV_ALREADY_CONNECTED -46 -#define DRWAV_NOT_CONNECTED -47 -#define DRWAV_CONNECTION_REFUSED -48 -#define DRWAV_NO_HOST -49 -#define DRWAV_IN_PROGRESS -50 -#define DRWAV_CANCELLED -51 -#define DRWAV_MEMORY_ALREADY_MAPPED -52 -#define DRWAV_AT_END -53 -#define DR_WAVE_FORMAT_PCM 0x1 -#define DR_WAVE_FORMAT_ADPCM 0x2 -#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 -#define DR_WAVE_FORMAT_ALAW 0x6 -#define DR_WAVE_FORMAT_MULAW 0x7 -#define DR_WAVE_FORMAT_DVI_ADPCM 0x11 -#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE -#define DRWAV_SEQUENTIAL 0x00000001 -DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision); -DRWAV_API const char* drwav_version_string(void); -typedef enum -{ - drwav_seek_origin_start, - drwav_seek_origin_current -} drwav_seek_origin; -typedef enum -{ - drwav_container_riff, - drwav_container_w64, - drwav_container_rf64 -} drwav_container; -typedef struct -{ - union - { - drwav_uint8 fourcc[4]; - drwav_uint8 guid[16]; - } id; - drwav_uint64 sizeInBytes; - unsigned int paddingSize; -} drwav_chunk_header; -typedef struct -{ - drwav_uint16 formatTag; - drwav_uint16 channels; - drwav_uint32 sampleRate; - drwav_uint32 avgBytesPerSec; - drwav_uint16 blockAlign; - drwav_uint16 bitsPerSample; - drwav_uint16 extendedSize; - drwav_uint16 validBitsPerSample; - drwav_uint32 channelMask; - drwav_uint8 subFormat[16]; -} drwav_fmt; -DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT); -typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); -typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); -typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT); -typedef struct -{ - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} drwav_allocation_callbacks; -typedef struct -{ - const drwav_uint8* data; - size_t dataSize; - size_t currentReadPos; -} drwav__memory_stream; -typedef struct -{ - void** ppData; - size_t* pDataSize; - size_t dataSize; - size_t dataCapacity; - size_t currentWritePos; -} drwav__memory_stream_write; -typedef struct -{ - drwav_container container; - drwav_uint32 format; - drwav_uint32 channels; - drwav_uint32 sampleRate; - drwav_uint32 bitsPerSample; -} drwav_data_format; -typedef enum -{ - drwav_metadata_type_none = 0, - drwav_metadata_type_unknown = 1 << 0, - drwav_metadata_type_smpl = 1 << 1, - drwav_metadata_type_inst = 1 << 2, - drwav_metadata_type_cue = 1 << 3, - drwav_metadata_type_acid = 1 << 4, - drwav_metadata_type_bext = 1 << 5, - drwav_metadata_type_list_label = 1 << 6, - drwav_metadata_type_list_note = 1 << 7, - drwav_metadata_type_list_labelled_cue_region = 1 << 8, - drwav_metadata_type_list_info_software = 1 << 9, - drwav_metadata_type_list_info_copyright = 1 << 10, - drwav_metadata_type_list_info_title = 1 << 11, - drwav_metadata_type_list_info_artist = 1 << 12, - drwav_metadata_type_list_info_comment = 1 << 13, - drwav_metadata_type_list_info_date = 1 << 14, - drwav_metadata_type_list_info_genre = 1 << 15, - drwav_metadata_type_list_info_album = 1 << 16, - drwav_metadata_type_list_info_tracknumber = 1 << 17, - drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software - | drwav_metadata_type_list_info_copyright - | drwav_metadata_type_list_info_title - | drwav_metadata_type_list_info_artist - | drwav_metadata_type_list_info_comment - | drwav_metadata_type_list_info_date - | drwav_metadata_type_list_info_genre - | drwav_metadata_type_list_info_album - | drwav_metadata_type_list_info_tracknumber, - drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label - | drwav_metadata_type_list_note - | drwav_metadata_type_list_labelled_cue_region, - drwav_metadata_type_all = -2, - drwav_metadata_type_all_including_unknown = -1 -} drwav_metadata_type; -typedef enum -{ - drwav_smpl_loop_type_forward = 0, - drwav_smpl_loop_type_pingpong = 1, - drwav_smpl_loop_type_backward = 2 -} drwav_smpl_loop_type; -typedef struct -{ - drwav_uint32 cuePointId; - drwav_uint32 type; - drwav_uint32 firstSampleByteOffset; - drwav_uint32 lastSampleByteOffset; - drwav_uint32 sampleFraction; - drwav_uint32 playCount; -} drwav_smpl_loop; -typedef struct -{ - drwav_uint32 manufacturerId; - drwav_uint32 productId; - drwav_uint32 samplePeriodNanoseconds; - drwav_uint32 midiUnityNote; - drwav_uint32 midiPitchFraction; - drwav_uint32 smpteFormat; - drwav_uint32 smpteOffset; - drwav_uint32 sampleLoopCount; - drwav_uint32 samplerSpecificDataSizeInBytes; - drwav_smpl_loop* pLoops; - drwav_uint8* pSamplerSpecificData; -} drwav_smpl; -typedef struct -{ - drwav_int8 midiUnityNote; - drwav_int8 fineTuneCents; - drwav_int8 gainDecibels; - drwav_int8 lowNote; - drwav_int8 highNote; - drwav_int8 lowVelocity; - drwav_int8 highVelocity; -} drwav_inst; -typedef struct -{ - drwav_uint32 id; - drwav_uint32 playOrderPosition; - drwav_uint8 dataChunkId[4]; - drwav_uint32 chunkStart; - drwav_uint32 blockStart; - drwav_uint32 sampleByteOffset; -} drwav_cue_point; -typedef struct -{ - drwav_uint32 cuePointCount; - drwav_cue_point *pCuePoints; -} drwav_cue; -typedef enum -{ - drwav_acid_flag_one_shot = 1, - drwav_acid_flag_root_note_set = 2, - drwav_acid_flag_stretch = 4, - drwav_acid_flag_disk_based = 8, - drwav_acid_flag_acidizer = 16 -} drwav_acid_flag; -typedef struct -{ - drwav_uint32 flags; - drwav_uint16 midiUnityNote; - drwav_uint16 reserved1; - float reserved2; - drwav_uint32 numBeats; - drwav_uint16 meterDenominator; - drwav_uint16 meterNumerator; - float tempo; -} drwav_acid; -typedef struct -{ - drwav_uint32 cuePointId; - drwav_uint32 stringLength; - char* pString; -} drwav_list_label_or_note; -typedef struct -{ - char* pDescription; - char* pOriginatorName; - char* pOriginatorReference; - char pOriginationDate[10]; - char pOriginationTime[8]; - drwav_uint64 timeReference; - drwav_uint16 version; - char* pCodingHistory; - drwav_uint32 codingHistorySize; - drwav_uint8* pUMID; - drwav_uint16 loudnessValue; - drwav_uint16 loudnessRange; - drwav_uint16 maxTruePeakLevel; - drwav_uint16 maxMomentaryLoudness; - drwav_uint16 maxShortTermLoudness; -} drwav_bext; -typedef struct -{ - drwav_uint32 stringLength; - char* pString; -} drwav_list_info_text; -typedef struct -{ - drwav_uint32 cuePointId; - drwav_uint32 sampleLength; - drwav_uint8 purposeId[4]; - drwav_uint16 country; - drwav_uint16 language; - drwav_uint16 dialect; - drwav_uint16 codePage; - drwav_uint32 stringLength; - char* pString; -} drwav_list_labelled_cue_region; -typedef enum -{ - drwav_metadata_location_invalid, - drwav_metadata_location_top_level, - drwav_metadata_location_inside_info_list, - drwav_metadata_location_inside_adtl_list -} drwav_metadata_location; -typedef struct -{ - drwav_uint8 id[4]; - drwav_metadata_location chunkLocation; - drwav_uint32 dataSizeInBytes; - drwav_uint8* pData; -} drwav_unknown_metadata; -typedef struct -{ - drwav_metadata_type type; - union - { - drwav_cue cue; - drwav_smpl smpl; - drwav_acid acid; - drwav_inst inst; - drwav_bext bext; - drwav_list_label_or_note labelOrNote; - drwav_list_labelled_cue_region labelledCueRegion; - drwav_list_info_text infoText; - drwav_unknown_metadata unknown; - } data; -} drwav_metadata; -typedef struct -{ - drwav_read_proc onRead; - drwav_write_proc onWrite; - drwav_seek_proc onSeek; - void* pUserData; - drwav_allocation_callbacks allocationCallbacks; - drwav_container container; - drwav_fmt fmt; - drwav_uint32 sampleRate; - drwav_uint16 channels; - drwav_uint16 bitsPerSample; - drwav_uint16 translatedFormatTag; - drwav_uint64 totalPCMFrameCount; - drwav_uint64 dataChunkDataSize; - drwav_uint64 dataChunkDataPos; - drwav_uint64 bytesRemaining; - drwav_uint64 readCursorInPCMFrames; - drwav_uint64 dataChunkDataSizeTargetWrite; - drwav_bool32 isSequentialWrite; - drwav_metadata_type allowedMetadataTypes; - drwav_metadata* pMetadata; - drwav_uint32 metadataCount; - drwav__memory_stream memoryStream; - drwav__memory_stream_write memoryStreamWrite; - struct - { - drwav_uint32 bytesRemainingInBlock; - drwav_uint16 predictor[2]; - drwav_int32 delta[2]; - drwav_int32 cachedFrames[4]; - drwav_uint32 cachedFrameCount; - drwav_int32 prevFrames[2][2]; - } msadpcm; - struct - { - drwav_uint32 bytesRemainingInBlock; - drwav_int32 predictor[2]; - drwav_int32 stepIndex[2]; - drwav_int32 cachedFrames[16]; - drwav_uint32 cachedFrameCount; - } ima; -} drwav; -DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount); -DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount); -DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav); -DRWAV_API drwav_result drwav_uninit(drwav* pWav); -DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex); -DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor); -DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength); -DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData); -DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -#ifndef DR_WAV_NO_CONVERSION_API -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount); -DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount); -DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount); -DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount); -DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount); -DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); -DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount); -DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount); -DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount); -DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); -#endif -#ifndef DR_WAV_NO_STDIO -DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); -#endif -DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_WAV_NO_CONVERSION_API -DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_WAV_NO_STDIO -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -#endif -DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -#endif -DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data); -DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data); -DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data); -DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data); -DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data); -DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data); -DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data); -DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]); -DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); -#ifdef __cplusplus -} -#endif -#endif -/* dr_wav_h end */ -#endif /* MA_NO_WAV */ - -#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -/* dr_flac_h begin */ -#ifndef dr_flac_h -#define dr_flac_h -#ifdef __cplusplus -extern "C" { -#endif -#define DRFLAC_STRINGIFY(x) #x -#define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) -#define DRFLAC_VERSION_MAJOR 0 -#define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 38 -#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) -#include -typedef signed char drflac_int8; -typedef unsigned char drflac_uint8; -typedef signed short drflac_int16; -typedef unsigned short drflac_uint16; -typedef signed int drflac_int32; -typedef unsigned int drflac_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 drflac_int64; - typedef unsigned __int64 drflac_uint64; -#else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long drflac_int64; - typedef unsigned long long drflac_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif -#endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) - typedef drflac_uint64 drflac_uintptr; -#else - typedef drflac_uint32 drflac_uintptr; -#endif -typedef drflac_uint8 drflac_bool8; -typedef drflac_uint32 drflac_bool32; -#define DRFLAC_TRUE 1 -#define DRFLAC_FALSE 0 -#if !defined(DRFLAC_API) - #if defined(DRFLAC_DLL) - #if defined(_WIN32) - #define DRFLAC_DLL_IMPORT __declspec(dllimport) - #define DRFLAC_DLL_EXPORT __declspec(dllexport) - #define DRFLAC_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define DRFLAC_DLL_IMPORT __attribute__((visibility("default"))) - #define DRFLAC_DLL_EXPORT __attribute__((visibility("default"))) - #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define DRFLAC_DLL_IMPORT - #define DRFLAC_DLL_EXPORT - #define DRFLAC_DLL_PRIVATE static - #endif - #endif - #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) - #define DRFLAC_API DRFLAC_DLL_EXPORT - #else - #define DRFLAC_API DRFLAC_DLL_IMPORT - #endif - #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE - #else - #define DRFLAC_API extern - #define DRFLAC_PRIVATE static - #endif -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1700 - #define DRFLAC_DEPRECATED __declspec(deprecated) -#elif (defined(__GNUC__) && __GNUC__ >= 4) - #define DRFLAC_DEPRECATED __attribute__((deprecated)) -#elif defined(__has_feature) - #if __has_feature(attribute_deprecated) - #define DRFLAC_DEPRECATED __attribute__((deprecated)) - #else - #define DRFLAC_DEPRECATED - #endif -#else - #define DRFLAC_DEPRECATED -#endif -DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); -DRFLAC_API const char* drflac_version_string(void); -#ifndef DR_FLAC_BUFFER_SIZE -#define DR_FLAC_BUFFER_SIZE 4096 -#endif -#if defined(_WIN64) || defined(_LP64) || defined(__LP64__) -#define DRFLAC_64BIT -#endif -#ifdef DRFLAC_64BIT -typedef drflac_uint64 drflac_cache_t; -#else -typedef drflac_uint32 drflac_cache_t; -#endif -#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 -#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 -#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 -#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 -#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 -#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 -#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 -#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 -#define DRFLAC_PICTURE_TYPE_OTHER 0 -#define DRFLAC_PICTURE_TYPE_FILE_ICON 1 -#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 -#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 -#define DRFLAC_PICTURE_TYPE_COVER_BACK 4 -#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 -#define DRFLAC_PICTURE_TYPE_MEDIA 6 -#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 -#define DRFLAC_PICTURE_TYPE_ARTIST 8 -#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 -#define DRFLAC_PICTURE_TYPE_BAND 10 -#define DRFLAC_PICTURE_TYPE_COMPOSER 11 -#define DRFLAC_PICTURE_TYPE_LYRICIST 12 -#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 -#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 -#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 -#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 -#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 -#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 -#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 -#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 -typedef enum -{ - drflac_container_native, - drflac_container_ogg, - drflac_container_unknown -} drflac_container; -typedef enum -{ - drflac_seek_origin_start, - drflac_seek_origin_current -} drflac_seek_origin; -#pragma pack(2) -typedef struct -{ - drflac_uint64 firstPCMFrame; - drflac_uint64 flacFrameOffset; - drflac_uint16 pcmFrameCount; -} drflac_seekpoint; -#pragma pack() -typedef struct -{ - drflac_uint16 minBlockSizeInPCMFrames; - drflac_uint16 maxBlockSizeInPCMFrames; - drflac_uint32 minFrameSizeInPCMFrames; - drflac_uint32 maxFrameSizeInPCMFrames; - drflac_uint32 sampleRate; - drflac_uint8 channels; - drflac_uint8 bitsPerSample; - drflac_uint64 totalPCMFrameCount; - drflac_uint8 md5[16]; -} drflac_streaminfo; -typedef struct -{ - drflac_uint32 type; - const void* pRawData; - drflac_uint32 rawDataSize; - union - { - drflac_streaminfo streaminfo; - struct - { - int unused; - } padding; - struct - { - drflac_uint32 id; - const void* pData; - drflac_uint32 dataSize; - } application; - struct - { - drflac_uint32 seekpointCount; - const drflac_seekpoint* pSeekpoints; - } seektable; - struct - { - drflac_uint32 vendorLength; - const char* vendor; - drflac_uint32 commentCount; - const void* pComments; - } vorbis_comment; - struct - { - char catalog[128]; - drflac_uint64 leadInSampleCount; - drflac_bool32 isCD; - drflac_uint8 trackCount; - const void* pTrackData; - } cuesheet; - struct - { - drflac_uint32 type; - drflac_uint32 mimeLength; - const char* mime; - drflac_uint32 descriptionLength; - const char* description; - drflac_uint32 width; - drflac_uint32 height; - drflac_uint32 colorDepth; - drflac_uint32 indexColorCount; - drflac_uint32 pictureDataSize; - const drflac_uint8* pPictureData; - } picture; - } data; -} drflac_metadata; -typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); -typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); -typedef struct -{ - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} drflac_allocation_callbacks; -typedef struct -{ - const drflac_uint8* data; - size_t dataSize; - size_t currentReadPos; -} drflac__memory_stream; -typedef struct -{ - drflac_read_proc onRead; - drflac_seek_proc onSeek; - void* pUserData; - size_t unalignedByteCount; - drflac_cache_t unalignedCache; - drflac_uint32 nextL2Line; - drflac_uint32 consumedBits; - drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; - drflac_cache_t cache; - drflac_uint16 crc16; - drflac_cache_t crc16Cache; - drflac_uint32 crc16CacheIgnoredBytes; -} drflac_bs; -typedef struct -{ - drflac_uint8 subframeType; - drflac_uint8 wastedBitsPerSample; - drflac_uint8 lpcOrder; - drflac_int32* pSamplesS32; -} drflac_subframe; -typedef struct -{ - drflac_uint64 pcmFrameNumber; - drflac_uint32 flacFrameNumber; - drflac_uint32 sampleRate; - drflac_uint16 blockSizeInPCMFrames; - drflac_uint8 channelAssignment; - drflac_uint8 bitsPerSample; - drflac_uint8 crc8; -} drflac_frame_header; -typedef struct -{ - drflac_frame_header header; - drflac_uint32 pcmFramesRemaining; - drflac_subframe subframes[8]; -} drflac_frame; -typedef struct -{ - drflac_meta_proc onMeta; - void* pUserDataMD; - drflac_allocation_callbacks allocationCallbacks; - drflac_uint32 sampleRate; - drflac_uint8 channels; - drflac_uint8 bitsPerSample; - drflac_uint16 maxBlockSizeInPCMFrames; - drflac_uint64 totalPCMFrameCount; - drflac_container container; - drflac_uint32 seekpointCount; - drflac_frame currentFLACFrame; - drflac_uint64 currentPCMFrame; - drflac_uint64 firstFLACFramePosInBytes; - drflac__memory_stream memoryStream; - drflac_int32* pDecodedSamples; - drflac_seekpoint* pSeekpoints; - void* _oggbs; - drflac_bool32 _noSeekTableSeek : 1; - drflac_bool32 _noBinarySearchSeek : 1; - drflac_bool32 _noBruteForceSeek : 1; - drflac_bs bs; - drflac_uint8 pExtraData[1]; -} drflac; -DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API void drflac_close(drflac* pFlac); -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); -DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); -#ifndef DR_FLAC_NO_STDIO -DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -#endif -DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_FLAC_NO_STDIO -DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -#endif -DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); -typedef struct -{ - drflac_uint32 countRemaining; - const char* pRunningData; -} drflac_vorbis_comment_iterator; -DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); -DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); -typedef struct -{ - drflac_uint32 countRemaining; - const char* pRunningData; -} drflac_cuesheet_track_iterator; -#pragma pack(4) -typedef struct -{ - drflac_uint64 offset; - drflac_uint8 index; - drflac_uint8 reserved[3]; -} drflac_cuesheet_track_index; -#pragma pack() -typedef struct -{ - drflac_uint64 offset; - drflac_uint8 trackNumber; - char ISRC[12]; - drflac_bool8 isAudio; - drflac_bool8 preEmphasis; - drflac_uint8 indexCount; - const drflac_cuesheet_track_index* pIndexPoints; -} drflac_cuesheet_track; -DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); -DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); -#ifdef __cplusplus -} -#endif -#endif -/* dr_flac_h end */ -#endif /* MA_NO_FLAC */ - -#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -/* dr_mp3_h begin */ -#ifndef dr_mp3_h -#define dr_mp3_h -#ifdef __cplusplus -extern "C" { -#endif -#define DRMP3_STRINGIFY(x) #x -#define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) -#define DRMP3_VERSION_MAJOR 0 -#define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 33 -#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) -#include -typedef signed char drmp3_int8; -typedef unsigned char drmp3_uint8; -typedef signed short drmp3_int16; -typedef unsigned short drmp3_uint16; -typedef signed int drmp3_int32; -typedef unsigned int drmp3_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 drmp3_int64; - typedef unsigned __int64 drmp3_uint64; -#else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long drmp3_int64; - typedef unsigned long long drmp3_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif -#endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) - typedef drmp3_uint64 drmp3_uintptr; -#else - typedef drmp3_uint32 drmp3_uintptr; -#endif -typedef drmp3_uint8 drmp3_bool8; -typedef drmp3_uint32 drmp3_bool32; -#define DRMP3_TRUE 1 -#define DRMP3_FALSE 0 -#if !defined(DRMP3_API) - #if defined(DRMP3_DLL) - #if defined(_WIN32) - #define DRMP3_DLL_IMPORT __declspec(dllimport) - #define DRMP3_DLL_EXPORT __declspec(dllexport) - #define DRMP3_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define DRMP3_DLL_IMPORT __attribute__((visibility("default"))) - #define DRMP3_DLL_EXPORT __attribute__((visibility("default"))) - #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define DRMP3_DLL_IMPORT - #define DRMP3_DLL_EXPORT - #define DRMP3_DLL_PRIVATE static - #endif - #endif - #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) - #define DRMP3_API DRMP3_DLL_EXPORT - #else - #define DRMP3_API DRMP3_DLL_IMPORT - #endif - #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE - #else - #define DRMP3_API extern - #define DRMP3_PRIVATE static - #endif -#endif -typedef drmp3_int32 drmp3_result; -#define DRMP3_SUCCESS 0 -#define DRMP3_ERROR -1 -#define DRMP3_INVALID_ARGS -2 -#define DRMP3_INVALID_OPERATION -3 -#define DRMP3_OUT_OF_MEMORY -4 -#define DRMP3_OUT_OF_RANGE -5 -#define DRMP3_ACCESS_DENIED -6 -#define DRMP3_DOES_NOT_EXIST -7 -#define DRMP3_ALREADY_EXISTS -8 -#define DRMP3_TOO_MANY_OPEN_FILES -9 -#define DRMP3_INVALID_FILE -10 -#define DRMP3_TOO_BIG -11 -#define DRMP3_PATH_TOO_LONG -12 -#define DRMP3_NAME_TOO_LONG -13 -#define DRMP3_NOT_DIRECTORY -14 -#define DRMP3_IS_DIRECTORY -15 -#define DRMP3_DIRECTORY_NOT_EMPTY -16 -#define DRMP3_END_OF_FILE -17 -#define DRMP3_NO_SPACE -18 -#define DRMP3_BUSY -19 -#define DRMP3_IO_ERROR -20 -#define DRMP3_INTERRUPT -21 -#define DRMP3_UNAVAILABLE -22 -#define DRMP3_ALREADY_IN_USE -23 -#define DRMP3_BAD_ADDRESS -24 -#define DRMP3_BAD_SEEK -25 -#define DRMP3_BAD_PIPE -26 -#define DRMP3_DEADLOCK -27 -#define DRMP3_TOO_MANY_LINKS -28 -#define DRMP3_NOT_IMPLEMENTED -29 -#define DRMP3_NO_MESSAGE -30 -#define DRMP3_BAD_MESSAGE -31 -#define DRMP3_NO_DATA_AVAILABLE -32 -#define DRMP3_INVALID_DATA -33 -#define DRMP3_TIMEOUT -34 -#define DRMP3_NO_NETWORK -35 -#define DRMP3_NOT_UNIQUE -36 -#define DRMP3_NOT_SOCKET -37 -#define DRMP3_NO_ADDRESS -38 -#define DRMP3_BAD_PROTOCOL -39 -#define DRMP3_PROTOCOL_UNAVAILABLE -40 -#define DRMP3_PROTOCOL_NOT_SUPPORTED -41 -#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define DRMP3_SOCKET_NOT_SUPPORTED -44 -#define DRMP3_CONNECTION_RESET -45 -#define DRMP3_ALREADY_CONNECTED -46 -#define DRMP3_NOT_CONNECTED -47 -#define DRMP3_CONNECTION_REFUSED -48 -#define DRMP3_NO_HOST -49 -#define DRMP3_IN_PROGRESS -50 -#define DRMP3_CANCELLED -51 -#define DRMP3_MEMORY_ALREADY_MAPPED -52 -#define DRMP3_AT_END -53 -#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 -#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) -#ifdef _MSC_VER - #define DRMP3_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define DRMP3_GNUC_INLINE_HINT __inline__ - #else - #define DRMP3_GNUC_INLINE_HINT inline - #endif - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) - #else - #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT - #endif -#elif defined(__WATCOMC__) - #define DRMP3_INLINE __inline -#else - #define DRMP3_INLINE -#endif -DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision); -DRMP3_API const char* drmp3_version_string(void); -typedef struct -{ - int frame_bytes, channels, hz, layer, bitrate_kbps; -} drmp3dec_frame_info; -typedef struct -{ - float mdct_overlap[2][9*32], qmf_state[15*2*32]; - int reserv, free_format_bytes; - drmp3_uint8 header[4], reserv_buf[511]; -} drmp3dec; -DRMP3_API void drmp3dec_init(drmp3dec *dec); -DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); -DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples); -typedef enum -{ - drmp3_seek_origin_start, - drmp3_seek_origin_current -} drmp3_seek_origin; -typedef struct -{ - drmp3_uint64 seekPosInBytes; - drmp3_uint64 pcmFrameIndex; - drmp3_uint16 mp3FramesToDiscard; - drmp3_uint16 pcmFramesToDiscard; -} drmp3_seek_point; -typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); -typedef struct -{ - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} drmp3_allocation_callbacks; -typedef struct -{ - drmp3_uint32 channels; - drmp3_uint32 sampleRate; -} drmp3_config; -typedef struct -{ - drmp3dec decoder; - drmp3dec_frame_info frameInfo; - drmp3_uint32 channels; - drmp3_uint32 sampleRate; - drmp3_read_proc onRead; - drmp3_seek_proc onSeek; - void* pUserData; - drmp3_allocation_callbacks allocationCallbacks; - drmp3_uint32 mp3FrameChannels; - drmp3_uint32 mp3FrameSampleRate; - drmp3_uint32 pcmFramesConsumedInMP3Frame; - drmp3_uint32 pcmFramesRemainingInMP3Frame; - drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; - drmp3_uint64 currentPCMFrame; - drmp3_uint64 streamCursor; - drmp3_seek_point* pSeekPoints; - drmp3_uint32 seekPointCount; - size_t dataSize; - size_t dataCapacity; - size_t dataConsumed; - drmp3_uint8* pData; - drmp3_bool32 atEnd : 1; - struct - { - const drmp3_uint8* pData; - size_t dataSize; - size_t currentReadPos; - } memory; -} drmp3; -DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_MP3_NO_STDIO -DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); -#endif -DRMP3_API void drmp3_uninit(drmp3* pMP3); -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut); -DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex); -DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3); -DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3); -DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount); -DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints); -DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints); -DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_MP3_NO_STDIO -DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -#endif -DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); -#ifdef __cplusplus -} -#endif -#endif -/* dr_mp3_h end */ -#endif /* MA_NO_MP3 */ - - -/************************************************************************************************************************************************************** - -Decoding - -**************************************************************************************************************************************************************/ -#ifndef MA_NO_DECODING - -static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - MA_ASSERT(pDecoder != NULL); - - return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) -{ - MA_ASSERT(pDecoder != NULL); - - return pDecoder->onSeek(pDecoder, byteOffset, origin); -} - -static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - - if (pDecoder->onTell == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pDecoder->onTell(pDecoder, pCursor); -} - - -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount) -{ - ma_decoding_backend_config config; - - MA_ZERO_OBJECT(&config); - config.preferredFormat = preferredFormat; - config.seekPointCount = seekPointCount; - - return config; -} - - -MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate) -{ - ma_decoder_config config; - MA_ZERO_OBJECT(&config); - config.format = outputFormat; - config.channels = outputChannels; - config.sampleRate = outputSampleRate; - config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */ - config.encodingFormat = ma_encoding_format_unknown; - - /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ - - return config; -} - -MA_API ma_decoder_config ma_decoder_config_init_default() -{ - return ma_decoder_config_init(ma_format_unknown, 0, 0); -} - -MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig) -{ - ma_decoder_config config; - if (pConfig != NULL) { - config = *pConfig; - } else { - MA_ZERO_OBJECT(&config); - } - - return config; -} - -static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig) -{ - ma_result result; - ma_data_converter_config converterConfig; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pConfig != NULL); - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap)); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal data format. */ - } - - - /* Make sure we're not asking for too many channels. */ - if (pConfig->channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */ - if (internalChannels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - - /* Output format. */ - if (pConfig->format == ma_format_unknown) { - pDecoder->outputFormat = internalFormat; - } else { - pDecoder->outputFormat = pConfig->format; - } - - if (pConfig->channels == 0) { - pDecoder->outputChannels = internalChannels; - } else { - pDecoder->outputChannels = pConfig->channels; - } - - if (pConfig->sampleRate == 0) { - pDecoder->outputSampleRate = internalSampleRate; - } else { - pDecoder->outputSampleRate = pConfig->sampleRate; - } - - converterConfig = ma_data_converter_config_init( - internalFormat, pDecoder->outputFormat, - internalChannels, pDecoder->outputChannels, - internalSampleRate, pDecoder->outputSampleRate - ); - converterConfig.pChannelMapIn = internalChannelMap; - converterConfig.pChannelMapOut = pConfig->pChannelMap; - converterConfig.channelMixMode = pConfig->channelMixMode; - converterConfig.ditherMode = pConfig->ditherMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ - converterConfig.resampling = pConfig->resampling; - - result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter); - if (result != MA_SUCCESS) { - return result; - } - - /* - Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll - need this if the data converter does not support calculation of the required input frame count. To - determine support for this we'll just run a test. - */ - { - ma_uint64 unused; - - result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); - if (result != MA_SUCCESS) { - /* - We were unable to calculate the required input frame count which means we'll need to use - a heap-allocated cache. - */ - ma_uint64 inputCacheCapSizeInBytes; - - pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); - - /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ - inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */ - if (pDecoder->pInputCache == NULL) { - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - } - } - - return MA_SUCCESS; -} - - - -static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_seek_bytes(pDecoder, offset, origin); -} - -static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_tell_bytes(pDecoder, pCursor); -} - - -static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInit == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - - - -static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL && pVTable->onInit != NULL) { - result = ma_decoder_init_from_vtable(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } else { - /* Initialization failed. Move on to the next one, but seek back to the start first so the next vtable starts from the first byte of the file. */ - result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; /* Failed to seek back to the start. */ - } - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - - -/* WAV */ -#ifdef dr_wav_h -#define MA_HAS_WAV - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32, s16 or s32. */ -#if !defined(MA_NO_WAV) - drwav dr; -#endif -} ma_wav; - -MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex); -MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor); -MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength); - - -static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex); -} - -static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor); -} - -static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_wav_ds_vtable = -{ - ma_wav_ds_read, - ma_wav_ds_seek, - ma_wav_ds_get_data_format, - ma_wav_ds_get_cursor, - ma_wav_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_WAV) -static drwav_allocation_callbacks drwav_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) -{ - drwav_allocation_callbacks callbacks; - - if (pAllocationCallbacks != NULL) { - callbacks.onMalloc = pAllocationCallbacks->onMalloc; - callbacks.onRealloc = pAllocationCallbacks->onRealloc; - callbacks.onFree = pAllocationCallbacks->onFree; - callbacks.pUserData = pAllocationCallbacks->pUserData; - } else { - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - callbacks.pUserData = NULL; - } - - return callbacks; -} - -static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pWav != NULL); - - result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static drwav_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, drwav_seek_origin origin) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pWav != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == drwav_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWav); - pWav->format = ma_format_unknown; /* Use closest match to source file by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { - pWav->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_wav_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pWav->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->onTell = onTell; - pWav->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_WAV) - { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; - - wavResult = drwav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, &wavAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - /* - If an explicit format was not specified, try picking the closest match based on the internal - format. The format needs to be supported by miniaudio. - */ - if (pWav->format == ma_format_unknown) { - switch (pWav->dr.translatedFormatTag) - { - case DR_WAVE_FORMAT_PCM: - { - if (pWav->dr.bitsPerSample == 8) { - pWav->format = ma_format_u8; - } else if (pWav->dr.bitsPerSample == 16) { - pWav->format = ma_format_s16; - } else if (pWav->dr.bitsPerSample == 24) { - pWav->format = ma_format_s24; - } else if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_s32; - } - } break; - - case DR_WAVE_FORMAT_IEEE_FLOAT: - { - if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_f32; - } - } break; - - default: break; - } - - /* Fall back to f32 if we couldn't find anything. */ - if (pWav->format == ma_format_unknown) { - pWav->format = ma_format_f32; - } - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; - - wavResult = drwav_init_file(&pWav->dr, pFilePath, &wavAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; - - wavResult = drwav_init_file_w(&pWav->dr, pFilePath, &wavAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; - - wavResult = drwav_init_memory(&pWav->dr, pData, dataSize, &wavAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL) { - return; - } - - (void)pAllocationCallbacks; - - #if !defined(MA_NO_WAV) - { - drwav_uninit(&pWav->dr); - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pWav->ds); -} - -MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = drwav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = drwav_read_pcm_frames_s16(&pWav->dr, frameCount, (drwav_int16*)pFramesOut); - } break; - - case ma_format_s32: - { - totalFramesRead = drwav_read_pcm_frames_s32(&pWav->dr, frameCount, (drwav_int32*)pFramesOut); - } break; - - /* Fallback to a raw read. */ - case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ - default: - { - totalFramesRead = drwav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); - } break; - } - - /* In the future we'll update dr_wav to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) -{ - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - drwav_bool32 wavResult; - - wavResult = drwav_seek_to_pcm_frame(&pWav->dr, frameIndex); - if (wavResult != DRWAV_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pWav == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pWav->format; - } - - #if !defined(MA_NO_WAV) - { - if (pChannels != NULL) { - *pChannels = pWav->dr.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pWav->dr.sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels); - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - drwav_result wavResult = drwav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); - if (wavResult != DRWAV_SUCCESS) { - return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */ - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - drwav_result wavResult = drwav_get_length_in_pcm_frames(&pWav->dr, pLength); - if (wavResult != DRWAV_SUCCESS) { - return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */ - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__wav(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_wav* pWav = (ma_wav*)pBackend; - - (void)pUserData; - - ma_wav_uninit(pWav, pAllocationCallbacks); - ma_free(pWav, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = -{ - ma_decoding_backend_init__wav, - ma_decoding_backend_init_file__wav, - ma_decoding_backend_init_file_w__wav, - ma_decoding_backend_init_memory__wav, - ma_decoding_backend_uninit__wav -}; - -static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); -} -#endif /* dr_wav_h */ - -/* FLAC */ -#ifdef dr_flac_h -#define MA_HAS_FLAC - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32, s16 or s32. */ -#if !defined(MA_NO_FLAC) - drflac* dr; -#endif -} ma_flac; - -MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex); -MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor); -MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength); - - -static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex); -} - -static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor); -} - -static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_flac_ds_vtable = -{ - ma_flac_ds_read, - ma_flac_ds_seek, - ma_flac_ds_get_data_format, - ma_flac_ds_get_cursor, - ma_flac_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_FLAC) -static drflac_allocation_callbacks drflac_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) -{ - drflac_allocation_callbacks callbacks; - - if (pAllocationCallbacks != NULL) { - callbacks.onMalloc = pAllocationCallbacks->onMalloc; - callbacks.onRealloc = pAllocationCallbacks->onRealloc; - callbacks.onFree = pAllocationCallbacks->onFree; - callbacks.pUserData = pAllocationCallbacks->pUserData; - } else { - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - callbacks.pUserData = NULL; - } - - return callbacks; -} - -static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pFlac != NULL); - - result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static drflac_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, drflac_seek_origin origin) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pFlac != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == drflac_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFlac); - pFlac->format = ma_format_f32; /* f32 by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { - pFlac->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_flac_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pFlac->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pFlac->onRead = onRead; - pFlac->onSeek = onSeek; - pFlac->onTell = onTell; - pFlac->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_FLAC) - { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, &flacAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open_file(pFilePath, &flacAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open_file_w(pFilePath, &flacAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open_memory(pData, dataSize, &flacAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFlac == NULL) { - return; - } - - (void)pAllocationCallbacks; - - #if !defined(MA_NO_FLAC) - { - drflac_close(pFlac->dr); - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pFlac->ds); -} - -MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = drflac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = drflac_read_pcm_frames_s16(pFlac->dr, frameCount, (drflac_int16*)pFramesOut); - } break; - - case ma_format_s32: - { - totalFramesRead = drflac_read_pcm_frames_s32(pFlac->dr, frameCount, (drflac_int32*)pFramesOut); - } break; - - case ma_format_u8: - case ma_format_s24: - case ma_format_unknown: - default: - { - return MA_INVALID_OPERATION; - }; - } - - /* In the future we'll update dr_flac to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) -{ - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - drflac_bool32 flacResult; - - flacResult = drflac_seek_to_pcm_frame(pFlac->dr, frameIndex); - if (flacResult != DRFLAC_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pFlac == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pFlac->format; - } - - #if !defined(MA_NO_FLAC) - { - if (pChannels != NULL) { - *pChannels = pFlac->dr->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pFlac->dr->sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels); - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - *pCursor = pFlac->dr->currentPCMFrame; - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - *pLength = pFlac->dr->totalPCMFrameCount; - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__flac(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_flac* pFlac = (ma_flac*)pBackend; - - (void)pUserData; - - ma_flac_uninit(pFlac, pAllocationCallbacks); - ma_free(pFlac, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = -{ - ma_decoding_backend_init__flac, - ma_decoding_backend_init_file__flac, - ma_decoding_backend_init_file_w__flac, - ma_decoding_backend_init_memory__flac, - ma_decoding_backend_uninit__flac -}; - -static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); -} -#endif /* dr_flac_h */ - -/* MP3 */ -#ifdef dr_mp3_h -#define MA_HAS_MP3 - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32 or s16. */ -#if !defined(MA_NO_MP3) - drmp3 dr; - drmp3_uint32 seekPointCount; - drmp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ -#endif -} ma_mp3; - -MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex); -MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor); -MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength); - - -static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex); -} - -static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor); -} - -static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_mp3_ds_vtable = -{ - ma_mp3_ds_read, - ma_mp3_ds_seek, - ma_mp3_ds_get_data_format, - ma_mp3_ds_get_cursor, - ma_mp3_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_MP3) -static drmp3_allocation_callbacks drmp3_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) -{ - drmp3_allocation_callbacks callbacks; - - if (pAllocationCallbacks != NULL) { - callbacks.onMalloc = pAllocationCallbacks->onMalloc; - callbacks.onRealloc = pAllocationCallbacks->onRealloc; - callbacks.onFree = pAllocationCallbacks->onFree; - callbacks.pUserData = pAllocationCallbacks->pUserData; - } else { - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - callbacks.pUserData = NULL; - } - - return callbacks; -} - -static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pMP3 != NULL); - - result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static drmp3_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, drmp3_seek_origin origin) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pMP3 != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == drmp3_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pMP3); - pMP3->format = ma_format_f32; /* f32 by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { - pMP3->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_mp3_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pMP3->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) -{ - drmp3_bool32 mp3Result; - drmp3_uint32 seekPointCount = 0; - drmp3_seek_point* pSeekPoints = NULL; - - MA_ASSERT(pMP3 != NULL); - MA_ASSERT(pConfig != NULL); - - seekPointCount = pConfig->seekPointCount; - if (seekPointCount > 0) { - pSeekPoints = (drmp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); - if (pSeekPoints == NULL) { - return MA_OUT_OF_MEMORY; - } - } - - mp3Result = drmp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); - if (mp3Result != MA_TRUE) { - return MA_ERROR; - } - - mp3Result = drmp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); - if (mp3Result != MA_TRUE) { - ma_free(pSeekPoints, pAllocationCallbacks); - return MA_ERROR; - } - - pMP3->seekPointCount = seekPointCount; - pMP3->pSeekPoints = pSeekPoints; - - return MA_SUCCESS; -} - -MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->onTell = onTell; - pMP3->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_MP3) - { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; - - mp3Result = drmp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, &mp3AllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; - - mp3Result = drmp3_init_file(&pMP3->dr, pFilePath, &mp3AllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; - - mp3Result = drmp3_init_file_w(&pMP3->dr, pFilePath, &mp3AllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; - - mp3Result = drmp3_init_memory(&pMP3->dr, pData, dataSize, &mp3AllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return; - } - - #if !defined(MA_NO_MP3) - { - drmp3_uninit(&pMP3->dr); - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */ - ma_free(pMP3->pSeekPoints, pAllocationCallbacks); - - ma_data_source_uninit(&pMP3->ds); -} - -MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = drmp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = drmp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (drmp3_int16*)pFramesOut); - } break; - - case ma_format_u8: - case ma_format_s24: - case ma_format_s32: - case ma_format_unknown: - default: - { - return MA_INVALID_OPERATION; - }; - } - - /* In the future we'll update dr_mp3 to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) -{ - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - drmp3_bool32 mp3Result; - - mp3Result = drmp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); - if (mp3Result != DRMP3_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pMP3 == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pMP3->format; - } - - #if !defined(MA_NO_MP3) - { - if (pChannels != NULL) { - *pChannels = pMP3->dr.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pMP3->dr.sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels); - } - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - *pCursor = pMP3->dr.currentPCMFrame; - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - *pLength = drmp3_get_pcm_frame_count(&pMP3->dr); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__mp3(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_mp3* pMP3 = (ma_mp3*)pBackend; - - (void)pUserData; - - ma_mp3_uninit(pMP3, pAllocationCallbacks); - ma_free(pMP3, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = -{ - ma_decoding_backend_init__mp3, - ma_decoding_backend_init_file__mp3, - ma_decoding_backend_init_file_w__mp3, - ma_decoding_backend_init_memory__mp3, - ma_decoding_backend_uninit__mp3 -}; - -static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); -} -#endif /* dr_mp3_h */ - -/* Vorbis */ -#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H -#define MA_HAS_VORBIS - -/* The size in bytes of each chunk of data to read from the Vorbis stream. */ -#define MA_VORBIS_DATA_CHUNK_SIZE 4096 - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_allocation_callbacks allocationCallbacks; /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */ - ma_format format; /* Only f32 is allowed with stb_vorbis. */ - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; -#if !defined(MA_NO_VORBIS) - stb_vorbis* stb; - ma_bool32 usingPushMode; - struct - { - ma_uint8* pData; - size_t dataSize; - size_t dataCapacity; - ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */ - ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */ - float** ppPacketData; - } push; -#endif -} ma_stbvorbis; - -MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex); -MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor); -MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength); - - -static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex); -} - -static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor); -} - -static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_stbvorbis_ds_vtable = -{ - ma_stbvorbis_ds_read, - ma_stbvorbis_ds_seek, - ma_stbvorbis_ds_get_data_format, - ma_stbvorbis_ds_get_cursor, - ma_stbvorbis_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - (void)pConfig; - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pVorbis); - pVorbis->format = ma_format_f32; /* Only supporting f32. */ - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -#if !defined(MA_NO_VORBIS) -static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) -{ - stb_vorbis_info info; - - MA_ASSERT(pVorbis != NULL); - - info = stb_vorbis_get_info(pVorbis->stb); - - pVorbis->channels = info.channels; - pVorbis->sampleRate = info.sample_rate; - - return MA_SUCCESS; -} -#endif - -MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pVorbis->onRead = onRead; - pVorbis->onSeek = onSeek; - pVorbis->onTell = onTell; - pVorbis->pReadSeekTellUserData = pReadSeekTellUserData; - ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks); - - #if !defined(MA_NO_VORBIS) - { - /* - stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the - pushing API. In order for us to be able to successfully initialize the decoder we need to - supply it with enough data. We need to keep loading data until we have enough. - */ - stb_vorbis* stb; - size_t dataSize = 0; - size_t dataCapacity = 0; - ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ - - for (;;) { - int vorbisError; - int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ - size_t bytesRead; - ma_uint8* pNewData; - - /* Allocate memory for the new chunk. */ - dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, pAllocationCallbacks); - if (pNewData == NULL) { - ma_free(pData, pAllocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pData = pNewData; - - /* Read in the next chunk. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); - dataSize += bytesRead; - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - /* We have a maximum of 31 bits with stb_vorbis. */ - if (dataSize > INT_MAX) { - ma_free(pData, pAllocationCallbacks); - return MA_TOO_BIG; - } - - stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); - if (stb != NULL) { - /* - Successfully opened the Vorbis decoder. We might have some leftover unprocessed - data so we'll need to move that down to the front. - */ - dataSize -= (size_t)consumedDataSize; /* Consume the data. */ - MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); - break; - } else { - /* Failed to open the decoder. */ - if (vorbisError == VORBIS_need_more_data) { - continue; - } else { - ma_free(pData, pAllocationCallbacks); - return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ - } - } - } - - MA_ASSERT(stb != NULL); - pVorbis->stb = stb; - pVorbis->push.pData = pData; - pVorbis->push.dataSize = dataSize; - pVorbis->push.dataCapacity = dataCapacity; - - pVorbis->usingPushMode = MA_TRUE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - ma_free(pData, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_VORBIS) - { - (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */ - - /* We can use stb_vorbis' pull mode for file based streams. */ - pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL); - if (pVorbis->stb == NULL) { - return MA_INVALID_FILE; - } - - pVorbis->usingPushMode = MA_FALSE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_VORBIS) - { - (void)pAllocationCallbacks; - - /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ - if (dataSize > INT_MAX) { - return MA_TOO_BIG; - } - - pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL); - if (pVorbis->stb == NULL) { - return MA_INVALID_FILE; - } - - pVorbis->usingPushMode = MA_FALSE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pVorbis == NULL) { - return; - } - - #if !defined(MA_NO_VORBIS) - { - stb_vorbis_close(pVorbis->stb); - - /* We'll have to clear some memory if we're using push mode. */ - if (pVorbis->usingPushMode) { - ma_free(pVorbis->push.pData, pAllocationCallbacks); - } - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pVorbis->ds); -} - -MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - ma_uint32 channels; - - ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0); - - if (format == ma_format_f32) { - /* We read differently depending on whether or not we're using push mode. */ - if (pVorbis->usingPushMode) { - /* Push mode. This is the complex case. */ - float* pFramesOutF32 = (float*)pFramesOut; - - while (totalFramesRead < frameCount) { - /* The first thing to do is read from any already-cached frames. */ - ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ - - /* The output pointer can be null in which case we just treate it as a seek. */ - if (pFramesOut != NULL) { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) { - pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame]; - } - - pFramesOutF32 += pVorbis->channels; - } - } - - /* Update pointers and counters. */ - pVorbis->push.framesConsumed += framesToReadFromCache; - pVorbis->push.framesRemaining -= framesToReadFromCache; - totalFramesRead += framesToReadFromCache; - - /* Don't bother reading any more frames right now if we've just finished loading. */ - if (totalFramesRead == frameCount) { - break; - } - - MA_ASSERT(pVorbis->push.framesRemaining == 0); - - /* Getting here means we've run out of cached frames. We'll need to load some more. */ - for (;;) { - int samplesRead = 0; - int consumedDataSize; - - /* We need to case dataSize to an int, so make sure we can do it safely. */ - if (pVorbis->push.dataSize > INT_MAX) { - break; /* Too big. */ - } - - consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead); - if (consumedDataSize != 0) { - /* Successfully decoded a Vorbis frame. Consume the data. */ - pVorbis->push.dataSize -= (size_t)consumedDataSize; - MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize); - - pVorbis->push.framesConsumed = 0; - pVorbis->push.framesRemaining = samplesRead; - - break; - } else { - /* Not enough data. Read more. */ - size_t bytesRead; - - /* Expand the data buffer if necessary. */ - if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) { - size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE; - ma_uint8* pNewData; - - pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks); - if (pNewData == NULL) { - result = MA_OUT_OF_MEMORY; - break; - } - - pVorbis->push.pData = pNewData; - pVorbis->push.dataCapacity = newCap; - } - - /* We should have enough room to load some data. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead); - pVorbis->push.dataSize += bytesRead; - - if (result != MA_SUCCESS) { - break; /* Failed to read any data. Get out. */ - } - } - } - - /* If we don't have a success code at this point it means we've encounted an error or the end of the file has been reached (probably the latter). */ - if (result != MA_SUCCESS) { - break; - } - } - } else { - /* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */ - while (totalFramesRead < frameCount) { - ma_uint64 framesRemaining = (frameCount - totalFramesRead); - int framesRead; - - if (framesRemaining > INT_MAX) { - framesRemaining = INT_MAX; - } - - framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */ - totalFramesRead += framesRead; - - if (framesRead < (int)framesRemaining) { - break; /* Nothing left to read. Get out. */ - } - } - } - } else { - result = MA_INVALID_ARGS; - } - - pVorbis->cursor += totalFramesRead; - - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex) -{ - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - /* Different seeking methods depending on whether or not we're using push mode. */ - if (pVorbis->usingPushMode) { - /* Push mode. This is the complex case. */ - ma_result result; - float buffer[4096]; - - /* - This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs - a full decode right from the start of the stream. Later on I'll need to write a layer that goes through all of the Ogg pages until we - find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis. - - TODO: Use seeking logic documented for stb_vorbis_flush_pushdata(). - */ - - /* Seek to the start of the file to begin with. */ - result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; - } - - stb_vorbis_flush_pushdata(pVorbis->stb); - pVorbis->push.framesRemaining = 0; - pVorbis->push.dataSize = 0; - - /* Move the cursor back to the start. We'll increment this in the loop below. */ - pVorbis->cursor = 0; - - while (pVorbis->cursor < frameIndex) { - ma_uint64 framesRead; - ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; - if (framesToRead > (frameIndex - pVorbis->cursor)) { - framesToRead = (frameIndex - pVorbis->cursor); - } - - result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); - pVorbis->cursor += framesRead; - - if (result != MA_SUCCESS) { - return result; - } - } - } else { - /* Pull mode. This is the simple case. */ - int vorbisResult; - - if (frameIndex > UINT_MAX) { - return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ - } - - vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */ - if (vorbisResult == 0) { - return MA_ERROR; /* See failed. */ - } - - pVorbis->cursor = frameIndex; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pVorbis == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pVorbis->format; - } - - #if !defined(MA_NO_VORBIS) - { - if (pChannels != NULL) { - *pChannels = pVorbis->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pVorbis->sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels); - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - *pCursor = pVorbis->cursor; - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - if (pVorbis->usingPushMode) { - *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */ - } else { - *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb); - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend; - - (void)pUserData; - - ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks); - ma_free(pVorbis, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = -{ - ma_decoding_backend_init__stbvorbis, - ma_decoding_backend_init_file__stbvorbis, - NULL, /* onInitFileW() */ - ma_decoding_backend_init_memory__stbvorbis, - ma_decoding_backend_uninit__stbvorbis -}; - -static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); -} -#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ - - - -static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - MA_ASSERT(pDecoder != NULL); - - if (pConfig != NULL) { - return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks); - } else { - pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default(); - return MA_SUCCESS; - } -} - -static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); -} - -static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor); -} - -static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_decoder_data_source_vtable = -{ - ma_decoder__data_source_on_read, - ma_decoder__data_source_on_seek, - ma_decoder__data_source_on_get_data_format, - ma_decoder__data_source_on_get_cursor, - ma_decoder__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - MA_ASSERT(pConfig != NULL); - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDecoder); - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->onRead = onRead; - pDecoder->onSeek = onSeek; - pDecoder->onTell = onTell; - pDecoder->pUserData = pUserData; - - result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder); - if (result != MA_SUCCESS) { - ma_data_source_uninit(&pDecoder->ds); - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__init_data_converter(pDecoder, pConfig); - - /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */ - if (result != MA_SUCCESS) { - ma_decoder_uninit(pDecoder); - return result; - } - - return result; -} - - -static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - /* Silence some warnings in the case that we don't have any decoder backends enabled. */ - (void)onRead; - (void)onSeek; - (void)pUserData; - - - /* If we've specified a specific encoding type, try that first. */ - if (pConfig->encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (pConfig->encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (pConfig->encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (pConfig->encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (pConfig->encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); - } - #endif - - /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */ - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (pConfig->encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder__postinit(pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_decoder_config config; - ma_result result; - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder); -} - - -static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - size_t bytesRemaining; - - MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos); - - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - - if (bytesRemaining == 0) { - return MA_AT_END; - } - - if (bytesToRead > 0) { - MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead); - pDecoder->data.memory.currentReadPos += bytesToRead; - } - - if (pBytesRead != NULL) { - *pBytesRead = bytesToRead; - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) -{ - if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) { - return MA_BAD_SEEK; - } - - if (origin == ma_seek_origin_current) { - if (byteOffset > 0) { - if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) { - byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */ - } - - pDecoder->data.memory.currentReadPos += (size_t)byteOffset; - } else { - if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */ - } - - pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset; - } - } else { - if (origin == ma_seek_origin_end) { - if (byteOffset < 0) { - byteOffset = -byteOffset; - } - - if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) { - pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */ - } else { - pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset; - } - } else { - if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) { - pDecoder->data.memory.currentReadPos = (size_t)byteOffset; - } else { - pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */ - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pCursor != NULL); - - *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos; - - return MA_SUCCESS; -} - -static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - pDecoder->data.memory.pData = (const ma_uint8*)pData; - pDecoder->data.memory.dataSize = dataSize; - pDecoder->data.memory.currentReadPos = 0; - - (void)pConfig; - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_decoder_config config; - ma_result result; - - config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */ - - result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); -} - - -#if defined(MA_HAS_WAV) || \ - defined(MA_HAS_MP3) || \ - defined(MA_HAS_FLAC) || \ - defined(MA_HAS_VORBIS) || \ - defined(MA_HAS_OPUS) -#define MA_HAS_PATH_API -#endif - -#if defined(MA_HAS_PATH_API) -static const char* ma_path_file_name(const char* path) -{ - const char* fileName; - - if (path == NULL) { - return NULL; - } - - fileName = path; - - /* We just loop through the path until we find the last slash. */ - while (path[0] != '\0') { - if (path[0] == '/' || path[0] == '\\') { - fileName = path; - } - - path += 1; - } - - /* At this point the file name is sitting on a slash, so just move forward. */ - while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { - fileName += 1; - } - - return fileName; -} - -static const wchar_t* ma_path_file_name_w(const wchar_t* path) -{ - const wchar_t* fileName; - - if (path == NULL) { - return NULL; - } - - fileName = path; - - /* We just loop through the path until we find the last slash. */ - while (path[0] != '\0') { - if (path[0] == '/' || path[0] == '\\') { - fileName = path; - } - - path += 1; - } - - /* At this point the file name is sitting on a slash, so just move forward. */ - while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { - fileName += 1; - } - - return fileName; -} - - -static const char* ma_path_extension(const char* path) -{ - const char* extension; - const char* lastOccurance; - - if (path == NULL) { - path = ""; - } - - extension = ma_path_file_name(path); - lastOccurance = NULL; - - /* Just find the last '.' and return. */ - while (extension[0] != '\0') { - if (extension[0] == '.') { - extension += 1; - lastOccurance = extension; - } - - extension += 1; - } - - return (lastOccurance != NULL) ? lastOccurance : extension; -} - -static const wchar_t* ma_path_extension_w(const wchar_t* path) -{ - const wchar_t* extension; - const wchar_t* lastOccurance; - - if (path == NULL) { - path = L""; - } - - extension = ma_path_file_name_w(path); - lastOccurance = NULL; - - /* Just find the last '.' and return. */ - while (extension[0] != '\0') { - if (extension[0] == '.') { - extension += 1; - lastOccurance = extension; - } - - extension += 1; - } - - return (lastOccurance != NULL) ? lastOccurance : extension; -} - - -static ma_bool32 ma_path_extension_equal(const char* path, const char* extension) -{ - const char* ext1; - const char* ext2; - - if (path == NULL || extension == NULL) { - return MA_FALSE; - } - - ext1 = extension; - ext2 = ma_path_extension(path); - -#if defined(_MSC_VER) || defined(__DMC__) - return _stricmp(ext1, ext2) == 0; -#else - return strcasecmp(ext1, ext2) == 0; -#endif -} - -static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension) -{ - const wchar_t* ext1; - const wchar_t* ext2; - - if (path == NULL || extension == NULL) { - return MA_FALSE; - } - - ext1 = extension; - ext2 = ma_path_extension_w(path); - -#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) - return _wcsicmp(ext1, ext2) == 0; -#else - /* - I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This - isn't the most efficient way to do it, but it should work OK. - */ - { - char ext1MB[4096]; - char ext2MB[4096]; - const wchar_t* pext1 = ext1; - const wchar_t* pext2 = ext2; - mbstate_t mbs1; - mbstate_t mbs2; - - MA_ZERO_OBJECT(&mbs1); - MA_ZERO_OBJECT(&mbs2); - - if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) { - return MA_FALSE; - } - if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) { - return MA_FALSE; - } - - return strcasecmp(ext1MB, ext2MB) == 0; - } -#endif -} -#endif /* MA_HAS_PATH_API */ - - - -static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pBufferOut != NULL); - - return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) -{ - MA_ASSERT(pDecoder != NULL); - - return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); -} - -static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - - return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor); -} - -static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->data.vfs.pVFS = pVFS; - pDecoder->data.vfs.file = file; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(&config, pDecoder); - } - #endif - - /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ - if (result != MA_SUCCESS) { - result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); - } else { - result = ma_decoder__postinit(&config, pDecoder); - } - - if (result != MA_SUCCESS) { - if (pDecoder->data.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */ - ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); - } - - return result; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->data.vfs.pVFS = pVFS; - pDecoder->data.vfs.file = file; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(&config, pDecoder); - } - #endif - - /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ - if (result != MA_SUCCESS) { - result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); - } else { - result = ma_decoder__postinit(&config, pDecoder); - } - - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - } - - if (pDecoder->onRead == ma_decoder__on_read_vfs) { - ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file); - pDecoder->data.vfs.file = NULL; - } - - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - ma_data_source_uninit(&pDecoder->ds); - - if (pDecoder->pInputCache != NULL) { - ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesReadOut; - void* pRunningFramesOut; - - if (pFramesRead != NULL) { - *pFramesRead = 0; /* Safety. */ - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend == NULL) { - return MA_INVALID_OPERATION; - } - - /* Fast path. */ - if (pDecoder->converter.isPassthrough) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut); - } else { - /* - Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we - need to run through each sample because we need to ensure it's internal cache is updated. - */ - if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); - } else { - /* Slow path. Need to run everything through the data converter. */ - ma_format internalFormat; - ma_uint32 internalChannels; - - totalFramesReadOut = 0; - pRunningFramesOut = pFramesOut; - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal format and channel count. */ - } - - /* - We run a different path depending on whether or not we are using a heap-allocated - intermediary buffer or not. If the data converter does not support the calculation of - the required number of input frames, we'll use the heap-allocated path. Otherwise we'll - use the stack-allocated path. - */ - if (pDecoder->pInputCache != NULL) { - /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */ - while (totalFramesReadOut < frameCount) { - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - - /* If there's any data available in the cache, that needs to get processed first. */ - if (pDecoder->inputCacheRemaining > 0) { - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) { - framesToReadThisIterationIn = pDecoder->inputCacheRemaining; - } - - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - pDecoder->inputCacheConsumed += framesToReadThisIterationIn; - pDecoder->inputCacheRemaining -= framesToReadThisIterationIn; - - totalFramesReadOut += framesToReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - - /* Getting here means there's no data in the cache and we need to fill it up from the data source. */ - if (pDecoder->inputCacheRemaining == 0) { - pDecoder->inputCacheConsumed = 0; - - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining); - if (result != MA_SUCCESS) { - break; - } - } - } - } else { - /* We have a way of determining the required number of input frames so just use the stack. */ - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - if (requiredInputFrameCount > 0) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); - } else { - framesReadThisIterationIn = 0; - } - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } - } - - pDecoder->readPointerInPCMFrames += totalFramesReadOut; - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesReadOut; - } - - if (result == MA_SUCCESS && totalFramesReadOut == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 internalFrameIndex; - ma_uint32 internalSampleRate; - ma_uint64 currentFrameIndex; - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - internalFrameIndex = frameIndex; - } else { - internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); - } - - /* Only seek if we're requesting a different frame to what we're currently sitting on. */ - ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex); - if (currentFrameIndex != internalFrameIndex) { - result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); - if (result == MA_SUCCESS) { - pDecoder->readPointerInPCMFrames = frameIndex; - } - - /* Reset the data converter so that any cached data in the resampler is cleared. */ - ma_data_converter_reset(&pDecoder->converter); - } - - return result; - } - - /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ - return MA_INVALID_ARGS; -} - -MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pFormat != NULL) { - *pFormat = pDecoder->outputFormat; - } - - if (pChannels != NULL) { - *pChannels = pDecoder->outputChannels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pDecoder->outputSampleRate; - } - - if (pChannelMap != NULL) { - ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pDecoder->readPointerInPCMFrames; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 internalLengthInPCMFrames; - ma_uint32 internalSampleRate; - - result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal length. */ - } - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - *pLength = internalLengthInPCMFrames; - } else { - *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames); - } - - return MA_SUCCESS; - } else { - return MA_NO_BACKEND; - } -} - -MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) -{ - ma_result result; - ma_uint64 totalFrameCount; - - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); - if (result != MA_SUCCESS) { - return result; - } - - if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { - *pAvailableFrames = 0; - } else { - *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_result result; - ma_uint64 totalFrameCount; - ma_uint64 bpf; - ma_uint64 dataCapInFrames; - void* pPCMFramesOut; - - MA_ASSERT(pDecoder != NULL); - - totalFrameCount = 0; - bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); - - /* The frame count is unknown until we try reading. Thus, we just run in a loop. */ - dataCapInFrames = 0; - pPCMFramesOut = NULL; - for (;;) { - ma_uint64 frameCountToTryReading; - ma_uint64 framesJustRead; - - /* Make room if there's not enough. */ - if (totalFrameCount == dataCapInFrames) { - void* pNewPCMFramesOut; - ma_uint64 newDataCapInFrames = dataCapInFrames*2; - if (newDataCapInFrames == 0) { - newDataCapInFrames = 4096; - } - - if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - return MA_TOO_BIG; - } - - pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks); - if (pNewPCMFramesOut == NULL) { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - dataCapInFrames = newDataCapInFrames; - pPCMFramesOut = pNewPCMFramesOut; - } - - frameCountToTryReading = dataCapInFrames - totalFrameCount; - MA_ASSERT(frameCountToTryReading > 0); - - result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead); - totalFrameCount += framesJustRead; - - if (result != MA_SUCCESS) { - break; - } - - if (framesJustRead < frameCountToTryReading) { - break; - } - } - - - if (pConfigOut != NULL) { - pConfigOut->format = pDecoder->outputFormat; - pConfigOut->channels = pDecoder->outputChannels; - pConfigOut->sampleRate = pDecoder->outputSampleRate; - } - - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = pPCMFramesOut; - } else { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - } - - if (pFrameCountOut != NULL) { - *pFrameCountOut = totalFrameCount; - } - - ma_decoder_uninit(pDecoder); - return MA_SUCCESS; -} - -MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_result result; - ma_decoder_config config; - ma_decoder decoder; - - if (pFrameCountOut != NULL) { - *pFrameCountOut = 0; - } - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = NULL; - } - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); - - return result; -} - -MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut); -} - -MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_decoder_config config; - ma_decoder decoder; - ma_result result; - - if (pFrameCountOut != NULL) { - *pFrameCountOut = 0; - } - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = NULL; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder_init_memory(pData, dataSize, &config, &decoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); -} -#endif /* MA_NO_DECODING */ - - -#ifndef MA_NO_ENCODING - -#if defined(MA_HAS_WAV) -static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite) -{ - ma_encoder* pEncoder = (ma_encoder*)pUserData; - size_t bytesWritten = 0; - - MA_ASSERT(pEncoder != NULL); - - pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten); - return bytesWritten; -} - -static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin) -{ - ma_encoder* pEncoder = (ma_encoder*)pUserData; - ma_result result; - - MA_ASSERT(pEncoder != NULL); - - result = pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); - if (result != MA_SUCCESS) { - return DRWAV_FALSE; - } else { - return DRWAV_TRUE; - } -} - -static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) -{ - drwav_data_format wavFormat; - drwav_allocation_callbacks allocationCallbacks; - drwav* pWav; - - MA_ASSERT(pEncoder != NULL); - - pWav = (drwav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - wavFormat.container = drwav_container_riff; - wavFormat.channels = pEncoder->config.channels; - wavFormat.sampleRate = pEncoder->config.sampleRate; - wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; - if (pEncoder->config.format == ma_format_f32) { - wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT; - } else { - wavFormat.format = DR_WAVE_FORMAT_PCM; - } - - allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; - allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc; - allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; - allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; - - if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { - return MA_ERROR; - } - - pEncoder->pInternalEncoder = pWav; - - return MA_SUCCESS; -} - -static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) -{ - drwav* pWav; - - MA_ASSERT(pEncoder != NULL); - - pWav = (drwav*)pEncoder->pInternalEncoder; - MA_ASSERT(pWav != NULL); - - drwav_uninit(pWav); - ma_free(pWav, &pEncoder->config.allocationCallbacks); -} - -static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) -{ - drwav* pWav; - ma_uint64 framesWritten; - - MA_ASSERT(pEncoder != NULL); - - pWav = (drwav*)pEncoder->pInternalEncoder; - MA_ASSERT(pWav != NULL); - - framesWritten = drwav_write_pcm_frames(pWav, frameCount, pFramesIn); - - if (pFramesWritten != NULL) { - *pFramesWritten = framesWritten; - } - - return MA_SUCCESS; -} -#endif - -MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_encoder_config config; - - MA_ZERO_OBJECT(&config); - config.encodingFormat = encodingFormat; - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - - return config; -} - -MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - - if (pEncoder == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEncoder); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) { - return MA_INVALID_ARGS; - } - - pEncoder->config = *pConfig; - - result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder) -{ - ma_result result = MA_SUCCESS; - - /* This assumes ma_encoder_preinit() has been called prior. */ - MA_ASSERT(pEncoder != NULL); - - if (onWrite == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; - } - - pEncoder->onWrite = onWrite; - pEncoder->onSeek = onSeek; - pEncoder->pUserData = pUserData; - - switch (pEncoder->config.encodingFormat) - { - case ma_encoding_format_wav: - { - #if defined(MA_HAS_WAV) - pEncoder->onInit = ma_encoder__on_init_wav; - pEncoder->onUninit = ma_encoder__on_uninit_wav; - pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav; - #else - result = MA_NO_BACKEND; - #endif - } break; - - default: - { - result = MA_INVALID_ARGS; - } break; - } - - /* Getting here means we should have our backend callbacks set up. */ - if (result == MA_SUCCESS) { - result = pEncoder->onInit(pEncoder); - } - - return result; -} - -static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) -{ - return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten); -} - -static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) -{ - return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin); -} - -MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); - if (result != MA_SUCCESS) { - return result; - } - - pEncoder->data.vfs.pVFS = pVFS; - pEncoder->data.vfs.file = file; - - result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); - if (result != MA_SUCCESS) { - return result; - } - - pEncoder->data.vfs.pVFS = pVFS; - pEncoder->data.vfs.file = file; - - result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder); -} - -MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder); -} - -MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder); -} - - -MA_API void ma_encoder_uninit(ma_encoder* pEncoder) -{ - if (pEncoder == NULL) { - return; - } - - if (pEncoder->onUninit) { - pEncoder->onUninit(pEncoder); - } - - /* If we have a file handle, close it. */ - if (pEncoder->onWrite == ma_encoder__on_write_vfs) { - ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file); - pEncoder->data.vfs.file = NULL; - } -} - - -MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) -{ - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - if (pEncoder == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten); -} -#endif /* MA_NO_ENCODING */ - - - -/************************************************************************************************************************************************************** - -Generation - -**************************************************************************************************************************************************************/ -#ifndef MA_NO_GENERATION -MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency) -{ - ma_waveform_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.type = type; - config.amplitude = amplitude; - config.frequency = frequency; - - return config; -} - -static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); -} - -static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_waveform* pWaveform = (ma_waveform*)pDataSource; - - *pFormat = pWaveform->config.format; - *pChannels = pWaveform->config.channels; - *pSampleRate = pWaveform->config.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels); - - return MA_SUCCESS; -} - -static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_waveform* pWaveform = (ma_waveform*)pDataSource; - - *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance); - - return MA_SUCCESS; -} - -static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency) -{ - return (1.0 / (sampleRate / frequency)); -} - -static void ma_waveform__update_advance(ma_waveform* pWaveform) -{ - pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); -} - -static ma_data_source_vtable g_ma_waveform_data_source_vtable = -{ - ma_waveform__data_source_on_read, - ma_waveform__data_source_on_seek, - ma_waveform__data_source_on_get_data_format, - ma_waveform__data_source_on_get_cursor, - NULL, /* onGetLength. There's no notion of a length in waveforms. */ - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWaveform); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds); - if (result != MA_SUCCESS) { - return result; - } - - pWaveform->config = *pConfig; - pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); - pWaveform->time = 0; - - return MA_SUCCESS; -} - -MA_API void ma_waveform_uninit(ma_waveform* pWaveform) -{ - if (pWaveform == NULL) { - return; - } - - ma_data_source_uninit(&pWaveform->ds); -} - -MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.amplitude = amplitude; - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.frequency = frequency; - ma_waveform__update_advance(pWaveform); - - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.type = type; - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.sampleRate = sampleRate; - ma_waveform__update_advance(pWaveform); - - return MA_SUCCESS; -} - -static float ma_waveform_sine_f32(double time, double amplitude) -{ - return (float)(ma_sind(MA_TAU_D * time) * amplitude); -} - -static ma_int16 ma_waveform_sine_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); -} - -static float ma_waveform_square_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - if (f < 0.5) { - r = amplitude; - } else { - r = -amplitude; - } - - return (float)r; -} - -static ma_int16 ma_waveform_square_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, amplitude)); -} - -static float ma_waveform_triangle_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - r = 2 * ma_abs(2 * (f - 0.5)) - 1; - - return (float)(r * amplitude); -} - -static ma_int16 ma_waveform_triangle_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude)); -} - -static float ma_waveform_sawtooth_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - r = 2 * (f - 0.5); - - return (float)(r * amplitude); -} - -static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude)); -} - -static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut != NULL) { - switch (pWaveform->config.type) - { - case ma_waveform_type_sine: - { - ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_square: - { - ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_triangle: - { - ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_sawtooth: - { - ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount); - } break; - - default: return MA_INVALID_OPERATION; /* Unknown waveform type. */ - } - } else { - pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */ - - return MA_SUCCESS; -} - - -MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) -{ - ma_noise_config config; - MA_ZERO_OBJECT(&config); - - config.format = format; - config.channels = channels; - config.type = type; - config.seed = seed; - config.amplitude = amplitude; - - if (config.seed == 0) { - config.seed = MA_DEFAULT_LCG_SEED; - } - - return config; -} - - -static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - /* No-op. Just pretend to be successful. */ - (void)pDataSource; - (void)frameIndex; - return MA_SUCCESS; -} - -static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_noise* pNoise = (ma_noise*)pDataSource; - - *pFormat = pNoise->config.format; - *pChannels = pNoise->config.channels; - *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_noise_data_source_vtable = -{ - ma_noise__data_source_on_read, - ma_noise__data_source_on_seek, /* No-op for noise. */ - ma_noise__data_source_on_get_data_format, - NULL, /* onGetCursor. No notion of a cursor for noise. */ - NULL, /* onGetLength. No notion of a length for noise. */ - NULL, /* onSetLooping */ - 0 -}; - - -#ifndef MA_PINK_NOISE_BIN_SIZE -#define MA_PINK_NOISE_BIN_SIZE 16 -#endif - -typedef struct -{ - size_t sizeInBytes; - struct - { - size_t binOffset; - size_t accumulationOffset; - size_t counterOffset; - } pink; - struct - { - size_t accumulationOffset; - } brownian; -} ma_noise_heap_layout; - -static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Pink. */ - if (pConfig->type == ma_noise_type_pink) { - /* bin */ - pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels; - pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE; - - /* accumulation */ - pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; - - /* counter */ - pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels; - } - - /* Brownian. */ - if (pConfig->type == ma_noise_type_brownian) { - /* accumulation */ - pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_noise_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_noise_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise) -{ - ma_result result; - ma_noise_heap_layout heapLayout; - ma_data_source_config dataSourceConfig; - ma_uint32 iChannel; - - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNoise); - - result = ma_noise_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pNoise->_pHeap = pHeap; - MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_noise_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pNoise->ds); - if (result != MA_SUCCESS) { - return result; - } - - pNoise->config = *pConfig; - ma_lcg_seed(&pNoise->lcg, pConfig->seed); - - if (pNoise->config.type == ma_noise_type_pink) { - pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset); - pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset); - pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset); - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel)); - pNoise->state.pink.accumulation[iChannel] = 0; - pNoise->state.pink.counter[iChannel] = 1; - } - } - - if (pNoise->config.type == ma_noise_type_brownian) { - pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset); - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pNoise->state.brownian.accumulation[iChannel] = 0; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_noise_init_preallocated(pConfig, pHeap, pNoise); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pNoise->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pNoise == NULL) { - return; - } - - ma_data_source_uninit(&pNoise->ds); - - if (pNoise->_ownsHeap) { - ma_free(pNoise->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->config.amplitude = amplitude; - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->lcg.state = seed; - return MA_SUCCESS; -} - - -MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->config.type = type; - return MA_SUCCESS; -} - -static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) -{ - return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_white(pNoise); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - - -static MA_INLINE unsigned int ma_tzcnt32(unsigned int x) -{ - unsigned int n; - - /* Special case for odd numbers since they should happen about half the time. */ - if (x & 0x1) { - return 0; - } - - if (x == 0) { - return sizeof(x) << 3; - } - - n = 1; - if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; } - if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; } - if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; } - if ((x & 0x00000003) == 0) { x >>= 2; n += 2; } - n -= x & 0x00000001; - - return n; -} - -/* -Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h - -This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/ -*/ -static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel) -{ - double result; - double binPrev; - double binNext; - unsigned int ibin; - - ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1); - - binPrev = pNoise->state.pink.bin[iChannel][ibin]; - binNext = ma_lcg_rand_f64(&pNoise->lcg); - pNoise->state.pink.bin[iChannel][ibin] = binNext; - - pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev); - pNoise->state.pink.counter[iChannel] += 1; - - result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]); - result /= 10; - - return (float)(result * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_pink(pNoise, iChannel); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - - -static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel) -{ - double result; - - result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]); - result /= 1.005; /* Don't escape the -1..1 range on average. */ - - pNoise->state.brownian.accumulation[iChannel] = result; - result /= 20; - - return (float)(result * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_brownian(pNoise, iChannel); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - -MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_uint64 framesRead = 0; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */ - if (pFramesOut == NULL) { - framesRead = frameCount; - } else { - switch (pNoise->config.type) { - case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break; - case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break; - case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break; - default: return MA_INVALID_OPERATION; /* Unknown noise type. */ - } - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - return MA_SUCCESS; -} -#endif /* MA_NO_GENERATION */ - - - -#ifndef MA_NO_RESOURCE_MANAGER -#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS -#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000 -#endif - -#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY -#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024 -#endif - -MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void) -{ - ma_resource_manager_pipeline_notifications notifications; - - MA_ZERO_OBJECT(¬ifications); - - return notifications; -} - -static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); } - if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); } -} - -static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); } - if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); } -} - -static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); } - if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); } -} - - - -#ifndef MA_DEFAULT_HASH_SEED -#define MA_DEFAULT_HASH_SEED 42 -#endif - -/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */ -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #if __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #endif -#endif - -static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) -{ - return (x << r) | (x >> (32 - r)); -} - -static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) -{ - if (ma_is_little_endian()) { - return blocks[i]; - } else { - return ma_swap_endian_uint32(blocks[i]); - } -} - -static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h) -{ - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - return h; -} - -static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed) -{ - const ma_uint8* data = (const ma_uint8*)key; - const ma_uint32* blocks; - const ma_uint8* tail; - const int nblocks = len / 4; - ma_uint32 h1 = seed; - ma_uint32 c1 = 0xcc9e2d51; - ma_uint32 c2 = 0x1b873593; - ma_uint32 k1; - int i; - - blocks = (const ma_uint32 *)(data + nblocks*4); - - for(i = -nblocks; i; i++) { - k1 = ma_hash_getblock(blocks,i); - - k1 *= c1; - k1 = ma_rotl32(k1, 15); - k1 *= c2; - - h1 ^= k1; - h1 = ma_rotl32(h1, 13); - h1 = h1*5 + 0xe6546b64; - } - - - tail = (const ma_uint8*)(data + nblocks*4); - - k1 = 0; - switch(len & 3) { - case 3: k1 ^= tail[2] << 16; - case 2: k1 ^= tail[1] << 8; - case 1: k1 ^= tail[0]; - k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; - }; - - - h1 ^= len; - h1 = ma_hash_fmix32(h1); - - return h1; -} - -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push -#endif -/* End MurmurHash3 */ - -static ma_uint32 ma_hash_string_32(const char* str) -{ - return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED); -} - -static ma_uint32 ma_hash_string_w_32(const wchar_t* str) -{ - return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); -} - - - - -/* -Basic BST Functions -*/ -static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(ppDataBufferNode != NULL); - - pCurrentNode = pResourceManager->pRootDataBufferNode; - while (pCurrentNode != NULL) { - if (hashedName32 == pCurrentNode->hashedName32) { - break; /* Found. */ - } else if (hashedName32 < pCurrentNode->hashedName32) { - pCurrentNode = pCurrentNode->pChildLo; - } else { - pCurrentNode = pCurrentNode->pChildHi; - } - } - - *ppDataBufferNode = pCurrentNode; - - if (pCurrentNode == NULL) { - return MA_DOES_NOT_EXIST; - } else { - return MA_SUCCESS; - } -} - -static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(ppInsertPoint != NULL); - - *ppInsertPoint = NULL; - - if (pResourceManager->pRootDataBufferNode == NULL) { - return MA_SUCCESS; /* No items. */ - } - - /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */ - pCurrentNode = pResourceManager->pRootDataBufferNode; - while (pCurrentNode != NULL) { - if (hashedName32 == pCurrentNode->hashedName32) { - result = MA_ALREADY_EXISTS; - break; - } else { - if (hashedName32 < pCurrentNode->hashedName32) { - if (pCurrentNode->pChildLo == NULL) { - result = MA_SUCCESS; - break; - } else { - pCurrentNode = pCurrentNode->pChildLo; - } - } else { - if (pCurrentNode->pChildHi == NULL) { - result = MA_SUCCESS; - break; - } else { - pCurrentNode = pCurrentNode->pChildHi; - } - } - } - } - - *ppInsertPoint = pCurrentNode; - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - /* The key must have been set before calling this function. */ - MA_ASSERT(pDataBufferNode->hashedName32 != 0); - - if (pInsertPoint == NULL) { - /* It's the first node. */ - pResourceManager->pRootDataBufferNode = pDataBufferNode; - } else { - /* It's not the first node. It needs to be inserted. */ - if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) { - MA_ASSERT(pInsertPoint->pChildLo == NULL); - pInsertPoint->pChildLo = pDataBufferNode; - } else { - MA_ASSERT(pInsertPoint->pChildHi == NULL); - pInsertPoint->pChildHi = pDataBufferNode; - } - } - - pDataBufferNode->pParent = pInsertPoint; - - return MA_SUCCESS; -} - -#if 0 /* Unused for now. */ -static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_result result; - ma_resource_manager_data_buffer_node* pInsertPoint; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint); - if (result != MA_SUCCESS) { - return MA_INVALID_ARGS; - } - - return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); -} -#endif - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pDataBufferNode != NULL); - - pCurrentNode = pDataBufferNode; - while (pCurrentNode->pChildLo != NULL) { - pCurrentNode = pCurrentNode->pChildLo; - } - - return pCurrentNode; -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pDataBufferNode != NULL); - - pCurrentNode = pDataBufferNode; - while (pCurrentNode->pChildHi != NULL) { - pCurrentNode = pCurrentNode->pChildHi; - } - - return pCurrentNode; -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->pChildHi != NULL); - - return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->pChildLo != NULL); - - return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); -} - -static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - if (pDataBufferNode->pChildLo == NULL) { - if (pDataBufferNode->pChildHi == NULL) { - /* Simple case - deleting a buffer with no children. */ - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */ - pResourceManager->pRootDataBufferNode = NULL; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = NULL; - } else { - pDataBufferNode->pParent->pChildHi = NULL; - } - } - } else { - /* Node has one child - pChildHi != NULL. */ - pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent; - - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); - pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi; - } else { - pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi; - } - } - } - } else { - if (pDataBufferNode->pChildHi == NULL) { - /* Node has one child - pChildLo != NULL. */ - pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent; - - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); - pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo; - } else { - pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo; - } - } - } else { - /* Complex case - deleting a node with two children. */ - ma_resource_manager_data_buffer_node* pReplacementDataBufferNode; - - /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */ - pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode); - MA_ASSERT(pReplacementDataBufferNode != NULL); - - /* - Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement - node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The - replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the - replacement node and reinserting it into the same position as the deleted node. - */ - MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */ - MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */ - - if (pReplacementDataBufferNode->pChildHi == NULL) { - if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { - pReplacementDataBufferNode->pParent->pChildLo = NULL; - } else { - pReplacementDataBufferNode->pParent->pChildHi = NULL; - } - } else { - pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent; - if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { - pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi; - } else { - pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi; - } - } - - - /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */ - if (pDataBufferNode->pParent != NULL) { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode; - } else { - pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode; - } - } - - /* Now need to update the replacement node's pointers. */ - pReplacementDataBufferNode->pParent = pDataBufferNode->pParent; - pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo; - pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi; - - /* Now the children of the replacement node need to have their parent pointers updated. */ - if (pReplacementDataBufferNode->pChildLo != NULL) { - pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode; - } - if (pReplacementDataBufferNode->pChildHi != NULL) { - pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode; - } - - /* Now the root node needs to be updated. */ - if (pResourceManager->pRootDataBufferNode == pDataBufferNode) { - pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode; - } - } - } - - return MA_SUCCESS; -} - -#if 0 /* Unused for now. */ -static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32) -{ - ma_result result; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode); - if (result != MA_SUCCESS) { - return result; /* Could not find the data buffer. */ - } - - return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode); -} -#endif - -static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - return (ma_resource_manager_data_supply_type)c89atomic_load_i32(&pDataBufferNode->data.type); -} - -static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) -{ - c89atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); -} - -static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) -{ - ma_uint32 refCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - (void)pResourceManager; - - refCount = c89atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; - - if (pNewRefCount != NULL) { - *pNewRefCount = refCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) -{ - ma_uint32 refCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - (void)pResourceManager; - - refCount = c89atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; - - if (pNewRefCount != NULL) { - *pNewRefCount = refCount; - } - - return MA_SUCCESS; -} - -static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - if (pDataBufferNode->isDataOwnedByResourceManager) { - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) { - ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks); - pDataBufferNode->data.backend.encoded.pData = NULL; - pDataBufferNode->data.backend.encoded.sizeInBytes = 0; - } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) { - ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks); - pDataBufferNode->data.backend.decoded.pData = NULL; - pDataBufferNode->data.backend.decoded.totalFrameCount = 0; - } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) { - ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks); - } else { - /* Should never hit this if the node was successfully initialized. */ - MA_ASSERT(pDataBufferNode->result != MA_SUCCESS); - } - } - - /* The data buffer itself needs to be freed. */ - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); -} - -static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - - return (ma_result)c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ -} - - -static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0; -} - - -typedef struct -{ - union - { - ma_async_notification_event e; - ma_async_notification_poll p; - } backend; /* Must be the first member. */ - ma_resource_manager* pResourceManager; -} ma_resource_manager_inline_notification; - -static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pNotification != NULL); - - pNotification->pResourceManager = pResourceManager; - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - return ma_async_notification_event_init(&pNotification->backend.e); - } else { - return ma_async_notification_poll_init(&pNotification->backend.p); - } -} - -static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pNotification != NULL); - - if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { - ma_async_notification_event_uninit(&pNotification->backend.e); - } else { - /* No need to uninitialize a polling notification. */ - } -} - -static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pNotification != NULL); - - if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { - ma_async_notification_event_wait(&pNotification->backend.e); - } else { - while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { - ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { - break; - } - } - } -} - -static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification) -{ - ma_resource_manager_inline_notification_wait(pNotification); - ma_resource_manager_inline_notification_uninit(pNotification); -} - - -static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_lock(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } else { - /* Threading not enabled. Do nothing. */ - } -} - -static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_unlock(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } else { - /* Threading not enabled. Do nothing. */ - } -} - -#ifndef MA_NO_THREADING -static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData) -{ - ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; - MA_ASSERT(pResourceManager != NULL); - - for (;;) { - ma_result result; - ma_job job; - - result = ma_resource_manager_next_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - break; - } - - /* Terminate if we got a quit message. */ - if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { - break; - } - - ma_job_process(&job); - } - - return (ma_thread_result)0; -} -#endif - -MA_API ma_resource_manager_config ma_resource_manager_config_init(void) -{ - ma_resource_manager_config config; - - MA_ZERO_OBJECT(&config); - config.decodedFormat = ma_format_unknown; - config.decodedChannels = 0; - config.decodedSampleRate = 0; - config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */ - config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY; - - /* Flags. */ - config.flags = 0; - #ifdef MA_NO_THREADING - { - /* Threading is disabled at compile time so disable threading at runtime as well by default. */ - config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - config.jobThreadCount = 0; - } - #endif - - return config; -} - - -MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager) -{ - ma_result result; - ma_job_queue_config jobQueueConfig; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResourceManager); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) { - return MA_INVALID_ARGS; /* Requesting too many job threads. */ - } - } - #endif - - pResourceManager->config = *pConfig; - ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks); - - /* Get the log set up early so we can start using it as soon as possible. */ - if (pResourceManager->config.pLog == NULL) { - result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log); - if (result == MA_SUCCESS) { - pResourceManager->config.pLog = &pResourceManager->log; - } else { - pResourceManager->config.pLog = NULL; /* Logging is unavailable. */ - } - } - - if (pResourceManager->config.pVFS == NULL) { - result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the default file system. */ - } - - pResourceManager->config.pVFS = &pResourceManager->defaultVFS; - } - - /* If threading has been disabled at compile time, enfore it at run time as well. */ - #ifdef MA_NO_THREADING - { - pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - } - #endif - - /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */ - if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { - pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; - - /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */ - if (pResourceManager->config.jobThreadCount > 0) { - return MA_INVALID_ARGS; - } - } - - /* Job queue. */ - jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity; - jobQueueConfig.flags = 0; - if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) { - if (pResourceManager->config.jobThreadCount > 0) { - return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */ - } - - jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING; - } - - result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue); - if (result != MA_SUCCESS) { - return result; - } - - - /* Custom decoding backends. */ - if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { - size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; - - pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); - - pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; - pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; - } - - - - /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_uint32 iJobThread; - - /* Data buffer lock. */ - result = ma_mutex_init(&pResourceManager->dataBufferBSTLock); - if (result != MA_SUCCESS) { - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* Create the job threads last to ensure the threads has access to valid data. */ - for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, 0, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return result; - } - } - } - #else - { - /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */ - MA_ASSERT(MA_FALSE); - } - #endif - } - - return MA_SUCCESS; -} - - -static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager); - - /* If everything was done properly, there shouldn't be any active data buffers. */ - while (pResourceManager->pRootDataBufferNode != NULL) { - ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - - /* The data buffer has been removed from the BST, so now we need to free it's data. */ - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - } -} - -MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) -{ - if (pResourceManager == NULL) { - return; - } - - /* - Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the - queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it. - */ - ma_resource_manager_post_job_quit(pResourceManager); - - /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_uint32 iJobThread; - - for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - ma_thread_wait(&pResourceManager->jobThreads[iJobThread]); - } - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } - - /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */ - ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager); - - /* The job queue is no longer needed. */ - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - - /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } - - ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); - - if (pResourceManager->config.pLog == &pResourceManager->log) { - ma_log_uninit(&pResourceManager->log); - } -} - -MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager) -{ - if (pResourceManager == NULL) { - return NULL; - } - - return pResourceManager->config.pLog; -} - - - -MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void) -{ - ma_resource_manager_data_source_config config; - - MA_ZERO_OBJECT(&config); - config.rangeEndInPCMFrames = ~((ma_uint64)0); - config.loopPointEndInPCMFrames = ~((ma_uint64)0); - - return config; -} - - -static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager) -{ - ma_decoder_config config; - - config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate); - config.allocationCallbacks = pResourceManager->config.allocationCallbacks; - config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables; - config.customBackendCount = pResourceManager->config.customDecodingBackendCount; - config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData; - - return config; -} - -static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - MA_ASSERT(pDecoder != NULL); - - config = ma_resource_manager__init_decoder_config(pResourceManager); - - if (pFilePath != NULL) { - result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); - return result; - } - } else { - result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder); - if (result != MA_SUCCESS) { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); - #endif - return result; - } - } - - return MA_SUCCESS; -} - -static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) -{ - switch (pDataBuffer->pNode->data.type) - { - case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; - case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer; - case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n"); - return NULL; - }; - }; -} - -static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence) -{ - ma_result result; - - MA_ASSERT(pDataBuffer != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDataBuffer->isConnectorInitialized == MA_FALSE); - - /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ - result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); - if (result != MA_SUCCESS && result != MA_BUSY) { - return result; /* The data buffer is in an erroneous state. */ - } - - /* - We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the - "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use - an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead. - */ - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ - { - ma_decoder_config config; - config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager); - result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder); - } break; - - case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ - { - ma_audio_buffer_config config; - config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL); - result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer); - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ - { - ma_paged_audio_buffer_config config; - config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data); - result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer); - } break; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown data supply type. Should never happen. Need to post an error here. */ - return MA_INVALID_ARGS; - }; - } - - /* - Initialization of the connector is when we can fire the init notification. This will give the application access to - the format/channels/rate of the data source. - */ - if (result == MA_SUCCESS) { - /* - Make sure the looping state is set before returning in order to handle the case where the - loop state was set on the data buffer before the connector was initialized. - */ - ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); - - pDataBuffer->isConnectorInitialized = MA_TRUE; - - if (pInitNotification != NULL) { - ma_async_notification_signal(pInitNotification); - } - - if (pInitFence != NULL) { - ma_fence_release(pInitFence); - } - } - - /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ - return result; -} - -static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBuffer != NULL); - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ - { - ma_decoder_uninit(&pDataBuffer->connector.decoder); - } break; - - case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ - { - ma_audio_buffer_uninit(&pDataBuffer->connector.buffer); - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ - { - ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer); - } break; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown data supply type. Should never happen. Need to post an error here. */ - return MA_INVALID_ARGS; - }; - } - - return MA_SUCCESS; -} - -static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - return c89atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); -} - -static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) -{ - ma_result result; - size_t dataSizeInBytes; - void* pData; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - - result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - if (pFilePath != NULL) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); - } else { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); - #endif - } - - return result; - } - - pDataBufferNode->data.backend.encoded.pData = pData; - pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */ - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder) -{ - ma_result result = MA_SUCCESS; - ma_decoder* pDecoder; - ma_uint64 totalFrameCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(ppDecoder != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - - *ppDecoder = NULL; /* For safety. */ - - pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks); - if (pDecoder == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder); - if (result != MA_SUCCESS) { - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* - At this point we have the decoder and we now need to initialize the data supply. This will - be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap - allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter - is used when the length of a sound is unknown until a full decode has been performed. - */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { - result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); - if (result != MA_SUCCESS) { - return result; - } - } else { - totalFrameCount = 0; - } - - if (totalFrameCount > 0) { - /* It's a known length. The data supply is a regular decoded buffer. */ - ma_uint64 dataSizeInBytes; - void* pData; - - dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); - if (dataSizeInBytes > MA_SIZE_MAX) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pData == NULL) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - /* The buffer needs to be initialized to silence in case the caller reads from it. */ - ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels); - - /* Data has been allocated and the data supply can now be initialized. */ - pDataBufferNode->data.backend.decoded.pData = pData; - pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount; - pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat; - pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels; - pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate; - pDataBufferNode->data.backend.decoded.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */ - } else { - /* - It's an unknown length. The data supply is a paged decoded buffer. Setting this up is - actually easier than the non-paged decoded buffer because we just need to initialize - a ma_paged_audio_buffer object. - */ - result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data); - if (result != MA_SUCCESS) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return result; - } - - pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate; - pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */ - } - - *ppDecoder = pDecoder; - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder) -{ - ma_result result = MA_SUCCESS; - ma_uint64 pageSizeInFrames; - ma_uint64 framesToTryReading; - ma_uint64 framesRead; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDecoder != NULL); - - /* We need to know the size of a page in frames to know how many frames to decode. */ - pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000); - framesToTryReading = pageSizeInFrames; - - /* - Here is where we do the decoding of the next page. We'll run a slightly different path depending - on whether or not we're using a flat or paged buffer because the allocation of the page differs - between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged - buffer, we need to allocate a new page and attach it to the linked list. - */ - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)) - { - case ma_resource_manager_data_supply_type_decoded: - { - /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */ - void* pDst; - ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount; - if (framesToTryReading > framesRemaining) { - framesToTryReading = framesRemaining; - } - - if (framesToTryReading > 0) { - pDst = ma_offset_ptr( - pDataBufferNode->data.backend.decoded.pData, - pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels) - ); - MA_ASSERT(pDst != NULL); - - result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead); - if (framesRead > 0) { - pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead; - } - } else { - framesRead = 0; - } - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - /* The destination buffer is a freshly allocated page. */ - ma_paged_audio_buffer_page* pPage; - - result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); - if (framesRead > 0) { - pPage->sizeInFrames = framesRead; - - result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); - if (result == MA_SUCCESS) { - pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead; - } else { - /* Failed to append the page. Just abort and set the status to MA_AT_END. */ - ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); - result = MA_AT_END; - } - } else { - /* No frames were read. Free the page and just set the status to MA_AT_END. */ - ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); - result = MA_AT_END; - } - } break; - - case ma_resource_manager_data_supply_type_encoded: - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unexpected data supply type. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)); - return MA_ERROR; - }; - } - - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; - ma_resource_manager_data_buffer_node* pInsertPoint; - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = NULL; - } - - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); - if (result == MA_ALREADY_EXISTS) { - /* The node already exists. We just need to increment the reference count. */ - pDataBufferNode = pInsertPoint; - - result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL); - if (result != MA_SUCCESS) { - return result; /* Should never happen. Failed to increment the reference count. */ - } - - result = MA_ALREADY_EXISTS; - goto done; - } else { - /* - The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This - needs to be done inside the critical section to ensure an uninitialization of the node - does not occur before initialization on another thread. - */ - pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks); - if (pDataBufferNode == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_OBJECT(pDataBufferNode); - pDataBufferNode->hashedName32 = hashedName32; - pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ - - if (pExistingData == NULL) { - pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ - pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */ - pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE; - } else { - pDataBufferNode->data = *pExistingData; - pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */ - pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE; - } - - result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); - if (result != MA_SUCCESS) { - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - return result; /* Should never happen. Failed to insert the data buffer into the BST. */ - } - - /* - Here is where we'll post the job, but only if we're loading asynchronously. If we're - loading synchronously we'll defer loading to a later stage, outside of the critical - section. - */ - if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { - /* Loading asynchronously. Post the job. */ - ma_job job; - char* pFilePathCopy = NULL; - wchar_t* pFilePathWCopy = NULL; - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks); - } else { - pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); - } - - /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */ - if (pInitFence != NULL) { ma_fence_acquire(pInitFence); } - if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); } - - /* We now have everything we need to post the job to the job thread. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager; - job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode; - job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy; - job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy; - job.data.resourceManager.loadDataBufferNode.flags = flags; - job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL; - job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL; - job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; - job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; - - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - /* Failed to post job. Probably ran out of memory. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - - /* - Fences were acquired before posting the job, but since the job was not able to - be posted, we need to make sure we release them so nothing gets stuck waiting. - */ - if (pInitFence != NULL) { ma_fence_release(pInitFence); } - if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); - } - - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - - return result; - } - } - } - -done: - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = pDataBufferNode; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_result result = MA_SUCCESS; - ma_bool32 nodeAlreadyExists = MA_FALSE; - ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; - ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = NULL; /* Safety. */ - } - - if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) { - return MA_INVALID_ARGS; - } - - /* If we're specifying existing data, it must be valid. */ - if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) { - return MA_INVALID_ARGS; - } - - /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; - } - - if (hashedName32 == 0) { - if (pFilePath != NULL) { - hashedName32 = ma_hash_string_32(pFilePath); - } else { - hashedName32 = ma_hash_string_w_32(pFilePathW); - } - } - - /* - Here is where we either increment the node's reference count or allocate a new one and add it - to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is - posted inside the critical section just in case the caller immediately uninitializes the node - as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the - node is not uninitialized before initialization. - */ - ma_resource_manager_data_buffer_bst_lock(pResourceManager); - { - result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode); - } - ma_resource_manager_data_buffer_bst_unlock(pResourceManager); - - if (result == MA_ALREADY_EXISTS) { - nodeAlreadyExists = MA_TRUE; - result = MA_SUCCESS; - } else { - if (result != MA_SUCCESS) { - return result; - } - } - - /* - If we're loading synchronously, we'll need to load everything now. When loading asynchronously, - a job will have been posted inside the BST critical section so that an uninitialization can be - allocated an appropriate execution order thereby preventing it from being uninitialized before - the node is initialized by the decoding thread(s). - */ - if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */ - if (pFilePath == NULL && pFilePathW == NULL) { - /* - If this path is hit, it means a buffer is being copied (i.e. initialized from only the - hashed name), but that node has been freed in the meantime, probably from some other - thread. This is an invalid operation. - */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n"); - result = MA_INVALID_OPERATION; - goto done; - } - - if (pDataBufferNode->isDataOwnedByResourceManager) { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) { - /* Loading synchronously. Load the sound in it's entirety here. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) { - /* No decoding. This is the simple case - just store the file contents in memory. */ - result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW); - if (result != MA_SUCCESS) { - goto done; - } - } else { - /* Decoding. We do this the same way as we do when loading asynchronously. */ - ma_decoder* pDecoder; - result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder); - if (result != MA_SUCCESS) { - goto done; - } - - /* We have the decoder, now decode page by page just like we do when loading asynchronously. */ - for (;;) { - /* Decode next page. */ - result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder); - if (result != MA_SUCCESS) { - break; /* Will return MA_AT_END when the last page has been decoded. */ - } - } - - /* Reaching the end needs to be considered successful. */ - if (result == MA_AT_END) { - result = MA_SUCCESS; - } - - /* - At this point the data buffer is either fully decoded or some error occurred. Either - way, the decoder is no longer necessary. - */ - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - } - - /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ - c89atomic_exchange_i32(&pDataBufferNode->result, result); - } else { - /* Loading asynchronously. We may need to wait for initialization. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_wait(&initNotification); - } - } - } else { - /* The data is not managed by the resource manager so there's nothing else to do. */ - MA_ASSERT(pExistingData != NULL); - } - } - -done: - /* If we failed to initialize the data buffer we need to free it. */ - if (result != MA_SUCCESS) { - if (nodeAlreadyExists == MA_FALSE) { - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - } - } - - /* - The init notification needs to be uninitialized. This will be used if the node does not already - exist, and we've specified ASYNC | WAIT_INIT. - */ - if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = pDataBufferNode; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW) -{ - ma_result result = MA_SUCCESS; - ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */ - ma_uint32 hashedName32 = 0; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - if (pDataBufferNode == NULL) { - if (pName == NULL && pNameW == NULL) { - return MA_INVALID_ARGS; - } - - if (pName != NULL) { - hashedName32 = ma_hash_string_32(pName); - } else { - hashedName32 = ma_hash_string_w_32(pNameW); - } - } - - /* - The first thing to do is decrement the reference counter of the node. Then, if the reference - count is zero, we need to free the node. If the node is still in the process of loading, we'll - need to post a job to the job queue to free the node. Otherwise we'll just do it here. - */ - ma_resource_manager_data_buffer_bst_lock(pResourceManager); - { - /* Might need to find the node. Must be done inside the critical section. */ - if (pDataBufferNode == NULL) { - result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode); - if (result != MA_SUCCESS) { - goto stage2; /* Couldn't find the node. */ - } - } - - result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount); - if (result != MA_SUCCESS) { - goto stage2; /* Should never happen. */ - } - - if (refCount == 0) { - result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - if (result != MA_SUCCESS) { - goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */ - } - } - } - ma_resource_manager_data_buffer_bst_unlock(pResourceManager); - -stage2: - if (result != MA_SUCCESS) { - return result; - } - - /* - Here is where we need to free the node. We don't want to do this inside the critical section - above because we want to keep that as small as possible for multi-threaded efficiency. - */ - if (refCount == 0) { - if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { - /* The sound is still loading. We need to delay the freeing of the node to a safe time. */ - ma_job job; - - /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ - c89atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager; - job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode; - - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - return result; - } - - /* If we don't support threading, process the job queue here. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { - result = ma_resource_manager_process_next_job(pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { - result = MA_SUCCESS; - break; - } - } - } else { - /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */ - } - } else { - /* The sound isn't loading so we can just free the node here. */ - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - } - } - - return result; -} - - - -static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pDataBuffer != NULL); - return c89atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); -} - -static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength); -} - -static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; - MA_ASSERT(pDataBuffer != NULL); - - c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping); - - /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ - ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = -{ - ma_resource_manager_data_buffer_cb__read_pcm_frames, - ma_resource_manager_data_buffer_cb__seek_to_pcm_frame, - ma_resource_manager_data_buffer_cb__get_data_format, - ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames, - ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames, - ma_resource_manager_data_buffer_cb__set_looping, - 0 -}; - -static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pDataBufferNode; - ma_data_source_config dataSourceConfig; - ma_bool32 async; - ma_uint32 flags; - ma_resource_manager_pipeline_notifications notifications; - - if (pDataBuffer == NULL) { - if (pConfig != NULL && pConfig->pNotifications != NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); - } - - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataBuffer); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pNotifications != NULL) { - notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */ - } else { - MA_ZERO_OBJECT(¬ifications); - } - - /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */ - flags = pConfig->flags; - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; - } - - async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; - - /* - Fences need to be acquired before doing anything. These must be aquired and released outside of - the node to ensure there's no holes where ma_fence_wait() could prematurely return before the - data buffer has completed initialization. - - When loading asynchronously, the node acquisition routine below will acquire the fences on this - thread and then release them on the async thread when the operation is complete. - - These fences are always released at the "done" tag at the end of this function. They'll be - acquired a second if loading asynchronously. This double acquisition system is just done to - simplify code maintanence. - */ - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - { - /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ - result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds); - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } - - pDataBuffer->pResourceManager = pResourceManager; - pDataBuffer->pNode = pDataBufferNode; - pDataBuffer->flags = flags; - pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */ - - /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ - if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { - /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); - c89atomic_exchange_i32(&pDataBuffer->result, result); - - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } else { - /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */ - ma_job job; - ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); - } - - /* - The status of the data buffer needs to be set to MA_BUSY before posting the job so that the - worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other - than MA_BUSY, it'll assume an error and fall through to an early exit. - */ - c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); - - /* Acquire fences a second time. These will be released by the async thread. */ - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); - job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer; - job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification; - job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification; - job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence; - job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence; - job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; - job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; - job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping; - - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); - c89atomic_exchange_i32(&pDataBuffer->result, result); - - /* Release the fences after the result has been set on the data buffer. */ - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - } else { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_wait(&initNotification); - - if (notifications.init.pNotification != NULL) { - ma_async_notification_signal(notifications.init.pNotification); - } - - /* NOTE: Do not release the init fence here. It will have been done by the job. */ - - /* Make sure we return an error if initialization failed on the async thread. */ - result = ma_resource_manager_data_buffer_result(pDataBuffer); - if (result == MA_BUSY) { - result = MA_SUCCESS; - } - } - } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } - - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); - goto done; - } - } -done: - if (result == MA_SUCCESS) { - if (pConfig->initialSeekPointInPCMFrames > 0) { - ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames); - } - } - - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - if (pExistingDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */ - - config = ma_resource_manager_data_source_config_init(); - config.flags = pExistingDataBuffer->flags; - - return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer); -} - -static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pDataBuffer != NULL); - - /* The connector should be uninitialized first. */ - ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer); - - /* With the connector uninitialized we can unacquire the node. */ - ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL); - - /* The base data source needs to be uninitialized as well. */ - ma_data_source_uninit(&pDataBuffer->ds); - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_result result; - - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) { - /* The data buffer can be deleted synchronously. */ - return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); - } else { - /* - The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will - be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event - to get processed before returning. - */ - ma_resource_manager_inline_notification notification; - ma_job job; - - /* - We need to mark the node as unavailable so we don't try reading from it anymore, but also to - let the loading thread know that it needs to abort it's loading procedure. - */ - c89atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); - - result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); - if (result != MA_SUCCESS) { - return result; /* Failed to create the notification. This should rarely, if ever, happen. */ - } - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); - job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer; - job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification; - job.data.resourceManager.freeDataBuffer.pDoneFence = NULL; - - result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_resource_manager_inline_notification_uninit(¬ification); - return result; - } - - ma_resource_manager_inline_notification_wait_and_uninit(¬ification); - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 framesRead = 0; - ma_bool32 isDecodedBufferBusy = MA_FALSE; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - /* - We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after - it's been uninitialized or is in the process of uninitializing. - */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - /* If the node is not initialized we need to abort with a busy code. */ - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - return MA_BUSY; /* Still loading. */ - } - - if (pDataBuffer->seekToCursorOnNextRead) { - pDataBuffer->seekToCursorOnNextRead = MA_FALSE; - - result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - } - - /* - For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot - exceed this amount. We'll read as much as we can, and then return MA_BUSY. - */ - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) { - ma_uint64 availableFrames; - - isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); - - if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { - /* Don't try reading more than the available frame count. */ - if (frameCount > availableFrames) { - frameCount = availableFrames; - - /* - If there's no frames available we want to set the status to MA_AT_END. The logic below - will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this - is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count - is 0 because that'll result in a situation where it's possible MA_AT_END won't get - returned. - */ - if (frameCount == 0) { - result = MA_AT_END; - } - } else { - isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ - } - } - } - - /* Don't attempt to read anything if we've got no frames available. */ - if (frameCount > 0) { - result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead); - } - - /* - If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound - as at the end and terminate decoding. - */ - if (result == MA_AT_END) { - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - result = MA_BUSY; - } - } - - if (isDecodedBufferBusy) { - result = MA_BUSY; - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex) -{ - ma_result result; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - /* If we haven't yet got a connector we need to abort. */ - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - pDataBuffer->seekTargetInPCMFrames = frameIndex; - pDataBuffer->seekToCursorOnNextRead = MA_TRUE; - return MA_BUSY; /* Still loading. */ - } - - result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex); - if (result != MA_SUCCESS) { - return result; - } - - pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */ - pDataBuffer->seekToCursorOnNextRead = MA_FALSE; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - *pFormat = pDataBuffer->pNode->data.backend.decoded.format; - *pChannels = pDataBuffer->pNode->data.backend.decoded.channels; - *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format; - *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels; - *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_unknown: - { - return MA_BUSY; /* Still loading. */ - }; - - default: - { - /* Unknown supply type. Should never hit this. */ - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - if (pDataBuffer == NULL || pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor); - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor); - }; - - case ma_resource_manager_data_supply_type_unknown: - { - return MA_BUSY; - }; - - default: - { - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - if (pDataBuffer == NULL || pLength == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - return MA_BUSY; /* Still loading. */ - } - - return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength); -} - -MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer) -{ - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return (ma_result)c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ -} - -MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) -{ - return ma_data_source_set_looping(pDataBuffer, isLooping); -} - -MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_data_source_is_looping(pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - return MA_BUSY; - } else { - return MA_INVALID_OPERATION; /* No connector. */ - } - } - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames); - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - ma_uint64 cursor; - ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor); - - if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) { - *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor; - } else { - *pAvailableFrames = 0; - } - - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown supply type. Should never hit this. */ - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL); -} - -MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL); -} - - -static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL); -} - -static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_resource_manager_data_supply data; - data.type = ma_resource_manager_data_supply_type_decoded; - data.backend.decoded.pData = pData; - data.backend.decoded.totalFrameCount = frameCount; - data.backend.decoded.format = format; - data.backend.decoded.channels = channels; - data.backend.decoded.sampleRate = sampleRate; - - return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); -} - -MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate); -} - -MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate); -} - - -static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes) -{ - ma_resource_manager_data_supply data; - data.type = ma_resource_manager_data_supply_type_encoded; - data.backend.encoded.pData = pData; - data.backend.encoded.sizeInBytes = sizeInBytes; - - return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); -} - -MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes) -{ - return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes); -} - -MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes) -{ - return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes); -} - - -MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath) -{ - return ma_resource_manager_unregister_data(pResourceManager, pFilePath); -} - -MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath) -{ - return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath); -} - -MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName) -{ - return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL); -} - -MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName) -{ - return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName); -} - - -static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return c89atomic_fetch_add_32(&pDataStream->executionCounter, 1); -} - -static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return c89atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); -} - -static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return c89atomic_load_32((ma_uint32*)&pDataStream->seekCounter); -} - - -static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex); -} - -static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor); -} - -static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength); -} - -static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; - MA_ASSERT(pDataStream != NULL); - - c89atomic_exchange_32(&pDataStream->isLooping, isLooping); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = -{ - ma_resource_manager_data_stream_cb__read_pcm_frames, - ma_resource_manager_data_stream_cb__seek_to_pcm_frame, - ma_resource_manager_data_stream_cb__get_data_format, - ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, - ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, - ma_resource_manager_data_stream_cb__set_looping, - MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT -}; - -static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) -{ - /* Loop if possible. */ - if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) { - absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; - } - - c89atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); -} - -MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - char* pFilePathCopy = NULL; - wchar_t* pFilePathWCopy = NULL; - ma_job job; - ma_bool32 waitBeforeReturning = MA_FALSE; - ma_resource_manager_inline_notification waitNotification; - ma_resource_manager_pipeline_notifications notifications; - - if (pDataStream == NULL) { - if (pConfig != NULL && pConfig->pNotifications != NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); - } - - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataStream); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pNotifications != NULL) { - notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */ - } else { - MA_ZERO_OBJECT(¬ifications); - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return result; - } - - pDataStream->pResourceManager = pResourceManager; - pDataStream->flags = pConfig->flags; - pDataStream->result = MA_BUSY; - - ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - ma_data_source_set_looping(pDataStream, pConfig->isLooping); - - if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return MA_INVALID_ARGS; - } - - /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */ - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pConfig->pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks); - } else { - pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return MA_OUT_OF_MEMORY; - } - - /* - We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we - can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same. - */ - if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - waitBeforeReturning = MA_TRUE; - ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); - } - - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - - /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames); - - /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.loadDataStream.pDataStream = pDataStream; - job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy; - job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy; - job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames; - job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification; - job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence; - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - - if (waitBeforeReturning) { - ma_resource_manager_inline_notification_uninit(&waitNotification); - } - - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* Wait if needed. */ - if (waitBeforeReturning) { - ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification); - - if (notifications.init.pNotification != NULL) { - ma_async_notification_signal(notifications.init.pNotification); - } - - /* NOTE: Do not release pInitFence here. That will be done by the job. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); -} - -MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); -} - -MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_inline_notification freeEvent; - ma_job job; - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ - c89atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); - - /* - We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need - to wait for it to complete before returning which means we need an event. - */ - ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.freeDataStream.pDataStream = pDataStream; - job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent; - job.data.resourceManager.freeDataStream.pDoneFence = NULL; - ma_resource_manager_post_job(pDataStream->pResourceManager, &job); - - /* We need to wait for the job to finish processing before we return. */ - ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent); - - return MA_SUCCESS; -} - - -static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); - - return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000); -} - -static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor) -{ - MA_ASSERT(pDataStream != NULL); - MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); - MA_ASSERT(pageIndex == 0 || pageIndex == 1); - - return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels)); -} - -static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex) -{ - ma_result result = MA_SUCCESS; - ma_uint64 pageSizeInFrames; - ma_uint64 totalFramesReadForThisPage = 0; - void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0); - - pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); - - /* The decoder needs to inherit the stream's looping and range state. */ - { - ma_uint64 rangeBeg; - ma_uint64 rangeEnd; - ma_uint64 loopPointBeg; - ma_uint64 loopPointEnd; - - ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream)); - - ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd); - ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd); - - ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd); - ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd); - } - - /* Just read straight from the decoder. It will deal with ranges and looping for us. */ - result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); - if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { - c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); - } - - c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); - c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); -} - -static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) -{ - ma_uint32 iPage; - - MA_ASSERT(pDataStream != NULL); - - for (iPage = 0; iPage < 2; iPage += 1) { - ma_resource_manager_data_stream_fill_page(pDataStream, iPage); - } -} - - -static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_uint64 framesAvailable; - ma_uint64 frameCount = 0; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pFrameCount != NULL) { - frameCount = *pFrameCount; - *pFrameCount = 0; - } - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; - } - - if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ - if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { - return MA_BUSY; - } - - /* If the page we're on is invalid it means we've caught up to the job thread. */ - if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { - framesAvailable = 0; - } else { - /* - The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is - that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. - */ - ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); - MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); - - framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; - } - - /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */ - if (framesAvailable == 0) { - if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) { - return MA_AT_END; - } else { - return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */ - } - } - - MA_ASSERT(framesAvailable > 0); - - if (frameCount > framesAvailable) { - frameCount = framesAvailable; - } - - *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor); - *pFrameCount = frameCount; - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount) -{ - ma_uint32 newRelativeCursor; - ma_uint32 pageSizeInFrames; - ma_job job; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* The frame count should always fit inside a 32-bit integer. */ - if (frameCount > 0xFFFFFFFF) { - return MA_INVALID_ARGS; - } - - pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); - - /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount); - - /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ - newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; - - /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */ - if (newRelativeCursor >= pageSizeInFrames) { - newRelativeCursor -= pageSizeInFrames; - - /* Here is where we post the job start decoding. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.pageDataStream.pDataStream = pDataStream; - job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; - - /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ - c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); - - /* Before posting the job we need to make sure we set some state. */ - pDataStream->relativeCursor = newRelativeCursor; - pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01; - return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); - } else { - /* We haven't moved into a new page so we can just move the cursor forward. */ - pDataStream->relativeCursor = newRelativeCursor; - return MA_SUCCESS; - } -} - - -MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesProcessed; - ma_format format; - ma_uint32 channels; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ - if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { - return MA_BUSY; - } - - ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0); - - /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */ - totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - void* pMappedFrames; - ma_uint64 mappedFrameCount; - - mappedFrameCount = frameCount - totalFramesProcessed; - result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount); - if (result != MA_SUCCESS) { - break; - } - - /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */ - if (pFramesOut != NULL) { - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels); - } - - totalFramesProcessed += mappedFrameCount; - - result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount); - if (result != MA_SUCCESS) { - break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */ - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - if (result == MA_SUCCESS && totalFramesProcessed == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex) -{ - ma_job job; - ma_result streamResult; - - streamResult = ma_resource_manager_data_stream_result(pDataStream); - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(streamResult != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) { - return MA_INVALID_OPERATION; - } - - /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ - if (c89atomic_load_32(&pDataStream->seekCounter) == 0) { - if (c89atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { - return MA_SUCCESS; - } - } - - - /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ - c89atomic_fetch_add_32(&pDataStream->seekCounter, 1); - - /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); - - /* - We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public - API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of - the first page. - */ - pDataStream->relativeCursor = 0; - pDataStream->currentPageIndex = 0; - c89atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); - c89atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); - - /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ - c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); - - /* - The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages - are invalid and any content contained within them will be discarded and replaced with newly decoded data. - */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.seekDataStream.pDataStream = pDataStream; - job.data.resourceManager.seekDataStream.frameIndex = frameIndex; - return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); -} - -MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - - if (pChannels != NULL) { - *pChannels = 0; - } - - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* - We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function - such that the application is responsible for ensuring it's not called while uninitializing so it should be safe. - */ - return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor) -{ - ma_result result; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - /* - If the stream is in an erroneous state we need to return an invalid operation. We can allow - this to be called when the data stream is in a busy state because the caller may have asked - for an initial seek position and it's convenient to return that as the cursor position. - */ - result = ma_resource_manager_data_stream_result(pDataStream); - if (result != MA_SUCCESS && result != MA_BUSY) { - return MA_INVALID_OPERATION; - } - - *pCursor = c89atomic_load_64(&pDataStream->absoluteCursor); - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength) -{ - ma_result streamResult; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - streamResult = ma_resource_manager_data_stream_result(pDataStream); - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(streamResult != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (streamResult != MA_SUCCESS) { - return streamResult; - } - - /* - We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we - calculated when we initialized it on the job thread. - */ - *pLength = pDataStream->totalLengthInPCMFrames; - if (*pLength == 0) { - return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream) -{ - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - return (ma_result)c89atomic_load_i32(&pDataStream->result); -} - -MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) -{ - return ma_data_source_set_looping(pDataStream, isLooping); -} - -MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream) -{ - if (pDataStream == NULL) { - return MA_FALSE; - } - - return c89atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ -} - -MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) -{ - ma_uint32 pageIndex0; - ma_uint32 pageIndex1; - ma_uint32 relativeCursor; - ma_uint64 availableFrames; - - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - pageIndex0 = pDataStream->currentPageIndex; - pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01; - relativeCursor = pDataStream->relativeCursor; - - availableFrames = 0; - if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { - availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; - if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { - availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); - } - } - - *pAvailableFrames = availableFrames; - return MA_SUCCESS; -} - - -static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSource); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - pDataSource->flags = pConfig->flags; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) -{ - ma_result result; - - result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource); - if (result != MA_SUCCESS) { - return result; - } - - /* The data source itself is just a data stream or a data buffer. */ - if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pName; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); -} - -MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pName; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); -} - -MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource) -{ - ma_result result; - ma_resource_manager_data_source_config config; - - if (pExistingDataSource == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_resource_manager_data_source_config_init(); - config.flags = pExistingDataSource->flags; - - result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource); - if (result != MA_SUCCESS) { - return result; - } - - /* Copying can only be done from data buffers. Streams cannot be copied. */ - if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return MA_INVALID_OPERATION; - } - - return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer); -} - -MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - /* All we need to is uninitialize the underlying data buffer or data stream. */ - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead); - } else { - return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead); - } -} - -MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex); - } else { - return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex); - } -} - -MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount); - } else { - return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ - } -} - -MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount); - } else { - return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ - } -} - -MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } else { - return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor); - } else { - return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength); - } else { - return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength); - } -} - -MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_result(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping); - } else { - return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping); - } -} - -MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_FALSE; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames); - } else { - return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames); - } -} - - -MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob) -{ - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_post(&pResourceManager->jobQueue, pJob); -} - -MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager) -{ - ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); - return ma_resource_manager_post_job(pResourceManager, &job); -} - -MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob) -{ - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_next(&pResourceManager->jobQueue, pJob); -} - - -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ - - /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ - if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ - } - - /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */ - if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) { - result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */ - goto done; - } - - /* - We're ready to start loading. Essentially what we're doing here is initializing the data supply - of the node. Once this is complete, data buffers can have their connectors initialized which - will allow then to have audio data read from them. - - Note that when the data supply type has been moved away from "unknown", that is when other threads - will determine that the node is available for data delivery and the data buffer connectors can be - initialized. Therefore, it's important that it is set after the data supply has been initialized. - */ - if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) { - /* - Decoding. This is the complex case because we're not going to be doing the entire decoding - process here. Instead it's going to be split of multiple jobs and loaded in pages. The - reason for this is to evenly distribute decoding time across multiple sounds, rather than - having one huge sound hog all the available processing resources. - - The first thing we do is initialize a decoder. This is allocated on the heap and is passed - around to the paging jobs. When the last paging job has completed it's processing, it'll - free the decoder for us. - - This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job - which is where the actual decoding work will be done. However, once this job is complete, - the node will be in a state where data buffer connectors can be initialized. - */ - ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */ - ma_job pageDataBufferNodeJob; - - /* Allocate the decoder by initializing a decoded data supply. */ - result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder); - - /* - Don't ever propagate an MA_BUSY result code or else the resource manager will think the - node is just busy decoding rather than in an error state. This should never happen, but - including this logic for safety just in case. - */ - if (result == MA_BUSY) { - result = MA_ERROR; - } - - if (result != MA_SUCCESS) { - if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result)); - } else { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result)); - #endif - } - - goto done; - } - - /* - At this point the node's data supply is initialized and other threads can start initializing - their data buffer connectors. However, no data will actually be available until we start to - actually decode it. To do this, we need to post a paging job which is where the decoding - work is done. - - Note that if an error occurred at an earlier point, this section will have been skipped. - */ - pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE); - pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence; - - /* The job has been set up so it can now be posted. */ - result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); - - /* - When we get here, we want to make sure the result code is set to MA_BUSY. The reason for - this is that the result will be copied over to the node's internal result variable. In - this case, since the decoding is still in-progress, we need to make sure the result code - is set to MA_BUSY. - */ - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result)); - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - } else { - result = MA_BUSY; - } - } else { - /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */ - result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW); - } - - -done: - /* File paths are no longer needed. */ - ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks); - ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks); - - /* - We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads - are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY - because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then - immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any - other error code would cause the buffer to look like it's in a state that it's not. - */ - c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); - - /* At this point initialization is complete and we can signal the notification if any. */ - if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification); - } - if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence); - } - - /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ - if (result != MA_BUSY) { - if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification); - } - if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence); - } - } - - /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ - c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - - if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification); - } - - if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); - } - - c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - - if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* Don't do any more decoding if the data buffer has started the uninitialization process. */ - result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); - if (result != MA_BUSY) { - goto done; - } - - /* We're ready to decode the next page. */ - result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); - - /* - If we have a success code by this point, we want to post another job. We're going to set the - result back to MA_BUSY to make it clear that there's still more to load. - */ - if (result == MA_SUCCESS) { - ma_job newJob; - newJob = *pJob; /* Everything is the same as the input job, except the execution order. */ - newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */ - - result = ma_resource_manager_post_job(pResourceManager, &newJob); - - /* Since the sound isn't yet fully decoded we want the status to be set to busy. */ - if (result == MA_SUCCESS) { - result = MA_BUSY; - } - } - -done: - /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */ - if (result != MA_BUSY) { - ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); - ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks); - } - - /* If we reached the end we need to treat it as successful. */ - if (result == MA_AT_END) { - result = MA_SUCCESS; - } - - /* Make sure we set the result of node in case some error occurred. */ - c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); - - /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ - if (result != MA_BUSY) { - if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification); - } - - if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence); - } - } - - c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return result; -} - - -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer* pDataBuffer; - ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown; - ma_bool32 isConnectorInitialized = MA_FALSE; - - /* - All we're doing here is checking if the node has finished loading. If not, we just re-post the job - and keep waiting. Otherwise we increment the execution counter and set the buffer's result code. - */ - MA_ASSERT(pJob != NULL); - - pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer; - MA_ASSERT(pDataBuffer != NULL); - - pResourceManager = pDataBuffer->pResourceManager; - - if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ - } - - /* - First thing we need to do is check whether or not the data buffer is getting deleted. If so we - just abort, but making sure we increment the execution pointer. - */ - result = ma_resource_manager_data_buffer_result(pDataBuffer); - if (result != MA_BUSY) { - goto done; /* <-- This will ensure the exucution pointer is incremented. */ - } else { - result = MA_SUCCESS; /* <-- Make sure this is reset. */ - } - - /* Try initializing the connector if we haven't already. */ - isConnectorInitialized = pDataBuffer->isConnectorInitialized; - if (isConnectorInitialized == MA_FALSE) { - dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); - - if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) { - /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ - ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */ - dataSourceConfig = ma_resource_manager_data_source_config_init(); - dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames; - dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames; - dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames; - dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames; - dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping; - - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result)); - goto done; - } - } else { - /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */ - } - } else { - /* The connector is already initialized. Nothing to do here. */ - } - - /* - If the data node is still loading, we need to repost the job and *not* increment the execution - pointer (i.e. we need to not fall through to the "done" label). - - There is a hole between here and the where the data connector is initialized where the data - buffer node may have finished initializing. We need to check for this by checking the result of - the data buffer node and whether or not we had an unknown data supply type at the time of - trying to initialize the data connector. - */ - result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); - if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { - return ma_resource_manager_post_job(pResourceManager, pJob); - } - -done: - /* Only move away from a busy code so that we don't trash any existing error codes. */ - c89atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); - - /* Only signal the other threads after the result has been set just for cleanliness sake. */ - if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification); - } - if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence); - } - - /* - If at this point the data buffer has not had it's connector initialized, it means the - notification event was never signalled which means we need to signal it here. - */ - if (pDataBuffer->isConnectorInitialized == MA_FALSE && result != MA_SUCCESS) { - if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); - } - if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence); - } - } - - c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer* pDataBuffer; - - MA_ASSERT(pJob != NULL); - - pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer; - MA_ASSERT(pDataBuffer != NULL); - - pResourceManager = pDataBuffer->pResourceManager; - - if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification); - } - - if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); - } - - c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_decoder_config decoderConfig; - ma_uint32 pageBufferSizeInBytes; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) { - result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */ - goto done; - } - - /* We need to initialize the decoder first so we can determine the size of the pages. */ - decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager); - - if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) { - result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder); - } else { - result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder); - } - if (result != MA_SUCCESS) { - goto done; - } - - /* Retrieve the total length of the file before marking the decoder are loaded. */ - if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { - result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); - if (result != MA_SUCCESS) { - goto done; /* Failed to retrieve the length. */ - } - } else { - pDataStream->totalLengthInPCMFrames = 0; - } - - /* - Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file - and we don't want to have another thread trying to access the decoder while it's scanning. - */ - pDataStream->isDecoderInitialized = MA_TRUE; - - /* We have the decoder so we can now initialize our page buffer. */ - pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels); - - pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pDataStream->pPageData == NULL) { - ma_decoder_uninit(&pDataStream->decoder); - result = MA_OUT_OF_MEMORY; - goto done; - } - - /* Seek to our initial seek point before filling the initial pages. */ - ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint); - - /* We have our decoder and our page buffer, so now we need to fill our pages. */ - ma_resource_manager_data_stream_fill_pages(pDataStream); - - /* And now we're done. We want to make sure the result is MA_SUCCESS. */ - result = MA_SUCCESS; - -done: - ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks); - ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); - - /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ - c89atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); - - /* Only signal the other threads after the result has been set just for cleanliness sake. */ - if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification); - } - if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); - } - - c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE); - - if (pDataStream->isDecoderInitialized) { - ma_decoder_uninit(&pDataStream->decoder); - } - - if (pDataStream->pPageData != NULL) { - ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks); - pDataStream->pPageData = NULL; /* Just in case... */ - } - - ma_data_source_uninit(&pDataStream->ds); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification); - } - if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); - } - - /*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* For streams, the status should be MA_SUCCESS. */ - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - result = MA_INVALID_OPERATION; - goto done; - } - - ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); - -done: - c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* For streams the status should be MA_SUCCESS for this to do anything. */ - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) { - result = MA_INVALID_OPERATION; - goto done; - } - - /* - With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except - instead of initializing the decoder, we seek to a frame. - */ - ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex); - - /* After seeking we'll need to reload the pages. */ - ma_resource_manager_data_stream_fill_pages(pDataStream); - - /* We need to let the public API know that we're done seeking. */ - c89atomic_fetch_sub_32(&pDataStream->seekCounter, 1); - -done: - c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob) -{ - if (pResourceManager == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_process(pJob); -} - -MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager) -{ - ma_result result; - ma_job job; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - /* This will return MA_CANCELLED if the next job is a quit job. */ - result = ma_resource_manager_next_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - return result; - } - - return ma_job_process(&job); -} -#else -/* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */ -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -#endif /* MA_NO_RESOURCE_MANAGER */ - - -#ifndef MA_NO_NODE_GRAPH -/* 10ms @ 48K = 480. Must never exceed 65535. */ -#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS -#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 -#endif - - -static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); - -MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - #ifndef MA_NO_GENERATION - { - ma_waveform_config waveformConfig; - ma_waveform waveform; - - waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400); - ma_waveform_init(&waveformConfig, &waveform); - ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL); - } - #else - { - (void)pFramesOut; - (void)frameCount; - (void)format; - (void)channels; - (void)sampleRate; - #if defined(MA_DEBUG_OUTPUT) - { - #if _MSC_VER - #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.") - #endif - } - #endif - } - #endif -} - - - -static ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) -{ - ma_uint64 iSample; - ma_uint64 sampleCount; - - if (pDst == NULL || pSrc == NULL || channels == 0) { - return MA_INVALID_ARGS; - } - - if (volume == 0) { - return MA_SUCCESS; /* No changes if the volume is 0. */ - } - - sampleCount = frameCount * channels; - - if (volume == 1) { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += pSrc[iSample]; - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); - } - } - - return MA_SUCCESS; -} - - -MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) -{ - ma_node_graph_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; - - return config; -} - - -static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) -{ - MA_ASSERT(pNodeGraph != NULL); - c89atomic_exchange_32(&pNodeGraph->isReading, isReading); -} - -#if 0 -static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) -{ - MA_ASSERT(pNodeGraph != NULL); - return c89atomic_load_32(&pNodeGraph->isReading); -} -#endif - - -static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_graph* pNodeGraph = (ma_node_graph*)pNode; - ma_uint64 framesRead; - - ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead); - - *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */ - - (void)ppFramesIn; - (void)pFrameCountIn; -} - -static ma_node_vtable g_node_graph_node_vtable = -{ - ma_node_graph_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* 0 input buses. */ - 1, /* 1 output bus. */ - 0 /* Flags. */ -}; - -static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - MA_ASSERT(pNode != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1); - MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1); - - /* Input channel count needs to be the same as the output channel count. */ - MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0)); - - /* We don't need to do anything here because it's a passthrough. */ - (void)pNode; - (void)ppFramesIn; - (void)pFrameCountIn; - (void)ppFramesOut; - (void)pFrameCountOut; - -#if 0 - /* The data has already been mixed. We just need to move it to the output buffer. */ - if (ppFramesIn != NULL) { - ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0)); - } -#endif -} - -static ma_node_vtable g_node_graph_endpoint_vtable = -{ - ma_node_graph_endpoint_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - 1, /* 1 output bus. */ - MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */ -}; - -MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph) -{ - ma_result result; - ma_node_config baseConfig; - ma_node_config endpointConfig; - - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNodeGraph); - pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames; - if (pNodeGraph->nodeCacheCapInFrames == 0) { - pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; - } - - - /* Base node so we can use the node graph as a node into another graph. */ - baseConfig = ma_node_config_init(); - baseConfig.vtable = &g_node_graph_node_vtable; - baseConfig.pOutputChannels = &pConfig->channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base); - if (result != MA_SUCCESS) { - return result; - } - - - /* Endpoint. */ - endpointConfig = ma_node_config_init(); - endpointConfig.vtable = &g_node_graph_endpoint_vtable; - endpointConfig.pInputChannels = &pConfig->channels; - endpointConfig.pOutputChannels = &pConfig->channels; - - result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint); - if (result != MA_SUCCESS) { - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; -} - -MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pNodeGraph == NULL) { - return; - } - - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); -} - -MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return NULL; - } - - return &pNodeGraph->endpoint; -} - -MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesRead; - ma_uint32 channels; - - if (pFramesRead != NULL) { - *pFramesRead = 0; /* Safety. */ - } - - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0); - - - /* We'll be nice and try to do a full read of all frameCount frames. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - ma_uint32 framesJustRead; - ma_uint64 framesToRead = frameCount - totalFramesRead; - - if (framesToRead > 0xFFFFFFFF) { - framesToRead = 0xFFFFFFFF; - } - - ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); - { - result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); - } - ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); - - totalFramesRead += framesJustRead; - - if (result != MA_SUCCESS) { - break; - } - - /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ - if (framesJustRead == 0) { - break; - } - } - - /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */ - if (totalFramesRead < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels); - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return 0; - } - - return ma_node_get_output_channels(&pNodeGraph->endpoint, 0); -} - -MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return 0; - } - - return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */ -} - -MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime) -{ - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */ -} - - -#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ - -static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pOutputBus != NULL); - MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT); - MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode)); - MA_ASSERT(channels < 256); - - MA_ZERO_OBJECT(pOutputBus); - - if (channels == 0) { - return MA_INVALID_ARGS; - } - - pOutputBus->pNode = pNode; - pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex; - pOutputBus->channels = (ma_uint8)channels; - pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */ - pOutputBus->volume = 1; - - return MA_SUCCESS; -} - -static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus) -{ - ma_spinlock_lock(&pOutputBus->lock); -} - -static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus) -{ - ma_spinlock_unlock(&pOutputBus->lock); -} - - -static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus) -{ - return pOutputBus->channels; -} - - -static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) -{ - if (hasRead) { - c89atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); - } else { - c89atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); - } -} - -static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) -{ - return (c89atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; -} - - -static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) -{ - c89atomic_exchange_32(&pOutputBus->isAttached, isAttached); -} - -static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) -{ - return c89atomic_load_32(&pOutputBus->isAttached); -} - - -static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume) -{ - MA_ASSERT(pOutputBus != NULL); - - if (volume < 0.0f) { - volume = 0.0f; - } - - c89atomic_exchange_f32(&pOutputBus->volume, volume); - - return MA_SUCCESS; -} - -static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) -{ - return c89atomic_load_f32((float*)&pOutputBus->volume); -} - - -static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(channels < 256); - - MA_ZERO_OBJECT(pInputBus); - - if (channels == 0) { - return MA_INVALID_ARGS; - } - - pInputBus->channels = (ma_uint8)channels; - - return MA_SUCCESS; -} - -static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) -{ - ma_spinlock_lock(&pInputBus->lock); -} - -static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) -{ - ma_spinlock_unlock(&pInputBus->lock); -} - - -static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) -{ - c89atomic_fetch_add_32(&pInputBus->nextCounter, 1); -} - -static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) -{ - c89atomic_fetch_sub_32(&pInputBus->nextCounter, 1); -} - -static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) -{ - return c89atomic_load_32(&pInputBus->nextCounter); -} - - -static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus) -{ - return pInputBus->channels; -} - - -static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - /* - Mark the output bus as detached first. This will prevent future iterations on the audio thread - from iterating this output bus. - */ - ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE); - - /* - We cannot use the output bus lock here since it'll be getting used at a higher level, but we do - still need to use the input bus lock since we'll be updating pointers on two different output - buses. The same rules apply here as the attaching case. Although we're using a lock here, we're - *not* using a lock when iterating over the list in the audio thread. We therefore need to craft - this in a way such that the iteration on the audio thread doesn't break. - - The the first thing to do is swap out the "next" pointer of the previous output bus with the - new "next" output bus. This is the operation that matters for iteration on the audio thread. - After that, the previous pointer on the new "next" pointer needs to be updated, after which - point the linked list will be in a good state. - */ - ma_node_input_bus_lock(pInputBus); - { - ma_node_output_bus* pOldPrev = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pPrev); - ma_node_output_bus* pOldNext = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext); - - if (pOldPrev != NULL) { - c89atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ - } - if (pOldNext != NULL) { - c89atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ - } - } - ma_node_input_bus_unlock(pInputBus); - - /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ - c89atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ - c89atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ - pOutputBus->pInputNode = NULL; - pOutputBus->inputNodeInputBusIndex = 0; - - - /* - For thread-safety reasons, we don't want to be returning from this straight away. We need to - wait for the audio thread to finish with the output bus. There's two things we need to wait - for. The first is the part that selects the next output bus in the list, and the other is the - part that reads from the output bus. Basically all we're doing is waiting for the input bus - to stop referencing the output bus. - - We're doing this part last because we want the section above to run while the audio thread - is finishing up with the output bus, just for efficiency reasons. We marked the output bus as - detached right at the top of this function which is going to prevent the audio thread from - iterating the output bus again. - */ - - /* Part 1: Wait for the current iteration to complete. */ - while (ma_node_input_bus_get_next_counter(pInputBus) > 0) { - ma_yield(); - } - - /* Part 2: Wait for any reads to complete. */ - while (c89atomic_load_32(&pOutputBus->refCount) > 0) { - ma_yield(); - } - - /* - At this point we're done detaching and we can be guaranteed that the audio thread is not going - to attempt to reference this output bus again (until attached again). - */ -} - -#if 0 /* Not used at the moment, but leaving here in case I need it later. */ -static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - ma_node_output_bus_lock(pOutputBus); - { - ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); - } - ma_node_output_bus_unlock(pOutputBus); -} -#endif - -static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - ma_node_output_bus_lock(pOutputBus); - { - ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pInputNode); - - /* Detach from any existing attachment first if necessary. */ - if (pOldInputNode != NULL) { - ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); - } - - /* - At this point we can be sure the output bus is not attached to anything. The linked list in the - old input bus has been updated so that pOutputBus will not get iterated again. - */ - pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ - pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; /* As above. */ - - /* - Now we need to attach the output bus to the linked list. This involves updating two pointers on - two different output buses so I'm going to go ahead and keep this simple and just use a lock. - There are ways to do this without a lock, but it's just too hard to maintain for it's value. - - Although we're locking here, it's important to remember that we're *not* locking when iterating - and reading audio data since that'll be running on the audio thread. As a result we need to be - careful how we craft this so that we don't break iteration. What we're going to do is always - attach the new item so that it becomes the first item in the list. That way, as we're iterating - we won't break any links in the list and iteration will continue safely. The detaching case will - also be crafted in a way as to not break list iteration. It's important to remember to use - atomic exchanges here since no locking is happening on the audio thread during iteration. - */ - ma_node_input_bus_lock(pInputBus); - { - ma_node_output_bus* pNewPrev = &pInputBus->head; - ma_node_output_bus* pNewNext = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); - - /* Update the local output bus. */ - c89atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); - c89atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); - - /* Update the other output buses to point back to the local output bus. */ - c89atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ - - /* Do the previous pointer last. This is only used for detachment. */ - if (pNewNext != NULL) { - c89atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); - } - } - ma_node_input_bus_unlock(pInputBus); - - /* - Mark the node as attached last. This is used to controlling whether or the output bus will be - iterated on the audio thread. Mainly required for detachment purposes. - */ - ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE); - } - ma_node_output_bus_unlock(pOutputBus); -} - -static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - ma_node_output_bus* pNext; - - MA_ASSERT(pInputBus != NULL); - - if (pOutputBus == NULL) { - return NULL; - } - - ma_node_input_bus_next_begin(pInputBus); - { - pNext = pOutputBus; - for (;;) { - pNext = (ma_node_output_bus*)c89atomic_load_ptr(&pNext->pNext); - if (pNext == NULL) { - break; /* Reached the end. */ - } - - if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) { - continue; /* The node is not attached. Keep checking. */ - } - - /* The next node has been selected. */ - break; - } - - /* We need to increment the reference count of the selected node. */ - if (pNext != NULL) { - c89atomic_fetch_add_32(&pNext->refCount, 1); - } - - /* The previous node is no longer being referenced. */ - c89atomic_fetch_sub_32(&pOutputBus->refCount, 1); - } - ma_node_input_bus_next_end(pInputBus); - - return pNext; -} - -static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus) -{ - return ma_node_input_bus_next(pInputBus, &pInputBus->head); -} - - - -static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) -{ - ma_result result = MA_SUCCESS; - ma_node_output_bus* pOutputBus; - ma_node_output_bus* pFirst; - ma_uint32 inputChannels; - ma_bool32 doesOutputBufferHaveContent = MA_FALSE; - - /* - This will be called from the audio thread which means we can't be doing any locking. Basically, - this function will not perfom any locking, whereas attaching and detaching will, but crafted in - such a way that we don't need to perform any locking here. The important thing to remember is - to always iterate in a forward direction. - - In order to process any data we need to first read from all input buses. That's where this - function comes in. This iterates over each of the attachments and accumulates/mixes them. We - also convert the channels to the nodes output channel count before mixing. We want to do this - channel conversion so that the caller of this function can invoke the processing callback - without having to do it themselves. - - When we iterate over each of the attachments on the input bus, we need to read as much data as - we can from each of them so that we don't end up with holes between each of the attachments. To - do this, we need to read from each attachment in a loop and read as many frames as we can, up - to `frameCount`. - */ - MA_ASSERT(pInputNode != NULL); - MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */ - - *pFramesRead = 0; /* Safety. */ - - inputChannels = ma_node_input_bus_get_channels(pInputBus); - - /* - We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They - are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first() - once per iteration, however we have an optimization to checks whether or not it's the first item in - the list. We therefore need to store a pointer to the first item rather than repeatedly calling - ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it - after calling ma_node_input_bus_next(), which we won't be. - */ - pFirst = ma_node_input_bus_first(pInputBus); - if (pFirst == NULL) { - return MA_SUCCESS; /* No attachments. Read nothing. */ - } - - for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) { - ma_uint32 framesProcessed = 0; - ma_bool32 isSilentOutput = MA_FALSE; - - MA_ASSERT(pOutputBus->pNode != NULL); - - isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; - - if (pFramesOut != NULL) { - /* Read. */ - float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; - ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels; - - while (framesProcessed < frameCount) { - float* pRunningFramesOut; - ma_uint32 framesToRead; - ma_uint32 framesJustRead; - - framesToRead = frameCount - framesProcessed; - if (framesToRead > tempCapInFrames) { - framesToRead = tempCapInFrames; - } - - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); - - if (doesOutputBufferHaveContent == MA_FALSE) { - /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */ - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); - } else { - /* Slow path. Not the first attachment. Mixing required. */ - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed); - if (result == MA_SUCCESS || result == MA_AT_END) { - if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ - ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1); - } - } - } - - framesProcessed += framesJustRead; - - /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */ - if (result != MA_SUCCESS) { - break; - } - - /* If we didn't read anything, abort so we don't get stuck in a loop. */ - if (framesJustRead == 0) { - break; - } - } - - /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */ - if (pOutputBus == pFirst && framesProcessed < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels); - } - - if (isSilentOutput == MA_FALSE) { - doesOutputBufferHaveContent = MA_TRUE; - } - } else { - /* Seek. */ - ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime); - } - } - - /* If we didn't output anything, output silence. */ - if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels); - } - - /* In this path we always "process" the entire amount. */ - *pFramesRead = frameCount; - - return result; -} - - -MA_API ma_node_config ma_node_config_init(void) -{ - ma_node_config config; - - MA_ZERO_OBJECT(&config); - config.initialState = ma_node_state_started; /* Nodes are started by default. */ - config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; - config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; - - return config; -} - - - -static ma_result ma_node_detach_full(ma_node* pNode); - -static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - float* pBasePtr; - - MA_ASSERT(pNodeBase != NULL); - - /* Input data is stored at the front of the buffer. */ - pBasePtr = pNodeBase->pCachedData; - for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); - } - - return pBasePtr; -} - -static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - float* pBasePtr; - - MA_ASSERT(pNodeBase != NULL); - - /* Cached output data starts after the input data. */ - pBasePtr = pNodeBase->pCachedData; - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); - } - - for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]); - } - - return pBasePtr; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t inputBusOffset; - size_t outputBusOffset; - size_t cachedDataOffset; - ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */ - ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */ -} ma_node_heap_layout; - -static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount) -{ - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pInputBusCount != NULL); - MA_ASSERT(pOutputBusCount != NULL); - - /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ - if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { - inputBusCount = pConfig->inputBusCount; - } else { - inputBusCount = pConfig->vtable->inputBusCount; - - if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) { - return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ - } - } - - if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { - outputBusCount = pConfig->outputBusCount; - } else { - outputBusCount = pConfig->vtable->outputBusCount; - - if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) { - return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ - } - } - - /* Bus counts must be within limits. */ - if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) { - return MA_INVALID_ARGS; - } - - - /* We must have channel counts for each bus. */ - if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { - return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ - } - - - /* Some special rules for passthrough nodes. */ - if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - if (pConfig->vtable->inputBusCount != 1 || pConfig->vtable->outputBusCount != 1) { - return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 input bus and 1 output bus. */ - } - - if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { - return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */ - } - } - - - *pInputBusCount = inputBusCount; - *pOutputBusCount = outputBusCount; - - return MA_SUCCESS; -} - -static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes = 0; - - /* Input buses. */ - if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { - pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount); - } else { - pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */ - } - - /* Output buses. */ - if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { - pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount); - } else { - pHeapLayout->outputBusOffset = MA_SIZE_MAX; - } - - /* - Cached audio data. - - We need to allocate memory for a caching both input and output data. We have an optimization - where no caching is necessary for specific conditions: - - - The node has 0 inputs and 1 output. - - When a node meets the above conditions, no cache is allocated. - - The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by - allocating too much, but at the same time we want it be large enough so that enough frames can - be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For - now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile - time. It might also be worth investigating whether or not this can be configured at run time. - */ - if (inputBusCount == 0 && outputBusCount == 1) { - /* Fast path. No cache needed. */ - pHeapLayout->cachedDataOffset = MA_SIZE_MAX; - } else { - /* Slow path. Cache needed. */ - size_t cachedDataSizeInBytes = 0; - ma_uint32 iBus; - - for (iBus = 0; iBus < inputBusCount; iBus += 1) { - cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); - } - - for (iBus = 0; iBus < outputBusCount; iBus += 1) { - cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); - } - - pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes); - } - - - /* - Not technically part of the heap, but we can output the input and output bus counts so we can - avoid a redundant call to ma_node_translate_bus_counts(). - */ - pHeapLayout->inputBusCount = inputBusCount; - pHeapLayout->outputBusCount = outputBusCount; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_node_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_result result; - ma_node_heap_layout heapLayout; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNodeBase); - - result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pNodeBase->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pNodeBase->pNodeGraph = pNodeGraph; - pNodeBase->vtable = pConfig->vtable; - pNodeBase->state = pConfig->initialState; - pNodeBase->stateTimes[ma_node_state_started] = 0; - pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */ - pNodeBase->inputBusCount = heapLayout.inputBusCount; - pNodeBase->outputBusCount = heapLayout.outputBusCount; - - if (heapLayout.inputBusOffset != MA_SIZE_MAX) { - pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); - } else { - pNodeBase->pInputBuses = pNodeBase->_inputBuses; - } - - if (heapLayout.outputBusOffset != MA_SIZE_MAX) { - pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); - } else { - pNodeBase->pOutputBuses = pNodeBase->_outputBuses; - } - - if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { - pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); - pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames; - } else { - pNodeBase->pCachedData = NULL; - } - - - - /* We need to run an initialization step for each input and output bus. */ - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { - result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); - if (result != MA_SUCCESS) { - return result; - } - } - - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { - result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); - if (result != MA_SUCCESS) { - return result; - } - } - - - /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */ - if (pNodeBase->pCachedData != NULL) { - ma_uint32 iBus; - - #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */ - /* For safety we'll go ahead and default the buffer to silence. */ - for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { - ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus])); - } - for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { - ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus])); - } - #else - /* For debugging. Default to a sine wave. */ - for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { - ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000); - } - for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { - ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000); - } - #endif - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return; - } - - /* - The first thing we need to do is fully detach the node. This will detach all inputs and - outputs. We need to do this first because it will sever the connection with the node graph and - allow us to complete uninitialization without needing to worry about thread-safety with the - audio thread. The detachment process will wait for any local processing of the node to finish. - */ - ma_node_detach_full(pNode); - - /* - At this point the node should be completely unreferenced by the node graph and we can finish up - the uninitialization process without needing to worry about thread-safety. - */ - if (pNodeBase->_ownsHeap) { - ma_free(pNodeBase->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode) -{ - if (pNode == NULL) { - return NULL; - } - - return ((const ma_node_base*)pNode)->pNodeGraph; -} - -MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ((ma_node_base*)pNode)->inputBusCount; -} - -MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ((ma_node_base*)pNode)->outputBusCount; -} - - -MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNode == NULL) { - return 0; - } - - if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]); -} - -MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNode == NULL) { - return 0; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]); -} - - -static ma_result ma_node_detach_full(ma_node* pNode) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - /* - Make sure the node is completely detached first. This will not return until the output bus is - guaranteed to no longer be referenced by the audio thread. - */ - ma_node_detach_all_output_buses(pNode); - - /* - At this point all output buses will have been detached from the graph and we can be guaranteed - that none of it's input nodes will be getting processed by the graph. We can detach these - without needing to worry about the audio thread touching them. - */ - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { - ma_node_input_bus* pInputBus; - ma_node_output_bus* pOutputBus; - - pInputBus = &pNodeBase->pInputBuses[iInputBus]; - - /* - This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those - functions are specifically for the audio thread. We'll instead just manually iterate using standard - linked list logic. We don't need to worry about the audio thread referencing these because the step - above severed the connection to the graph. - */ - for (pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext)) { - ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex) -{ - ma_result result = MA_SUCCESS; - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_node_base* pInputNodeBase; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return MA_INVALID_ARGS; /* Invalid output bus index. */ - } - - /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */ - ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); - { - pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; - if (pInputNodeBase != NULL) { - ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]); - } - } - ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]); - - return result; -} - -MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode) -{ - ma_uint32 iOutputBus; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) { - ma_node_detach_output_bus(pNode, iOutputBus); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode; - - if (pNodeBase == NULL || pOtherNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (pNodeBase == pOtherNodeBase) { - return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */ - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) { - return MA_INVALID_OPERATION; /* Invalid bus index. */ - } - - /* The output channel count of the output node must be the same as the input channel count of the input node. */ - if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) { - return MA_INVALID_OPERATION; /* Channel count is incompatible. */ - } - - /* This will deal with detaching if the output bus is already attached to something. */ - ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return MA_INVALID_ARGS; /* Invalid bus index. */ - } - - return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume); -} - -MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return 0; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]); -} - -MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - c89atomic_exchange_i32(&pNodeBase->state, state); - - return MA_SUCCESS; -} - -MA_API ma_node_state ma_node_get_state(const ma_node* pNode) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return ma_node_state_stopped; - } - - return (ma_node_state)c89atomic_load_i32(&pNodeBase->state); -} - -MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) -{ - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ - if (state != ma_node_state_started && state != ma_node_state_stopped) { - return MA_INVALID_ARGS; - } - - c89atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); - - return MA_SUCCESS; -} - -MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state) -{ - if (pNode == NULL) { - return 0; - } - - /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ - if (state != ma_node_state_started && state != ma_node_state_stopped) { - return 0; - } - - return c89atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); -} - -MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) -{ - if (pNode == NULL) { - return ma_node_state_stopped; - } - - return ma_node_get_state_by_time_range(pNode, globalTime, globalTime); -} - -MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd) -{ - ma_node_state state; - - if (pNode == NULL) { - return ma_node_state_stopped; - } - - state = ma_node_get_state(pNode); - - /* An explicitly stopped node is always stopped. */ - if (state == ma_node_state_stopped) { - return ma_node_state_stopped; - } - - /* - Getting here means the node is marked as started, but it may still not be truly started due to - it's start time not having been reached yet. Also, the stop time may have also been reached in - which case it'll be considered stopped. - */ - if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { - return ma_node_state_stopped; /* Start time has not yet been reached. */ - } - - if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { - return ma_node_state_stopped; /* Stop time has been reached. */ - } - - /* Getting here means the node is marked as started and is within it's start/stop times. */ - return ma_node_state_started; -} - -MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return c89atomic_load_64(&((ma_node_base*)pNode)->localTime); -} - -MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) -{ - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - c89atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); - - return MA_SUCCESS; -} - - - -static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - MA_ASSERT(pNode != NULL); - - if (pNodeBase->vtable->onProcess) { - pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); - } -} - -static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_result result = MA_SUCCESS; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_uint32 totalFramesRead = 0; - float* ppFramesIn[MA_MAX_NODE_BUS_COUNT]; - float* ppFramesOut[MA_MAX_NODE_BUS_COUNT]; - ma_uint64 globalTimeBeg; - ma_uint64 globalTimeEnd; - ma_uint64 startTime; - ma_uint64 stopTime; - ma_uint32 timeOffsetBeg; - ma_uint32 timeOffsetEnd; - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - - /* - pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and - expected that the number of frames read may be different to that requested. Therefore, the caller - must look at this value to correctly determine how many frames were read. - */ - MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */ - if (pFramesRead == NULL) { - return MA_INVALID_ARGS; - } - - *pFramesRead = 0; /* Safety. */ - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) { - return MA_INVALID_ARGS; /* Invalid output bus index. */ - } - - /* Don't do anything if we're in a stopped state. */ - if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { - return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ - } - - - globalTimeBeg = globalTime; - globalTimeEnd = globalTime + frameCount; - startTime = ma_node_get_state_time(pNode, ma_node_state_started); - stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); - - /* - At this point we know that we are inside our start/stop times. However, we may need to adjust - our frame count and output pointer to accomodate since we could be straddling the time period - that this function is getting called for. - - It's possible (and likely) that the start time does not line up with the output buffer. We - therefore need to offset it by a number of frames to accomodate. The same thing applies for - the stop time. - */ - timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; - timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; - - /* Trim based on the start offset. We need to silence the start of the buffer. */ - if (timeOffsetBeg > 0) { - ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); - pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); - frameCount -= timeOffsetBeg; - } - - /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ - if (timeOffsetEnd > 0) { - frameCount -= timeOffsetEnd; - } - - - /* We run on different paths depending on the bus counts. */ - inputBusCount = ma_node_get_input_bus_count(pNode); - outputBusCount = ma_node_get_output_bus_count(pNode); - - /* - Run a simplified path when there are no inputs and one output. In this case there's nothing to - actually read and we can go straight to output. This is a very common scenario because the vast - majority of data source nodes will use this setup so this optimization I think is worthwhile. - */ - if (inputBusCount == 0 && outputBusCount == 1) { - /* Fast path. No need to read from input and no need for any caching. */ - frameCountIn = 0; - frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ - - ppFramesOut[0] = pFramesOut; - ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); - totalFramesRead = frameCountOut; - } else { - /* Slow path. Need to read input data. */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - /* - Fast path. We're running a passthrough. We need to read directly into the output buffer, but - still fire the callback so that event handling and trigger nodes can do their thing. Since - it's a passthrough there's no need for any kind of caching logic. - */ - MA_ASSERT(outputBusCount == inputBusCount); - MA_ASSERT(outputBusCount == 1); - MA_ASSERT(outputBusIndex == 0); - - /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */ - ppFramesOut[0] = pFramesOut; - ppFramesIn[0] = ppFramesOut[0]; - - result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime); - if (result == MA_SUCCESS) { - /* Even though it's a passthrough, we still need to fire the callback. */ - frameCountIn = totalFramesRead; - frameCountOut = totalFramesRead; - - if (totalFramesRead > 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ - } - - /* - A passthrough should never have modified the input and output frame counts. If you're - triggering these assers you need to fix your processing callback. - */ - MA_ASSERT(frameCountIn == totalFramesRead); - MA_ASSERT(frameCountOut == totalFramesRead); - } - } else { - /* Slow path. Need to do caching. */ - ma_uint32 framesToProcessIn; - ma_uint32 framesToProcessOut; - ma_bool32 consumeNullInput = MA_FALSE; - - /* - We use frameCount as a basis for the number of frames to read since that's what's being - requested, however we still need to clamp it to whatever can fit in the cache. - - This will also be used as the basis for determining how many input frames to read. This is - not ideal because it can result in too many input frames being read which introduces latency. - To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount - which is used as hint to miniaudio as to how many input frames it needs to read at a time. This - callback is completely optional, and if it's not set, miniaudio will assume `frameCount`. - - This function will be called multiple times for each period of time, once for each output node. - We cannot read from each input node each time this function is called. Instead we need to check - whether or not this is first output bus to be read from for this time period, and if so, read - from our input data. - - To determine whether or not we're ready to read data, we check a flag. There will be one flag - for each output. When the flag is set, it means data has been read previously and that we're - ready to advance time forward for our input nodes by reading fresh data. - */ - framesToProcessOut = frameCount; - if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) { - framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus; - } - - framesToProcessIn = frameCount; - if (pNodeBase->vtable->onGetRequiredInputFrameCount) { - pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */ - } - if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) { - framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus; - } - - - MA_ASSERT(framesToProcessIn <= 0xFFFF); - MA_ASSERT(framesToProcessOut <= 0xFFFF); - - if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) { - /* Getting here means we need to do another round of processing. */ - pNodeBase->cachedFrameCountOut = 0; - - for (;;) { - frameCountOut = 0; - - /* - We need to prepare our output frame pointers for processing. In the same iteration we need - to mark every output bus as unread so that future calls to this function for different buses - for the current time period don't pull in data when they should instead be reading from cache. - */ - for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) { - ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */ - ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus); - } - - /* We only need to read from input buses if there isn't already some data in the cache. */ - if (pNodeBase->cachedFrameCountIn == 0) { - ma_uint32 maxFramesReadIn = 0; - - /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */ - for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { - ma_uint32 framesRead; - - /* The first thing to do is get the offset within our bulk allocation to store this input data. */ - ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus); - - /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */ - result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime); - if (result != MA_SUCCESS) { - /* It doesn't really matter if we fail because we'll just fill with silence. */ - framesRead = 0; /* Just for safety, but I don't think it's really needed. */ - } - - /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */ - /* Any leftover frames need to silenced for safety. */ - if (framesRead < framesToProcessIn) { - ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus)); - } - - maxFramesReadIn = ma_max(maxFramesReadIn, framesRead); - } - - /* This was a fresh load of input data so reset our consumption counter. */ - pNodeBase->consumedFrameCountIn = 0; - - /* - We don't want to keep processing if there's nothing to process, so set the number of cached - input frames to the maximum number we read from each attachment (the lesser will be padded - with silence). If we didn't read anything, this will be set to 0 and the entire buffer will - have been assigned to silence. This being equal to 0 is an important property for us because - it allows us to detect when NULL can be passed into the processing callback for the input - buffer for the purpose of continuous processing. - */ - pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn; - } else { - /* We don't need to read anything, but we do need to prepare our input frame pointers. */ - for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { - ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus)); - } - } - - /* - At this point we have our input data so now we need to do some processing. Sneaky little - optimization here - we can set the pointer to the output buffer for this output bus so - that the final copy into the output buffer is done directly by onProcess(). - */ - if (pFramesOut != NULL) { - ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex)); - } - - - /* Give the processing function the entire capacity of the output buffer. */ - frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut); - - /* - We need to treat nodes with continuous processing a little differently. For these ones, - we always want to fire the callback with the requested number of frames, regardless of - pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass - in NULL for the input buffer to the callback. - */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) { - /* We're using continuous processing. Make sure we specify the whole frame count at all times. */ - frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */ - - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) { - consumeNullInput = MA_TRUE; - } else { - consumeNullInput = MA_FALSE; - } - - /* - Since we're using continuous processing we're always passing in a full frame count - regardless of how much input data was read. If this is greater than what we read as - input, we'll end up with an underflow. We instead need to make sure our cached frame - count is set to the number of frames we'll be passing to the data callback. Not - doing this will result in an underflow when we "consume" the cached data later on. - - Note that this check needs to be done after the "consumeNullInput" check above because - we use the property of cachedFrameCountIn being 0 to determine whether or not we - should be passing in a null pointer to the processing callback for when the node is - configured with MA_NODE_FLAG_ALLOW_NULL_INPUT. - */ - if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) { - pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn; - } - } else { - frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */ - consumeNullInput = MA_FALSE; - } - - /* - Process data slightly differently depending on whether or not we're consuming NULL - input (checked just above). - */ - if (consumeNullInput) { - ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); - } else { - /* - We want to skip processing if there's no input data, but we can only do that safely if - we know that there is no chance of any output frames being produced. If continuous - processing is being used, this won't be a problem because the input frame count will - always be non-0. However, if continuous processing is *not* enabled and input and output - data is processed at different rates, we still need to process that last input frame - because there could be a few excess output frames needing to be produced from cached - data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for - determining whether or not we need to process the node even when there are no input - frames available right now. - */ - if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ - } else { - frameCountOut = 0; /* No data was processed. */ - } - } - - /* - Thanks to our sneaky optimization above we don't need to do any data copying directly into - the output buffer - the onProcess() callback just did that for us. We do, however, need to - apply the number of input and output frames that were processed. Note that due to continuous - processing above, we need to do explicit checks here. If we just consumed a NULL input - buffer it means that no actual input data was processed from the internal buffers and we - don't want to be modifying any counters. - */ - if (consumeNullInput == MA_FALSE) { - pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn; - pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn; - } - - /* The cached output frame count is always equal to what we just read. */ - pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut; - - /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */ - if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) { - break; - } - } - } else { - /* - We're not needing to read anything from the input buffer so just read directly from our - already-processed data. - */ - if (pFramesOut != NULL) { - ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex)); - } - } - - /* The number of frames read is always equal to the number of cached output frames. */ - totalFramesRead = pNodeBase->cachedFrameCountOut; - - /* Now that we've read the data, make sure our read flag is set. */ - ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); - } - } - - /* Apply volume, if necessary. */ - ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); - - /* Advance our local time forward. */ - c89atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); - - *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ - return result; -} - - - - -/* Data source node. */ -MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource) -{ - ma_data_source_node_config config; - - MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.pDataSource = pDataSource; - - return config; -} - - -static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode; - ma_format format; - ma_uint32 channels; - ma_uint32 frameCount; - ma_uint64 framesRead = 0; - - MA_ASSERT(pDataSourceNode != NULL); - MA_ASSERT(pDataSourceNode->pDataSource != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0); - MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1); - - /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */ - (void)ppFramesIn; - (void)pFrameCountIn; - - frameCount = *pFrameCountOut; - - /* miniaudio should never be calling this with a frame count of zero. */ - MA_ASSERT(frameCount > 0); - - if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */ - /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */ - MA_ASSERT(format == ma_format_f32); - (void)format; /* Just to silence some static analysis tools. */ - - ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead); - } - - *pFrameCountOut = (ma_uint32)framesRead; -} - -static ma_node_vtable g_ma_data_source_node_vtable = -{ - ma_data_source_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* 0 input buses. */ - 1, /* 1 output bus. */ - 0 -}; - -MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode) -{ - ma_result result; - ma_format format; /* For validating the format, which must be ma_format_f32. */ - ma_uint32 channels; /* For specifying the channel count of the output bus. */ - ma_node_config baseConfig; - - if (pDataSourceNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSourceNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0); /* Don't care about sample rate. This will check pDataSource for NULL. */ - if (result != MA_SUCCESS) { - return result; - } - - MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */ - if (format != ma_format_f32) { - return MA_INVALID_ARGS; /* Invalid format. */ - } - - /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */ - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */ - - /* - The channel count is defined by the data source. It is invalid for the caller to manually set - the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the - channel count pointer to NULL which is how it must remain. If you trigger any of these asserts - it means you're explicitly setting the channel count. Instead, configure the output channel - count of your data source to be the necessary channel count. - */ - if (baseConfig.pOutputChannels != NULL) { - return MA_INVALID_ARGS; - } - - baseConfig.pOutputChannels = &channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base); - if (result != MA_SUCCESS) { - return result; - } - - pDataSourceNode->pDataSource = pConfig->pDataSource; - - return MA_SUCCESS; -} - -MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks); -} - -MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping) -{ - if (pDataSourceNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping); -} - -MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode) -{ - if (pDataSourceNode == NULL) { - return MA_FALSE; - } - - return ma_data_source_is_looping(pDataSourceNode->pDataSource); -} - - - -/* Splitter Node. */ -MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) -{ - ma_splitter_node_config config; - - MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.channels = channels; - - return config; -} - - -static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iOutputBus; - ma_uint32 channels; - - MA_ASSERT(pNodeBase != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); - MA_ASSERT(ma_node_get_output_bus_count(pNodeBase) >= 2); - - /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ - (void)pFrameCountIn; - - /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */ - channels = ma_node_get_input_channels(pNodeBase, 0); - - /* Splitting is just copying the first input bus and copying it over to each output bus. */ - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { - ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels); - } -} - -static ma_node_vtable g_ma_splitter_node_vtable = -{ - ma_splitter_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - 2, /* 2 output buses. */ - 0 -}; - -MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode) -{ - ma_result result; - ma_node_config baseConfig; - ma_uint32 pInputChannels[1]; - ma_uint32 pOutputChannels[2]; - - if (pSplitterNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSplitterNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Splitters require the same number of channels between inputs and outputs. */ - pInputChannels[0] = pConfig->channels; - pOutputChannels[0] = pConfig->channels; - pOutputChannels[1] = pConfig->channels; - - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_splitter_node_vtable; - baseConfig.pInputChannels = pInputChannels; - baseConfig.pOutputChannels = pOutputChannels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base node. */ - } - - return MA_SUCCESS; -} - -MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_uninit(pSplitterNode, pAllocationCallbacks); -} - - -/* -Biquad Node -*/ -MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2) -{ - ma_biquad_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - - return config; -} - -static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_biquad_node_vtable = -{ - ma_biquad_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->biquad.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_biquad_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->biquad.channels; - baseNodeConfig.pOutputChannels = &pConfig->biquad.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - MA_ASSERT(pNode != NULL); - - return ma_biquad_reinit(pConfig, &pLPFNode->biquad); -} - -MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks); -} - - - -/* -Low Pass Filter Node -*/ -MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_lpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_lpf_node_vtable = -{ - ma_lpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->lpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_lpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->lpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->lpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_lpf_reinit(pConfig, &pLPFNode->lpf); -} - -MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks); -} - - - -/* -High Pass Filter Node -*/ -MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_hpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_hpf_node_vtable = -{ - ma_hpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->hpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_hpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->hpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->hpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_hpf_reinit(pConfig, &pHPFNode->hpf); -} - -MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks); -} - - - - -/* -Band Pass Filter Node -*/ -MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_bpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_bpf_node_vtable = -{ - ma_bpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->bpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_bpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->bpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->bpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_bpf_reinit(pConfig, &pBPFNode->bpf); -} - -MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks); -} - - - -/* -Notching Filter Node -*/ -MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) -{ - ma_notch_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency); - - return config; -} - -static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_notch_node* pBPFNode = (ma_notch_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_notch_node_vtable = -{ - ma_notch_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->notch.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_notch_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->notch.channels; - baseNodeConfig.pOutputChannels = &pConfig->notch.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode) -{ - ma_notch_node* pNotchNode = (ma_notch_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_notch2_reinit(pConfig, &pNotchNode->notch); -} - -MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_notch_node* pNotchNode = (ma_notch_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks); -} - - - -/* -Peaking Filter Node -*/ -MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_peak_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_peak_node* pBPFNode = (ma_peak_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_peak_node_vtable = -{ - ma_peak_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->peak.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak); - if (result != MA_SUCCESS) { - ma_node_uninit(pNode, pAllocationCallbacks); - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_peak_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->peak.channels; - baseNodeConfig.pOutputChannels = &pConfig->peak.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode) -{ - ma_peak_node* pPeakNode = (ma_peak_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_peak2_reinit(pConfig, &pPeakNode->peak); -} - -MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_peak_node* pPeakNode = (ma_peak_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks); -} - - - -/* -Low Shelf Filter Node -*/ -MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_loshelf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_loshelf_node_vtable = -{ - ma_loshelf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->loshelf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_loshelf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->loshelf.channels; - baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode) -{ - ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf); -} - -MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks); -} - - - -/* -High Shelf Filter Node -*/ -MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_hishelf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_hishelf_node_vtable = -{ - ma_hishelf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->hishelf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_hishelf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->hishelf.channels; - baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode) -{ - ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf); -} - -MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks); -} - - - - -MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) -{ - ma_delay_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay); - - return config; -} - - -static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_delay_node* pDelayNode = (ma_delay_node*)pNode; - - (void)pFrameCountIn; - - ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_delay_node_vtable = -{ - ma_delay_node_process_pcm_frames, - NULL, - 1, /* 1 input channels. */ - 1, /* 1 output channel. */ - MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */ -}; - -MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode) -{ - ma_result result; - ma_node_config baseConfig; - - if (pDelayNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDelayNode); - - result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay); - if (result != MA_SUCCESS) { - return result; - } - - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_delay_node_vtable; - baseConfig.pInputChannels = &pConfig->delay.channels; - baseConfig.pOutputChannels = &pConfig->delay.channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode); - if (result != MA_SUCCESS) { - ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); - return result; - } - - return result; -} - -MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pDelayNode == NULL) { - return; - } - - /* The base node is always uninitialized first. */ - ma_node_uninit(pDelayNode, pAllocationCallbacks); - ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); -} - -MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_wet(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_wet(&pDelayNode->delay); -} - -MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_dry(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_dry(&pDelayNode->delay); -} - -MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_decay(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_decay(&pDelayNode->delay); -} -#endif /* MA_NO_NODE_GRAPH */ - - -#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) -/************************************************************************************************************************************************************** - -Engine - -**************************************************************************************************************************************************************/ -#define MA_SEEK_TARGET_NONE (~(ma_uint64)0) - -MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) -{ - ma_engine_node_config config; - - MA_ZERO_OBJECT(&config); - config.pEngine = pEngine; - config.type = type; - config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; - config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; - - return config; -} - - -static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) -{ - ma_bool32 isUpdateRequired = MA_FALSE; - float newPitch; - - MA_ASSERT(pEngineNode != NULL); - - newPitch = c89atomic_load_explicit_f32(&pEngineNode->pitch, c89atomic_memory_order_acquire); - - if (pEngineNode->oldPitch != newPitch) { - pEngineNode->oldPitch = newPitch; - isUpdateRequired = MA_TRUE; - } - - if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) { - pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch; - isUpdateRequired = MA_TRUE; - } - - if (isUpdateRequired) { - float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine); - ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch); - } -} - -static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode) -{ - MA_ASSERT(pEngineNode != NULL); - - /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ - return !c89atomic_load_explicit_32(&pEngineNode->isPitchDisabled, c89atomic_memory_order_acquire); -} - -static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) -{ - MA_ASSERT(pEngineNode != NULL); - - return !c89atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, c89atomic_memory_order_acquire); -} - -static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) -{ - ma_uint64 inputFrameCount = 0; - - if (ma_engine_node_is_pitching_enabled(pEngineNode)) { - ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount); - if (result != MA_SUCCESS) { - inputFrameCount = 0; - } - } else { - inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */ - } - - return inputFrameCount; -} - -static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - ma_uint32 totalFramesProcessedIn; - ma_uint32 totalFramesProcessedOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_bool32 isPitchingEnabled; - ma_bool32 isFadingEnabled; - ma_bool32 isSpatializationEnabled; - ma_bool32 isPanningEnabled; - - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - - channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer); - channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer); - - totalFramesProcessedIn = 0; - totalFramesProcessedOut = 0; - - isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); - isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; - isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); - isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; - - /* Keep going while we've still got data available for processing. */ - while (totalFramesProcessedOut < frameCountOut) { - /* - We need to process in a specific order. We always do resampling first because it's likely - we're going to be increasing the channel count after spatialization. Also, I want to do - fading based on the output sample rate. - - We'll first read into a buffer from the resampler. Then we'll do all processing that - operates on the on the input channel count. We'll then get the spatializer to output to - the output buffer and then do all effects from that point directly in the output buffer - in-place. - - Note that we're always running the resampler. If we try to be clever and skip resampling - when the pitch is 1, we'll get a glitch when we move away from 1, back to 1, and then - away from 1 again. We'll want to implement any pitch=1 optimizations in the resampler - itself. - - There's a small optimization here that we'll utilize since it might be a fairly common - case. When the input and output channel counts are the same, we'll read straight into the - output buffer from the resampler and do everything in-place. - */ - const float* pRunningFramesIn; - float* pRunningFramesOut; - float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */ - float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; - ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn; - ma_uint32 framesAvailableIn; - ma_uint32 framesAvailableOut; - ma_uint32 framesJustProcessedIn; - ma_uint32 framesJustProcessedOut; - ma_bool32 isWorkingBufferValid = MA_FALSE; - - framesAvailableIn = frameCountIn - totalFramesProcessedIn; - framesAvailableOut = frameCountOut - totalFramesProcessedOut; - - pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn); - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut); - - if (channelsIn == channelsOut) { - /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */ - pWorkingBuffer = pRunningFramesOut; - } else { - /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */ - pWorkingBuffer = temp; - if (framesAvailableOut > tempCapInFrames) { - framesAvailableOut = tempCapInFrames; - } - } - - /* First is resampler. */ - if (isPitchingEnabled) { - ma_uint64 resampleFrameCountIn = framesAvailableIn; - ma_uint64 resampleFrameCountOut = framesAvailableOut; - - ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut); - isWorkingBufferValid = MA_TRUE; - - framesJustProcessedIn = (ma_uint32)resampleFrameCountIn; - framesJustProcessedOut = (ma_uint32)resampleFrameCountOut; - } else { - framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut); - framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */ - } - - /* Fading. */ - if (isFadingEnabled) { - if (isWorkingBufferValid) { - ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */ - } else { - ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); - isWorkingBufferValid = MA_TRUE; - } - } - - /* - If at this point we still haven't actually done anything with the working buffer we need - to just read straight from the input buffer. - */ - if (isWorkingBufferValid == MA_FALSE) { - pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */ - } - - /* Spatialization. */ - if (isSpatializationEnabled) { - ma_uint32 iListener; - - /* - When determining the listener to use, we first check to see if the sound is pinned to a - specific listener. If so, we use that. Otherwise we just use the closest listener. - */ - if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { - iListener = pEngineNode->pinnedListenerIndex; - } else { - iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, pEngineNode->spatializer.position.x, pEngineNode->spatializer.position.y, pEngineNode->spatializer.position.z); - } - - ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); - } else { - /* No spatialization, but we still need to do channel conversion. */ - if (channelsIn == channelsOut) { - /* No channel conversion required. Just copy straight to the output buffer. */ - ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut, ma_format_f32, channelsOut); - } else { - /* Channel conversion required. TODO: Add support for channel maps here. */ - ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->pEngine->monoExpansionMode); - } - } - - /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */ - - /* Panning. */ - if (isPanningEnabled) { - ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */ - } - - /* We're done for this chunk. */ - totalFramesProcessedIn += framesJustProcessedIn; - totalFramesProcessedOut += framesJustProcessedOut; - - /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */ - if (framesJustProcessedOut == 0) { - break; - } - } - - /* At this point we're done processing. */ - *pFrameCountIn = totalFramesProcessedIn; - *pFrameCountOut = totalFramesProcessedOut; -} - -static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */ - ma_result result = MA_SUCCESS; - ma_sound* pSound = (ma_sound*)pNode; - ma_uint32 frameCount = *pFrameCountOut; - ma_uint32 totalFramesRead = 0; - ma_format dataSourceFormat; - ma_uint32 dataSourceChannels; - ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 tempCapInFrames; - ma_uint64 seekTarget; - - /* This is a data source node which means no input buses. */ - (void)ppFramesIn; - (void)pFrameCountIn; - - /* If we're marked at the end we need to stop the sound and do nothing. */ - if (ma_sound_at_end(pSound)) { - ma_sound_stop(pSound); - *pFrameCountOut = 0; - return; - } - - /* If we're seeking, do so now before reading. */ - seekTarget = c89atomic_load_64(&pSound->seekTarget); - if (seekTarget != MA_SEEK_TARGET_NONE) { - ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); - - /* Any time-dependant effects need to have their times updated. */ - ma_node_set_time(pSound, seekTarget); - - c89atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); - } - - /* - We want to update the pitch once. For sounds, this can be either at the start or at the end. If - we don't force this to only ever be updating once, we could end up in a situation where - retrieving the required input frame count ends up being different to what we actually retrieve. - What could happen is that the required input frame count is calculated, the pitch is update, - and then this processing function is called resulting in a different number of input frames - being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else - you'll hit the aforementioned bug. - */ - ma_engine_node_update_pitch_if_required(&pSound->engineNode); - - /* - For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ - from the main engine. - */ - result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0); - if (result == MA_SUCCESS) { - tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); - - /* Keep reading until we've read as much as was requested or we reach the end of the data source. */ - while (totalFramesRead < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesRead; - ma_uint32 framesToRead; - ma_uint64 framesJustRead; - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - const float* pRunningFramesIn; - float* pRunningFramesOut; - - /* - The first thing we need to do is read into the temporary buffer. We can calculate exactly - how many input frames we'll need after resampling. - */ - framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining); - if (framesToRead > tempCapInFrames) { - framesToRead = tempCapInFrames; - } - - result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead); - - /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ - if (result == MA_AT_END) { - c89atomic_exchange_32(&pSound->atEnd, MA_TRUE); /* This will be set to false in ma_sound_start(). */ - } - - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound))); - - frameCountIn = (ma_uint32)framesJustRead; - frameCountOut = framesRemaining; - - /* Convert if necessary. */ - if (dataSourceFormat == ma_format_f32) { - /* Fast path. No data conversion necessary. */ - pRunningFramesIn = (float*)temp; - ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); - } else { - /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */ - float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */ - ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); - - /* Now that we have our samples in f32 format we can process like normal. */ - pRunningFramesIn = tempf32; - ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); - } - - /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */ - MA_ASSERT(frameCountIn == framesJustRead); - totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */ - - if (result != MA_SUCCESS || ma_sound_at_end(pSound)) { - break; /* Might have reached the end. */ - } - } - } - - *pFrameCountOut = totalFramesRead; -} - -static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - /* - Make sure the pitch is updated before trying to read anything. It's important that this is done - only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that - ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(), - and if another thread modifies the pitch just after that call it can result in a glitch due to - the input rate changing. - */ - ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); - - /* For groups, the input data has already been read and we just need to apply the effect. */ - ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); -} - -static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount) -{ - ma_uint64 inputFrameCount; - - MA_ASSERT(pInputFrameCount != NULL); - - /* Our pitch will affect this calculation. We need to update it. */ - ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); - - inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount); - if (inputFrameCount > 0xFFFFFFFF) { - inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */ - } - - *pInputFrameCount = (ma_uint32)inputFrameCount; - - return MA_SUCCESS; -} - - -static ma_node_vtable g_ma_engine_node_vtable__sound = -{ - ma_engine_node_process_pcm_frames__sound, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */ - 1, /* Sounds have one output bus. */ - 0 /* Default flags. */ -}; - -static ma_node_vtable g_ma_engine_node_vtable__group = -{ - ma_engine_node_process_pcm_frames__group, - ma_engine_node_get_required_input_frame_count__group, - 1, /* Groups have one input bus. */ - 1, /* Groups have one output bus. */ - MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */ -}; - - - -static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig) -{ - ma_node_config baseNodeConfig; - - if (pConfig->type == ma_engine_node_type_sound) { - /* Sound. */ - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound; - baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */ - } else { - /* Group. */ - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_engine_node_vtable__group; - baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */ - } - - return baseNodeConfig; -} - -static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig) -{ - return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]); -} - -typedef struct -{ - size_t sizeInBytes; - size_t baseNodeOffset; - size_t resamplerOffset; - size_t spatializerOffset; -} ma_engine_node_heap_layout; - -static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) -{ - ma_result result; - size_t tempHeapSize; - ma_node_config baseNodeConfig; - ma_linear_resampler_config resamplerConfig; - ma_spatializer_config spatializerConfig; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - - MA_ASSERT(pHeapLayout); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pEngine == NULL) { - return MA_INVALID_ARGS; /* An engine must be specified. */ - } - - pHeapLayout->sizeInBytes = 0; - - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); - channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); - - - /* Base node. */ - baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); - baseNodeConfig.pInputChannels = &channelsIn; - baseNodeConfig.pOutputChannels = &channelsOut; - - result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the base node. */ - } - - pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Resmapler. */ - resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ - resamplerConfig.lpfOrder = 0; - - result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the resampler. */ - } - - pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Spatializer. */ - spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); - - result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the spatializer. */ - } - - pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_engine_node_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode) -{ - ma_result result; - ma_engine_node_heap_layout heapLayout; - ma_node_config baseNodeConfig; - ma_linear_resampler_config resamplerConfig; - ma_fader_config faderConfig; - ma_spatializer_config spatializerConfig; - ma_panner_config pannerConfig; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEngineNode); - - result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) { - return MA_INVALID_ARGS; /* Invalid listener. */ - } - - pEngineNode->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pEngineNode->pEngine = pConfig->pEngine; - pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); - pEngineNode->pitch = 1; - pEngineNode->oldPitch = 1; - pEngineNode->oldDopplerPitch = 1; - pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; - pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; - pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; - - - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); - channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); - - - /* Base node. */ - baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); - baseNodeConfig.pInputChannels = &channelsIn; - baseNodeConfig.pOutputChannels = &channelsOut; - - result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode); - if (result != MA_SUCCESS) { - goto error0; - } - - - /* - We can now initialize the effects we need in order to implement the engine node. There's a - defined order of operations here, mainly centered around when we convert our channels from the - data source's native channel count to the engine's channel count. As a rule, we want to do as - much computation as possible before spatialization because there's a chance that will increase - the channel count, thereby increasing the amount of work needing to be done to process. - */ - - /* We'll always do resampling first. */ - resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine)); - resamplerConfig.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */ - - result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler); - if (result != MA_SUCCESS) { - goto error1; - } - - - /* After resampling will come the fader. */ - faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine)); - - result = ma_fader_init(&faderConfig, &pEngineNode->fader); - if (result != MA_SUCCESS) { - goto error2; - } - - - /* - Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to - ensure channels counts link up correctly in the node graph. - */ - spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); - spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; - - result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); - if (result != MA_SUCCESS) { - goto error2; - } - - - /* - After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't - be able to pan mono sounds. - */ - pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]); - - result = ma_panner_init(&pannerConfig, &pEngineNode->panner); - if (result != MA_SUCCESS) { - goto error3; - } - - return MA_SUCCESS; - - /* No need for allocation callbacks here because we use a preallocated heap. */ -error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); -error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL); -error1: ma_node_uninit(&pEngineNode->baseNode, NULL); -error0: return result; -} - -MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pEngineNode->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - /* - The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we - destroy anything that might be in the middle of being used by the processing function. - */ - ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); - - /* Now that the node has been uninitialized we can safely uninitialize the rest. */ - ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); - ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); - - /* Free the heap last. */ - if (pEngineNode->_ownsHeap) { - ma_free(pEngineNode->_pHeap, pAllocationCallbacks); - } -} - - -MA_API ma_sound_config ma_sound_config_init(void) -{ - ma_sound_config config; - - MA_ZERO_OBJECT(&config); - config.rangeEndInPCMFrames = ~((ma_uint64)0); - config.loopPointEndInPCMFrames = ~((ma_uint64)0); - - return config; -} - -MA_API ma_sound_group_config ma_sound_group_config_init(void) -{ - ma_sound_group_config config; - - MA_ZERO_OBJECT(&config); - - return config; -} - - -MA_API ma_engine_config ma_engine_config_init(void) -{ - ma_engine_config config; - - MA_ZERO_OBJECT(&config); - config.listenerCount = 1; /* Always want at least one listener. */ - config.monoExpansionMode = ma_mono_expansion_mode_default; - - return config; -} - - -#if !defined(MA_NO_DEVICE_IO) -static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - ma_engine* pEngine = (ma_engine*)pDevice->pUserData; - - (void)pFramesIn; - - /* - Experiment: Try processing a resource manager job if we're on the Emscripten build. - - This serves two purposes: - - 1) It ensures jobs are actually processed at some point since we cannot guarantee that the - caller is doing the right thing and calling ma_resource_manager_process_next_job(); and - - 2) It's an attempt at working around an issue where processing jobs on the Emscripten main - loop doesn't work as well as it should. When trying to load sounds without the `DECODE` - flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time - before the callback is processed. I think it's got something to do with the single- - threaded nature of Web, but I'm not entirely sure. - */ - #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN) - { - if (pEngine->pResourceManager != NULL) { - if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { - ma_resource_manager_process_next_job(pEngine->pResourceManager); - } - } - } - #endif - - ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); -} -#endif - -MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) -{ - ma_result result; - ma_node_graph_config nodeGraphConfig; - ma_engine_config engineConfig; - ma_spatializer_listener_config listenerConfig; - ma_uint32 iListener; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEngine); - - /* The config is allowed to be NULL in which case we use defaults for everything. */ - if (pConfig != NULL) { - engineConfig = *pConfig; - } else { - engineConfig = ma_engine_config_init(); - } - - pEngine->monoExpansionMode = engineConfig.monoExpansionMode; - ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); - - #if !defined(MA_NO_RESOURCE_MANAGER) - { - pEngine->pResourceManager = engineConfig.pResourceManager; - } - #endif - - #if !defined(MA_NO_DEVICE_IO) - { - pEngine->pDevice = engineConfig.pDevice; - - /* If we don't have a device, we need one. */ - if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { - ma_device_config deviceConfig; - - pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks); - if (pEngine->pDevice == NULL) { - return MA_OUT_OF_MEMORY; - } - - deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID; - deviceConfig.playback.format = ma_format_f32; - deviceConfig.playback.channels = engineConfig.channels; - deviceConfig.sampleRate = engineConfig.sampleRate; - deviceConfig.dataCallback = ma_engine_data_callback_internal; - deviceConfig.pUserData = pEngine; - deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; - deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; - deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ - deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */ - - if (engineConfig.pContext == NULL) { - ma_context_config contextConfig = ma_context_config_init(); - contextConfig.allocationCallbacks = pEngine->allocationCallbacks; - contextConfig.pLog = engineConfig.pLog; - - /* If the engine config does not specify a log, use the resource manager's if we have one. */ - #ifndef MA_NO_RESOURCE_MANAGER - { - if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) { - contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager); - } - } - #endif - - result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice); - } else { - result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice); - } - - if (result != MA_SUCCESS) { - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - pEngine->pDevice = NULL; - return result; - } - - pEngine->ownsDevice = MA_TRUE; - } - - /* Update the channel count and sample rate of the engine config so we can reference it below. */ - if (pEngine->pDevice != NULL) { - engineConfig.channels = pEngine->pDevice->playback.channels; - engineConfig.sampleRate = pEngine->pDevice->sampleRate; - } - } - #endif - - if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) { - return MA_INVALID_ARGS; - } - - pEngine->sampleRate = engineConfig.sampleRate; - - /* The engine always uses either the log that was passed into the config, or the context's log is available. */ - if (engineConfig.pLog != NULL) { - pEngine->pLog = engineConfig.pLog; - } else { - #if !defined(MA_NO_DEVICE_IO) - { - pEngine->pLog = ma_device_get_log(pEngine->pDevice); - } - #else - { - pEngine->pLog = NULL; - } - #endif - } - - - /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */ - nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); - nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames; - - result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); - if (result != MA_SUCCESS) { - goto on_error_1; - } - - - /* We need at least one listener. */ - if (engineConfig.listenerCount == 0) { - engineConfig.listenerCount = 1; - } - - if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) { - result = MA_INVALID_ARGS; /* Too many listeners. */ - goto on_error_1; - } - - for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) { - listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph)); - - /* - If we're using a device, use the device's channel map for the listener. Otherwise just use - miniaudio's default channel map. - */ - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - /* - Temporarily disabled. There is a subtle bug here where front-left and front-right - will be used by the device's channel map, but this is not what we want to use for - spatialization. Instead we want to use side-left and side-right. I need to figure - out a better solution for this. For now, disabling the user of device channel maps. - */ - /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ - } - } - #endif - - result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */ - if (result != MA_SUCCESS) { - goto on_error_2; - } - - pEngine->listenerCount += 1; - } - - - /* Gain smoothing for spatialized sounds. */ - pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames; - if (pEngine->gainSmoothTimeInFrames == 0) { - ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds; - if (gainSmoothTimeInMilliseconds == 0) { - gainSmoothTimeInMilliseconds = 8; - } - - pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */ - } - - - /* We need a resource manager. */ - #ifndef MA_NO_RESOURCE_MANAGER - { - if (pEngine->pResourceManager == NULL) { - ma_resource_manager_config resourceManagerConfig; - - pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks); - if (pEngine->pResourceManager == NULL) { - result = MA_OUT_OF_MEMORY; - goto on_error_2; - } - - resourceManagerConfig = ma_resource_manager_config_init(); - resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */ - resourceManagerConfig.decodedFormat = ma_format_f32; - resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */ - resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine); - ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); - resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; - - /* The Emscripten build cannot use threads. */ - #if defined(MA_EMSCRIPTEN) - { - resourceManagerConfig.jobThreadCount = 0; - resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - } - #endif - - result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager); - if (result != MA_SUCCESS) { - goto on_error_3; - } - - pEngine->ownsResourceManager = MA_TRUE; - } - } - #endif - - /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */ - pEngine->inlinedSoundLock = 0; - pEngine->pInlinedSoundHead = NULL; - - /* Start the engine if required. This should always be the last step. */ - #if !defined(MA_NO_DEVICE_IO) - { - if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) { - result = ma_engine_start(pEngine); - if (result != MA_SUCCESS) { - goto on_error_4; /* Failed to start the engine. */ - } - } - } - #endif - - return MA_SUCCESS; - -#if !defined(MA_NO_DEVICE_IO) -on_error_4: -#endif -#if !defined(MA_NO_RESOURCE_MANAGER) -on_error_3: - if (pEngine->ownsResourceManager) { - ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); - } -#endif /* MA_NO_RESOURCE_MANAGER */ -on_error_2: - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); - } - - ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); -on_error_1: - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->ownsDevice) { - ma_device_uninit(pEngine->pDevice); - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - } - } - #endif - - return result; -} - -MA_API void ma_engine_uninit(ma_engine* pEngine) -{ - ma_uint32 iListener; - - if (pEngine == NULL) { - return; - } - - /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */ - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->ownsDevice) { - ma_device_uninit(pEngine->pDevice); - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - } else { - if (pEngine->pDevice != NULL) { - ma_device_stop(pEngine->pDevice); - } - } - } - #endif - - /* - All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case - I want to do some kind of garbage collection later on. - */ - ma_spinlock_lock(&pEngine->inlinedSoundLock); - { - for (;;) { - ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead; - if (pSoundToDelete == NULL) { - break; /* Done. */ - } - - pEngine->pInlinedSoundHead = pSoundToDelete->pNext; - - ma_sound_uninit(&pSoundToDelete->sound); - ma_free(pSoundToDelete, &pEngine->allocationCallbacks); - } - } - ma_spinlock_unlock(&pEngine->inlinedSoundLock); - - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); - } - - /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */ - ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); - - /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pEngine->ownsResourceManager) { - ma_resource_manager_uninit(pEngine->pResourceManager); - ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); - } -#endif -} - -MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, pFramesRead); -} - -MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - return &pEngine->nodeGraph; -} - -#if !defined(MA_NO_RESOURCE_MANAGER) -MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - #if !defined(MA_NO_RESOURCE_MANAGER) - { - return pEngine->pResourceManager; - } - #else - { - return NULL; - } - #endif -} -#endif - -MA_API ma_device* ma_engine_get_device(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - #if !defined(MA_NO_DEVICE_IO) - { - return pEngine->pDevice; - } - #else - { - return NULL; - } - #endif -} - -MA_API ma_log* ma_engine_get_log(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - if (pEngine->pLog != NULL) { - return pEngine->pLog; - } else { - #if !defined(MA_NO_DEVICE_IO) - { - return ma_device_get_log(ma_engine_get_device(pEngine)); - } - #else - { - return NULL; - } - #endif - } -} - -MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) -{ - return ma_node_graph_get_endpoint(&pEngine->nodeGraph); -} - -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) -{ - return ma_node_graph_get_time(&pEngine->nodeGraph); -} - -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); -} - -MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) -{ - return ma_node_graph_get_channels(&pEngine->nodeGraph); -} - -MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return pEngine->sampleRate; -} - - -MA_API ma_result ma_engine_start(ma_engine* pEngine) -{ - ma_result result; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - result = ma_device_start(pEngine->pDevice); - } else { - result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */ - } - } - #else - { - result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */ - } - #endif - - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_stop(ma_engine* pEngine) -{ - ma_result result; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - result = ma_device_stop(pEngine->pDevice); - } else { - result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */ - } - } - #else - { - result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */ - } - #endif - - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) -{ - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); -} - -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) -{ - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, ma_volume_db_to_linear(gainDB)); -} - - -MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return pEngine->listenerCount; -} - -MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ) -{ - ma_uint32 iListener; - ma_uint32 iListenerClosest; - float closestLen2 = MA_FLT_MAX; - - if (pEngine == NULL || pEngine->listenerCount == 1) { - return 0; - } - - iListenerClosest = 0; - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - if (ma_engine_listener_is_enabled(pEngine, iListener)) { - float len2 = ma_vec3f_len2(ma_vec3f_sub(pEngine->listeners[iListener].position, ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); - if (closestLen2 > len2) { - closestLen2 = len2; - iListenerClosest = iListener; - } - } - } - - MA_ASSERT(iListenerClosest < 255); - return iListenerClosest; -} - -MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = 0; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = 0; - } - - if (pOuterGain != NULL) { - *pOuterGain = 0; - } - - ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 1, 0); - } - - return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled); -} - -MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return MA_FALSE; - } - - return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]); -} - - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex) -{ - ma_result result = MA_SUCCESS; - ma_sound_inlined* pSound = NULL; - ma_sound_inlined* pNextSound = NULL; - - if (pEngine == NULL || pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - /* Attach to the endpoint node if nothing is specicied. */ - if (pNode == NULL) { - pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); - nodeInputBusIndex = 0; - } - - /* - We want to check if we can recycle an already-allocated inlined sound. Since this is just a - helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep - the implementation simple. Maybe this can be optimized later if there's enough demand, but - if this function is being used it probably means the caller doesn't really care too much. - - What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise - we just keep iterating. If we reach the end without finding a sound to recycle we just - allocate a new one. This doesn't scale well for a massive number of sounds being played - simultaneously as we don't ever actually free the sound objects. Some kind of garbage - collection routine might be valuable for this which I'll think about. - */ - ma_spinlock_lock(&pEngine->inlinedSoundLock); - { - ma_uint32 soundFlags = 0; - - for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) { - if (ma_sound_at_end(&pNextSound->sound)) { - /* - The sound is at the end which means it's available for recycling. All we need to do - is uninitialize it and reinitialize it. All we're doing is recycling memory. - */ - pSound = pNextSound; - c89atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); - break; - } - } - - if (pSound != NULL) { - /* - We actually want to detach the sound from the list here. The reason is because we want the sound - to be in a consistent state at the non-recycled case to simplify the logic below. - */ - if (pEngine->pInlinedSoundHead == pSound) { - pEngine->pInlinedSoundHead = pSound->pNext; - } - - if (pSound->pPrev != NULL) { - pSound->pPrev->pNext = pSound->pNext; - } - if (pSound->pNext != NULL) { - pSound->pNext->pPrev = pSound->pPrev; - } - - /* Now the previous sound needs to be uninitialized. */ - ma_sound_uninit(&pNextSound->sound); - } else { - /* No sound available for recycling. Allocate one now. */ - pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks); - } - - if (pSound != NULL) { /* Safety check for the allocation above. */ - /* - At this point we should have memory allocated for the inlined sound. We just need - to initialize it like a normal sound now. - */ - soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */ - soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */ - soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ - soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ - - result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound); - if (result == MA_SUCCESS) { - /* Now attach the sound to the graph. */ - result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); - if (result == MA_SUCCESS) { - /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */ - pSound->pNext = pEngine->pInlinedSoundHead; - pSound->pPrev = NULL; - - pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */ - if (pSound->pNext != NULL) { - pSound->pNext->pPrev = pSound; - } - } else { - ma_free(pSound, &pEngine->allocationCallbacks); - } - } else { - ma_free(pSound, &pEngine->allocationCallbacks); - } - } else { - result = MA_OUT_OF_MEMORY; - } - } - ma_spinlock_unlock(&pEngine->inlinedSoundLock); - - if (result != MA_SUCCESS) { - return result; - } - - /* Finally we can start playing the sound. */ - result = ma_sound_start(&pSound->sound); - if (result != MA_SUCCESS) { - /* Failed to start the sound. We need to mark it for recycling and return an error. */ - c89atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); - return result; - } - - c89atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); - return result; -} - -MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup) -{ - return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0); -} -#endif - - -static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSound); - pSound->seekTarget = MA_SEEK_TARGET_NONE; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result; - ma_engine_node_config engineNodeConfig; - ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */ - - /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */ - MA_ASSERT(pEngine != NULL); - MA_ASSERT(pSound != NULL); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pSound->pDataSource = pConfig->pDataSource; - - if (pConfig->pDataSource != NULL) { - type = ma_engine_node_type_sound; - } else { - type = ma_engine_node_type_group; - } - - /* - Sounds are engine nodes. Before we can initialize this we need to determine the channel count. - If we can't do this we need to abort. It's up to the caller to ensure they're using a data - source that provides this information upfront. - */ - engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); - engineNodeConfig.channelsIn = pConfig->channelsIn; - engineNodeConfig.channelsOut = pConfig->channelsOut; - - /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ - if (pConfig->pDataSource != NULL) { - result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the channel count. */ - } - - if (engineNodeConfig.channelsIn == 0) { - return MA_INVALID_OPERATION; /* Invalid channel count. */ - } - - if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) { - engineNodeConfig.channelsOut = engineNodeConfig.channelsIn; - } - } - - - /* Getting here means we should have a valid channel count and we can initialize the engine node. */ - result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode); - if (result != MA_SUCCESS) { - return result; - } - - /* If no attachment is specified, attach the sound straight to the endpoint. */ - if (pConfig->pInitialAttachment == NULL) { - /* No group. Attach straight to the endpoint by default, unless the caller has requested that do not. */ - if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { - result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); - } - } else { - /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */ - result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex); - } - - if (result != MA_SUCCESS) { - ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks); - return result; - } - - - /* Apply initial range and looping state to the data source if applicable. */ - if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - } - - if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - } - - ma_sound_set_looping(pSound, pConfig->isLooping); - - return MA_SUCCESS; -} - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result = MA_SUCCESS; - ma_uint32 flags; - ma_sound_config config; - ma_resource_manager_pipeline_notifications notifications; - - /* - The engine requires knowledge of the channel count of the underlying data source before it can - initialize the sound. Therefore, we need to make the resource manager wait until initialization - of the underlying data source to be initialized so we can get access to the channel count. To - do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced. - - Because we're initializing the data source before the sound, there's a chance the notification - will get triggered before this function returns. This is OK, so long as the caller is aware of - it and can avoid accessing the sound from within the notification. - */ - flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; - - pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); - if (pSound->pResourceManagerDataSource == NULL) { - return MA_OUT_OF_MEMORY; - } - - notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pFence = pConfig->pDoneFence; - - /* - We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does - not return prematurely before the sound has finished initializing. - */ - if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); } - { - ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init(); - resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath; - resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW; - resourceManagerDataSourceConfig.flags = flags; - resourceManagerDataSourceConfig.pNotifications = ¬ifications; - resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames; - resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; - resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; - resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; - resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - resourceManagerDataSourceConfig.isLooping = pConfig->isLooping; - - result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); - if (result != MA_SUCCESS) { - goto done; - } - - pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ - - /* We need to use a slightly customized version of the config so we'll need to make a copy. */ - config = *pConfig; - config.pFilePath = NULL; - config.pFilePathW = NULL; - config.pDataSource = pSound->pResourceManagerDataSource; - - result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); - if (result != MA_SUCCESS) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - MA_ZERO_OBJECT(pSound); - goto done; - } - } -done: - if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); } - return result; -} - -MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) -{ - ma_sound_config config = ma_sound_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.pDoneFence = pDoneFence; - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) -{ - ma_sound_config config = ma_sound_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.pDoneFence = pDoneFence; - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) -{ - ma_result result; - ma_sound_config config; - - result = ma_sound_preinit(pEngine, pSound); - if (result != MA_SUCCESS) { - return result; - } - - if (pExistingSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */ - if (pExistingSound->pResourceManagerDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - /* - We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) - the this will fail. - */ - pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); - if (pSound->pResourceManagerDataSource == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource); - if (result != MA_SUCCESS) { - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - return result; - } - - config = ma_sound_config_init(); - config.pDataSource = pSound->pResourceManagerDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - - result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); - if (result != MA_SUCCESS) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - MA_ZERO_OBJECT(pSound); - return result; - } - - return MA_SUCCESS; -} -#endif - -MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) -{ - ma_sound_config config = ma_sound_config_init(); - config.pDataSource = pDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result; - - result = ma_sound_preinit(pEngine, pSound); - if (result != MA_SUCCESS) { - return result; - } - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* We need to load the sound differently depending on whether or not we're loading from a file. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { - return ma_sound_init_from_file_internal(pEngine, pConfig, pSound); - } else -#endif - { - /* - Getting here means we're not loading from a file. We may be loading from an already-initialized - data source, or none at all. If we aren't specifying any data source, we'll be initializing the - the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this - for us, so no special treatment required here. - */ - return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound); - } -} - -MA_API void ma_sound_uninit(ma_sound* pSound) -{ - if (pSound == NULL) { - return; - } - - /* - Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done - so which makes thread safety beyond this point trivial. - */ - ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks); - - /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pSound->ownsDataSource) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks); - pSound->pDataSource = NULL; - } -#else - MA_ASSERT(pSound->ownsDataSource == MA_FALSE); -#endif -} - -MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound) -{ - if (pSound == NULL) { - return NULL; - } - - return pSound->engineNode.pEngine; -} - -MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound) -{ - if (pSound == NULL) { - return NULL; - } - - return pSound->pDataSource; -} - -MA_API ma_result ma_sound_start(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* If the sound is already playing, do nothing. */ - if (ma_sound_is_playing(pSound)) { - return MA_SUCCESS; - } - - /* If the sound is at the end it means we want to start from the start again. */ - if (ma_sound_at_end(pSound)) { - ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0); - if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) { - return result; /* Failed to seek back to the start. */ - } - - /* Make sure we clear the end indicator. */ - c89atomic_exchange_32(&pSound->atEnd, MA_FALSE); - } - - /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ - ma_node_set_state(pSound, ma_node_state_started); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */ - ma_node_set_state(pSound, ma_node_state_stopped); - - return MA_SUCCESS; -} - -MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) -{ - if (pSound == NULL) { - return; - } - - /* The volume is controlled via the output bus. */ - ma_node_set_output_bus_volume(pSound, 0, volume); -} - -MA_API float ma_sound_get_volume(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_node_get_output_bus_volume(pSound, 0); -} - -MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) -{ - if (pSound == NULL) { - return; - } - - ma_panner_set_pan(&pSound->engineNode.panner, pan); -} - -MA_API float ma_sound_get_pan(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_panner_get_pan(&pSound->engineNode.panner); -} - -MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode) -{ - if (pSound == NULL) { - return; - } - - ma_panner_set_mode(&pSound->engineNode.panner, panMode); -} - -MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_pan_mode_balance; - } - - return ma_panner_get_mode(&pSound->engineNode.panner); -} - -MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) -{ - if (pSound == NULL) { - return; - } - - if (pitch <= 0) { - return; - } - - c89atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, c89atomic_memory_order_release); -} - -MA_API float ma_sound_get_pitch(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return c89atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ -} - -MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) -{ - if (pSound == NULL) { - return; - } - - c89atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, c89atomic_memory_order_release); -} - -MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - return ma_engine_node_is_spatialization_enabled(&pSound->engineNode); -} - -MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex) -{ - if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) { - return; - } - - c89atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, c89atomic_memory_order_release); -} - -MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_LISTENER_INDEX_CLOSEST; - } - - return c89atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, c89atomic_memory_order_acquire); -} - -MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) -{ - ma_uint32 listenerIndex; - - if (pSound == NULL) { - return 0; - } - - listenerIndex = ma_sound_get_pinned_listener_index(pSound); - if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { - ma_vec3f position = ma_sound_get_position(pSound); - return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); - } - - return listenerIndex; -} - -MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) -{ - ma_vec3f relativePos; - ma_engine* pEngine; - - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - pEngine = ma_sound_get_engine(pSound); - if (pEngine == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); - - return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); -} - -MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_position(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_direction(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_velocity(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel); -} - -MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_attenuation_model_none; - } - - return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning); -} - -MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_positioning_absolute; - } - - return ma_spatializer_get_positioning(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff); -} - -MA_API float ma_sound_get_rolloff(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain); -} - -MA_API float ma_sound_get_min_gain(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain); -} - -MA_API float ma_sound_get_max_gain(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance); -} - -MA_API float ma_sound_get_min_distance(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance); -} - -MA_API float ma_sound_get_max_distance(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = 0; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = 0; - } - - if (pOuterGain != NULL) { - *pOuterGain = 0; - } - - ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor); -} - -MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); -} - -MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 1; - } - - return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); -} - - -MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_fader_set_fade(&pSound->engineNode.fader, volumeBeg, volumeEnd, fadeLengthInFrames); -} - -MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); -} - -MA_API float ma_sound_get_current_fade_volume(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - return ma_fader_get_current_volume(&pSound->engineNode.fader); -} - -MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); -} - -MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_node_set_state_time(pSound, ma_node_state_stopped, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); -} - -MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - return ma_node_get_state_by_time(pSound, ma_engine_get_time(ma_sound_get_engine(pSound))) == ma_node_state_started; -} - -MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_node_get_time(pSound); -} - -MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) -{ - if (pSound == NULL) { - return; - } - - /* Looping is only a valid concept if the sound is backed by a data source. */ - if (pSound->pDataSource == NULL) { - return; - } - - /* The looping state needs to be applied to the data source in order for any looping to actually happen. */ - ma_data_source_set_looping(pSound->pDataSource, isLooping); -} - -MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - /* There is no notion of looping for sounds that are not backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_FALSE; - } - - return ma_data_source_is_looping(pSound->pDataSource); -} - -MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - /* There is no notion of an end of a sound if it's not backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_FALSE; - } - - return c89atomic_load_32(&pSound->atEnd); -} - -MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Seeking is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ - c89atomic_exchange_64(&pSound->seekTarget, frameIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */ - if (pSound->pDataSource == NULL) { - ma_uint32 channels; - - if (pFormat != NULL) { - *pFormat = ma_format_f32; - } - - channels = ma_node_get_input_channels(&pSound->engineNode, 0); - if (pChannels != NULL) { - *pChannels = channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels); - } - - return MA_SUCCESS; - } else { - return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } -} - -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a cursor is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); -} - -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a sound length is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); -} - -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a cursor is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_cursor_in_seconds(pSound->pDataSource, pCursor); -} - -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a sound length is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); -} - - -MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) -{ - ma_sound_group_config config = ma_sound_group_config_init(); - config.flags = flags; - config.pInitialAttachment = pParentGroup; - return ma_sound_group_init_ex(pEngine, &config, pGroup); -} - -MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup) -{ - ma_sound_config soundConfig; - - if (pGroup == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pGroup); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* A sound group is just a sound without a data source. */ - soundConfig = *pConfig; - soundConfig.pFilePath = NULL; - soundConfig.pFilePathW = NULL; - soundConfig.pDataSource = NULL; - - /* - Groups need to have spatialization disabled by default because I think it'll be pretty rare - that programs will want to spatialize groups (but not unheard of). Certainly it feels like - disabling this by default feels like the right option. Spatialization can be enabled with a - call to ma_sound_group_set_spatialization_enabled(). - */ - soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION; - - return ma_sound_init_ex(pEngine, &soundConfig, pGroup); -} - -MA_API void ma_sound_group_uninit(ma_sound_group* pGroup) -{ - ma_sound_uninit(pGroup); -} - -MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup) -{ - return ma_sound_get_engine(pGroup); -} - -MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup) -{ - return ma_sound_start(pGroup); -} - -MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup) -{ - return ma_sound_stop(pGroup); -} - -MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume) -{ - ma_sound_set_volume(pGroup, volume); -} - -MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup) -{ - return ma_sound_get_volume(pGroup); -} - -MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan) -{ - ma_sound_set_pan(pGroup, pan); -} - -MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup) -{ - return ma_sound_get_pan(pGroup); -} - -MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode) -{ - ma_sound_set_pan_mode(pGroup, panMode); -} - -MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup) -{ - return ma_sound_get_pan_mode(pGroup); -} - -MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch) -{ - ma_sound_set_pitch(pGroup, pitch); -} - -MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup) -{ - return ma_sound_get_pitch(pGroup); -} - -MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled) -{ - ma_sound_set_spatialization_enabled(pGroup, enabled); -} - -MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup) -{ - return ma_sound_is_spatialization_enabled(pGroup); -} - -MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex) -{ - ma_sound_set_pinned_listener_index(pGroup, listenerIndex); -} - -MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup) -{ - return ma_sound_get_pinned_listener_index(pGroup); -} - -MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) -{ - return ma_sound_get_listener_index(pGroup); -} - -MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) -{ - return ma_sound_get_direction_to_listener(pGroup); -} - -MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_position(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup) -{ - return ma_sound_get_position(pGroup); -} - -MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_direction(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup) -{ - return ma_sound_get_direction(pGroup); -} - -MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_velocity(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup) -{ - return ma_sound_get_velocity(pGroup); -} - -MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel) -{ - ma_sound_set_attenuation_model(pGroup, attenuationModel); -} - -MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup) -{ - return ma_sound_get_attenuation_model(pGroup); -} - -MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning) -{ - ma_sound_set_positioning(pGroup, positioning); -} - -MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup) -{ - return ma_sound_get_positioning(pGroup); -} - -MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff) -{ - ma_sound_set_rolloff(pGroup, rolloff); -} - -MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup) -{ - return ma_sound_get_rolloff(pGroup); -} - -MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain) -{ - ma_sound_set_min_gain(pGroup, minGain); -} - -MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup) -{ - return ma_sound_get_min_gain(pGroup); -} - -MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain) -{ - ma_sound_set_max_gain(pGroup, maxGain); -} - -MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup) -{ - return ma_sound_get_max_gain(pGroup); -} - -MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance) -{ - ma_sound_set_min_distance(pGroup, minDistance); -} - -MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup) -{ - return ma_sound_get_min_distance(pGroup); -} - -MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance) -{ - ma_sound_set_max_distance(pGroup, maxDistance); -} - -MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup) -{ - return ma_sound_get_max_distance(pGroup); -} - -MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor) -{ - ma_sound_set_doppler_factor(pGroup, dopplerFactor); -} - -MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) -{ - return ma_sound_get_doppler_factor(pGroup); -} - -MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) -{ - ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); -} - -MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) -{ - return ma_sound_get_directional_attenuation_factor(pGroup); -} - -MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) -{ - ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); -} - -MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) -{ - ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds); -} - -MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup) -{ - return ma_sound_get_current_fade_volume(pGroup); -} - -MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) -{ - ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); -} - -MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) -{ - ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); -} - -MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup) -{ - return ma_sound_is_playing(pGroup); -} - -MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup) -{ - return ma_sound_get_time_in_pcm_frames(pGroup); -} -#endif /* MA_NO_ENGINE */ - - - -/************************************************************************************************************************************************************** -*************************************************************************************************************************************************************** - -Auto Generated -============== -All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as dr_wav, dr_flac, etc. If you find a bug in the -code below please report the bug to the respective repository for the relevant project (probably dr_libs). - -*************************************************************************************************************************************************************** -**************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -#if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ -/* dr_wav_c begin */ -#ifndef dr_wav_c -#define dr_wav_c -#include -#include -#include -#ifndef DR_WAV_NO_STDIO -#include -#include -#endif -#ifndef DRWAV_ASSERT -#include -#define DRWAV_ASSERT(expression) assert(expression) -#endif -#ifndef DRWAV_MALLOC -#define DRWAV_MALLOC(sz) malloc((sz)) -#endif -#ifndef DRWAV_REALLOC -#define DRWAV_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef DRWAV_FREE -#define DRWAV_FREE(p) free((p)) -#endif -#ifndef DRWAV_COPY_MEMORY -#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef DRWAV_ZERO_MEMORY -#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef DRWAV_ZERO_OBJECT -#define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p)) -#endif -#define drwav_countof(x) (sizeof(x) / sizeof(x[0])) -#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -#define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) -#define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) -#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) -#define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset)) -#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 -#if defined(__x86_64__) || defined(_M_X64) - #define DRWAV_X64 -#elif defined(__i386) || defined(_M_IX86) - #define DRWAV_X86 -#elif defined(__arm__) || defined(_M_ARM) - #define DRWAV_ARM -#endif -#ifdef _MSC_VER - #define DRWAV_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define DRWAV_GNUC_INLINE_HINT __inline__ - #else - #define DRWAV_GNUC_INLINE_HINT inline - #endif - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline)) - #else - #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT - #endif -#elif defined(__WATCOMC__) - #define DRWAV_INLINE __inline -#else - #define DRWAV_INLINE -#endif -#if defined(SIZE_MAX) - #define DRWAV_SIZE_MAX SIZE_MAX -#else - #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) - #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF) - #else - #define DRWAV_SIZE_MAX 0xFFFFFFFF - #endif -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define DRWAV_HAS_BYTESWAP16_INTRINSIC - #define DRWAV_HAS_BYTESWAP32_INTRINSIC - #define DRWAV_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define DRWAV_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define DRWAV_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define DRWAV_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define DRWAV_HAS_BYTESWAP32_INTRINSIC - #define DRWAV_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define DRWAV_HAS_BYTESWAP16_INTRINSIC - #endif -#endif -DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision) -{ - if (pMajor) { - *pMajor = DRWAV_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = DRWAV_VERSION_MINOR; - } - if (pRevision) { - *pRevision = DRWAV_VERSION_REVISION; - } -} -DRWAV_API const char* drwav_version_string(void) -{ - return DRWAV_VERSION_STRING; -} -#ifndef DRWAV_MAX_SAMPLE_RATE -#define DRWAV_MAX_SAMPLE_RATE 384000 -#endif -#ifndef DRWAV_MAX_CHANNELS -#define DRWAV_MAX_CHANNELS 256 -#endif -#ifndef DRWAV_MAX_BITS_PER_SAMPLE -#define DRWAV_MAX_BITS_PER_SAMPLE 64 -#endif -static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; -static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static DRWAV_INLINE int drwav__is_little_endian(void) -{ -#if defined(DRWAV_X86) || defined(DRWAV_X64) - return DRWAV_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return DRWAV_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} -static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid) -{ - int i; - for (i = 0; i < 16; ++i) { - guid[i] = data[i]; - } -} -static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) -{ -#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} -static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) -{ -#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) - drwav_uint32 r; - __asm__ __volatile__ ( - #if defined(DRWAV_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} -static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) -{ -#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) | - ((n & ((drwav_uint64)0xFF000000 )) << 8) | - ((n & ((drwav_uint64)0x00FF0000 )) << 24) | - ((n & ((drwav_uint64)0x0000FF00 )) << 40) | - ((n & ((drwav_uint64)0x000000FF )) << 56); -#endif -} -static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n) -{ - return (drwav_int16)drwav__bswap16((drwav_uint16)n); -} -static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]); - } -} -static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p) -{ - drwav_uint8 t; - t = p[0]; - p[0] = p[2]; - p[2] = t; -} -static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - drwav_uint8* pSample = pSamples + (iSample*3); - drwav__bswap_s24(pSample); - } -} -static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n) -{ - return (drwav_int32)drwav__bswap32((drwav_uint32)n); -} -static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]); - } -} -static DRWAV_INLINE float drwav__bswap_f32(float n) -{ - union { - drwav_uint32 i; - float f; - } x; - x.f = n; - x.i = drwav__bswap32(x.i); - return x.f; -} -static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]); - } -} -static DRWAV_INLINE double drwav__bswap_f64(double n) -{ - union { - drwav_uint64 i; - double f; - } x; - x.f = n; - x.i = drwav__bswap64(x.i); - return x.f; -} -static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]); - } -} -static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) -{ - switch (bytesPerSample) - { - case 1: - { - } break; - case 2: - { - drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); - } break; - case 3: - { - drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount); - } break; - case 4: - { - drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount); - } break; - default: - { - DRWAV_ASSERT(DRWAV_FALSE); - } break; - } -} -static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) -{ - switch (bytesPerSample) - { - #if 0 - case 2: - { - drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount); - } break; - #endif - case 4: - { - drwav__bswap_samples_f32((float*)pSamples, sampleCount); - } break; - case 8: - { - drwav__bswap_samples_f64((double*)pSamples, sampleCount); - } break; - default: - { - DRWAV_ASSERT(DRWAV_FALSE); - } break; - } -} -static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format) -{ - switch (format) - { - case DR_WAVE_FORMAT_PCM: - { - drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample); - } break; - case DR_WAVE_FORMAT_IEEE_FLOAT: - { - drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample); - } break; - case DR_WAVE_FORMAT_ALAW: - case DR_WAVE_FORMAT_MULAW: - { - drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); - } break; - case DR_WAVE_FORMAT_ADPCM: - case DR_WAVE_FORMAT_DVI_ADPCM: - default: - { - DRWAV_ASSERT(DRWAV_FALSE); - } break; - } -} -DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return DRWAV_MALLOC(sz); -} -DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return DRWAV_REALLOC(p, sz); -} -DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData) -{ - (void)pUserData; - DRWAV_FREE(p); -} -DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - DRWAV_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return *pAllocationCallbacks; - } else { - drwav_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = drwav__malloc_default; - allocationCallbacks.onRealloc = drwav__realloc_default; - allocationCallbacks.onFree = drwav__free_default; - return allocationCallbacks; - } -} -static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag) -{ - return - formatTag == DR_WAVE_FORMAT_ADPCM || - formatTag == DR_WAVE_FORMAT_DVI_ADPCM; -} -DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 2); -} -DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 8); -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); -DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); -DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut) -{ - if (container == drwav_container_riff || container == drwav_container_rf64) { - drwav_uint8 sizeInBytes[4]; - if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { - return DRWAV_AT_END; - } - if (onRead(pUserData, sizeInBytes, 4) != 4) { - return DRWAV_INVALID_FILE; - } - pHeaderOut->sizeInBytes = drwav_bytes_to_u32(sizeInBytes); - pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 8; - } else { - drwav_uint8 sizeInBytes[8]; - if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { - return DRWAV_AT_END; - } - if (onRead(pUserData, sizeInBytes, 8) != 8) { - return DRWAV_INVALID_FILE; - } - pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24; - pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 24; - } - return DRWAV_SUCCESS; -} -DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) -{ - drwav_uint64 bytesRemainingToSeek = offset; - while (bytesRemainingToSeek > 0) { - if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - bytesRemainingToSeek -= 0x7FFFFFFF; - } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - bytesRemainingToSeek = 0; - } - } - return DRWAV_TRUE; -} -DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) -{ - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, drwav_seek_origin_start); - } - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { - return DRWAV_FALSE; - } - offset -= 0x7FFFFFFF; - for (;;) { - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, drwav_seek_origin_current); - } - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - offset -= 0x7FFFFFFF; - } -} -DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut) -{ - drwav_chunk_header header; - drwav_uint8 fmt[16]; - if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - while (((container == drwav_container_riff || container == drwav_container_rf64) && !drwav_fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) { - if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize; - if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - } - if (container == drwav_container_riff || container == drwav_container_rf64) { - if (!drwav_fourcc_equal(header.id.fourcc, "fmt ")) { - return DRWAV_FALSE; - } - } else { - if (!drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT)) { - return DRWAV_FALSE; - } - } - if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += sizeof(fmt); - fmtOut->formatTag = drwav_bytes_to_u16(fmt + 0); - fmtOut->channels = drwav_bytes_to_u16(fmt + 2); - fmtOut->sampleRate = drwav_bytes_to_u32(fmt + 4); - fmtOut->avgBytesPerSec = drwav_bytes_to_u32(fmt + 8); - fmtOut->blockAlign = drwav_bytes_to_u16(fmt + 12); - fmtOut->bitsPerSample = drwav_bytes_to_u16(fmt + 14); - fmtOut->extendedSize = 0; - fmtOut->validBitsPerSample = 0; - fmtOut->channelMask = 0; - DRWAV_ZERO_MEMORY(fmtOut->subFormat, sizeof(fmtOut->subFormat)); - if (header.sizeInBytes > 16) { - drwav_uint8 fmt_cbSize[2]; - int bytesReadSoFar = 0; - if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += sizeof(fmt_cbSize); - bytesReadSoFar = 18; - fmtOut->extendedSize = drwav_bytes_to_u16(fmt_cbSize); - if (fmtOut->extendedSize > 0) { - if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - if (fmtOut->extendedSize != 22) { - return DRWAV_FALSE; - } - } - if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - drwav_uint8 fmtext[22]; - if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) { - return DRWAV_FALSE; - } - fmtOut->validBitsPerSample = drwav_bytes_to_u16(fmtext + 0); - fmtOut->channelMask = drwav_bytes_to_u32(fmtext + 2); - drwav_bytes_to_guid(fmtext + 6, fmtOut->subFormat); - } else { - if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - } - *pRunningBytesReadOut += fmtOut->extendedSize; - bytesReadSoFar += fmtOut->extendedSize; - } - if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar); - } - if (header.paddingSize > 0) { - if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += header.paddingSize; - } - return DRWAV_TRUE; -} -DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) -{ - size_t bytesRead; - DRWAV_ASSERT(onRead != NULL); - DRWAV_ASSERT(pCursor != NULL); - bytesRead = onRead(pUserData, pBufferOut, bytesToRead); - *pCursor += bytesRead; - return bytesRead; -} -#if 0 -DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor) -{ - DRWAV_ASSERT(onSeek != NULL); - DRWAV_ASSERT(pCursor != NULL); - if (!onSeek(pUserData, offset, origin)) { - return DRWAV_FALSE; - } - if (origin == drwav_seek_origin_start) { - *pCursor = offset; - } else { - *pCursor += offset; - } - return DRWAV_TRUE; -} -#endif -#define DRWAV_SMPL_BYTES 36 -#define DRWAV_SMPL_LOOP_BYTES 24 -#define DRWAV_INST_BYTES 7 -#define DRWAV_ACID_BYTES 24 -#define DRWAV_CUE_BYTES 4 -#define DRWAV_BEXT_BYTES 602 -#define DRWAV_BEXT_DESCRIPTION_BYTES 256 -#define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32 -#define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32 -#define DRWAV_BEXT_RESERVED_BYTES 180 -#define DRWAV_BEXT_UMID_BYTES 64 -#define DRWAV_CUE_POINT_BYTES 24 -#define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4 -#define DRWAV_LIST_LABELLED_TEXT_BYTES 20 -#define DRWAV_METADATA_ALIGNMENT 8 -typedef enum -{ - drwav__metadata_parser_stage_count, - drwav__metadata_parser_stage_read -} drwav__metadata_parser_stage; -typedef struct -{ - drwav_read_proc onRead; - drwav_seek_proc onSeek; - void *pReadSeekUserData; - drwav__metadata_parser_stage stage; - drwav_metadata *pMetadata; - drwav_uint32 metadataCount; - drwav_uint8 *pData; - drwav_uint8 *pDataCursor; - drwav_uint64 metadataCursor; - drwav_uint64 extraCapacity; -} drwav__metadata_parser; -DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser) -{ - drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity; - if (cap > DRWAV_SIZE_MAX) { - return 0; - } - return (size_t)cap; -} -DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align) -{ - drwav_uint8* pResult; - if (align) { - drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align; - if (modulo != 0) { - pParser->pDataCursor += align - modulo; - } - } - pResult = pParser->pDataCursor; - DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser))); - pParser->pDataCursor += size; - return pResult; -} -DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align) -{ - size_t extra = bytes + (align ? (align - 1) : 0); - pParser->extraCapacity += extra; -} -DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { - pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); - pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); - pParser->pDataCursor = pParser->pData; - if (pParser->pData == NULL) { - return DRWAV_OUT_OF_MEMORY; - } - pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1); - pParser->metadataCursor = 0; - } - return DRWAV_SUCCESS; -} -DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) -{ - if (pCursor != NULL) { - return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); - } else { - return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); - } -} -DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) -{ - drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; - drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); - DRWAV_ASSERT(pChunkHeader != NULL); - if (bytesJustRead == sizeof(smplHeaderData)) { - drwav_uint32 iSampleLoop; - pMetadata->type = drwav_metadata_type_smpl; - pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); - pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4); - pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8); - pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12); - pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16); - pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20); - pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24); - pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28); - pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32); - if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) { - pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT); - for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { - drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES]; - bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); - if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); - } else { - break; - } - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); - DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); - drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); - } - } - } - return totalBytesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) -{ - drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; - drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(cueHeaderSectionData)) { - pMetadata->type = drwav_metadata_type_cue; - pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData); - if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) { - pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT); - DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); - if (pMetadata->data.cue.cuePointCount > 0) { - drwav_uint32 iCuePoint; - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES]; - bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); - if (bytesJustRead == sizeof(cuePointData)) { - pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0); - pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4); - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; - pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); - pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20); - } else { - break; - } - } - } - } - } - return totalBytesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) -{ - drwav_uint8 instData[DRWAV_INST_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); - if (bytesRead == sizeof(instData)) { - pMetadata->type = drwav_metadata_type_inst; - pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0]; - pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1]; - pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2]; - pMetadata->data.inst.lowNote = (drwav_int8)instData[3]; - pMetadata->data.inst.highNote = (drwav_int8)instData[4]; - pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5]; - pMetadata->data.inst.highVelocity = (drwav_int8)instData[6]; - } - return bytesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) -{ - drwav_uint8 acidData[DRWAV_ACID_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); - if (bytesRead == sizeof(acidData)) { - pMetadata->type = drwav_metadata_type_acid; - pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0); - pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4); - pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6); - pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8); - pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12); - pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16); - pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18); - pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20); - } - return bytesRead; -} -DRWAV_PRIVATE size_t drwav__strlen(const char* str) -{ - size_t result = 0; - while (*str++) { - result += 1; - } - return result; -} -DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead) -{ - size_t result = 0; - while (*str++ && result < maxToRead) { - result += 1; - } - return result; -} -DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead) -{ - size_t len = drwav__strlen_clamped(str, maxToRead); - if (len) { - char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1); - DRWAV_ASSERT(result != NULL); - DRWAV_COPY_MEMORY(result, str, len); - result[len] = '\0'; - return result; - } else { - return NULL; - } -} -typedef struct -{ - const void* pBuffer; - size_t sizeInBytes; - size_t cursor; -} drwav_buffer_reader; -DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader) -{ - DRWAV_ASSERT(pBuffer != NULL); - DRWAV_ASSERT(pReader != NULL); - DRWAV_ZERO_OBJECT(pReader); - pReader->pBuffer = pBuffer; - pReader->sizeInBytes = sizeInBytes; - pReader->cursor = 0; - return DRWAV_SUCCESS; -} -DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader) -{ - DRWAV_ASSERT(pReader != NULL); - return drwav_offset_ptr(pReader->pBuffer, pReader->cursor); -} -DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek) -{ - DRWAV_ASSERT(pReader != NULL); - if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { - return DRWAV_BAD_SEEK; - } - pReader->cursor += bytesToSeek; - return DRWAV_SUCCESS; -} -DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) -{ - drwav_result result = DRWAV_SUCCESS; - size_t bytesRemaining; - DRWAV_ASSERT(pReader != NULL); - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - bytesRemaining = (pReader->sizeInBytes - pReader->cursor); - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (pDst == NULL) { - result = drwav_buffer_reader_seek(pReader, bytesToRead); - } else { - DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead); - pReader->cursor += bytesToRead; - } - DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); - if (result == DRWAV_SUCCESS) { - if (pBytesRead != NULL) { - *pBytesRead = bytesToRead; - } - } - return DRWAV_SUCCESS; -} -DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst) -{ - drwav_result result; - size_t bytesRead; - drwav_uint8 data[2]; - DRWAV_ASSERT(pReader != NULL); - DRWAV_ASSERT(pDst != NULL); - *pDst = 0; - result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { - return result; - } - *pDst = drwav_bytes_to_u16(data); - return DRWAV_SUCCESS; -} -DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst) -{ - drwav_result result; - size_t bytesRead; - drwav_uint8 data[4]; - DRWAV_ASSERT(pReader != NULL); - DRWAV_ASSERT(pDst != NULL); - *pDst = 0; - result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { - return result; - } - *pDst = drwav_bytes_to_u32(data); - return DRWAV_SUCCESS; -} -DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) -{ - drwav_uint8 bextData[DRWAV_BEXT_BYTES]; - size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); - if (bytesRead == sizeof(bextData)) { - drwav_buffer_reader reader; - drwav_uint32 timeReferenceLow; - drwav_uint32 timeReferenceHigh; - size_t extraBytes; - pMetadata->type = drwav_metadata_type_bext; - if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) { - pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES); - drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES); - pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES); - drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); - pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES); - drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES); - drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); - drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); - drwav_buffer_reader_read_u32(&reader, &timeReferenceLow); - drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh); - pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow; - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); - pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1); - drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); - DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES)); - extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES); - if (extraBytes > 0) { - pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1); - DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); - bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); - pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory); - } else { - pMetadata->data.bext.pCodingHistory = NULL; - pMetadata->data.bext.codingHistorySize = 0; - } - } - } - return bytesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type) -{ - drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES]; - drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(cueIDBuffer)) { - drwav_uint32 sizeIncludingNullTerminator; - pMetadata->type = type; - pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer); - sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; - if (sizeIncludingNullTerminator > 0) { - pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); - } else { - pMetadata->data.labelOrNote.stringLength = 0; - pMetadata->data.labelOrNote.pString = NULL; - } - } - return totalBytesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) -{ - drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES]; - drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(buffer)) { - drwav_uint32 sizeIncludingNullTerminator; - pMetadata->type = drwav_metadata_type_list_labelled_cue_region; - pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0); - pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4); - pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; - pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; - pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; - pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; - pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12); - pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14); - pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16); - pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18); - sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES; - if (sizeIncludingNullTerminator > 0) { - pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); - } else { - pMetadata->data.labelledCueRegion.stringLength = 0; - pMetadata->data.labelledCueRegion.pString = NULL; - } - } - return totalBytesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type) -{ - drwav_uint64 bytesRead = 0; - drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize; - if (pParser->stage == drwav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); - } else { - drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = type; - if (stringSizeWithNullTerminator > 0) { - pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; - pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); - DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL); - bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); - if (bytesRead == chunkSize) { - pParser->metadataCursor += 1; - } else { - } - } else { - pMetadata->data.infoText.stringLength = 0; - pMetadata->data.infoText.pString = NULL; - pParser->metadataCursor += 1; - } - } - return bytesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location) -{ - drwav_uint64 bytesRead = 0; - if (location == drwav_metadata_location_invalid) { - return 0; - } - if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt") || drwav_fourcc_equal(pChunkId, "fact")) { - return 0; - } - if (pParser->stage == drwav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); - } else { - drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = drwav_metadata_type_unknown; - pMetadata->data.unknown.chunkLocation = location; - pMetadata->data.unknown.id[0] = pChunkId[0]; - pMetadata->data.unknown.id[1] = pChunkId[1]; - pMetadata->data.unknown.id[2] = pChunkId[2]; - pMetadata->data.unknown.id[3] = pChunkId[3]; - pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize; - pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1); - DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); - if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - return bytesRead; -} -DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID) -{ - return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID); -} -DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes) -{ - const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc; - drwav_uint64 bytesRead = 0; - if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) { - if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { - drwav_uint8 buffer[4]; - size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) { - return bytesRead; - } - bytesRead += 28; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - drwav_uint32 loopCount = drwav_bytes_to_u32(buffer); - drwav_uint64 calculatedLoopCount; - calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES; - if (calculatedLoopCount == loopCount) { - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer); - pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT); - drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); - } - } else { - } - } - } else { - bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) { - if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - } else { - bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) { - if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - } else { - bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) { - if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { - size_t cueCount; - pParser->metadataCount += 1; - cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES; - drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT); - } else { - bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) { - if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { - char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1]; - size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES; - size_t bytesJustRead; - buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0'; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead); - if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) { - return bytesRead; - } - allocSizeNeeded += drwav__strlen(buffer) + 1; - buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); - if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) { - return bytesRead; - } - allocSizeNeeded += drwav__strlen(buffer) + 1; - buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); - if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) { - return bytesRead; - } - allocSizeNeeded += drwav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; - drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); - pParser->metadataCount += 1; - } else { - bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) { - drwav_metadata_location listType = drwav_metadata_location_invalid; - while (bytesRead < pChunkHeader->sizeInBytes) { - drwav_uint8 subchunkId[4]; - drwav_uint8 subchunkSizeBuffer[4]; - drwav_uint64 subchunkDataSize; - drwav_uint64 subchunkBytesRead = 0; - drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); - if (bytesJustRead != sizeof(subchunkId)) { - break; - } - if (drwav_fourcc_equal(subchunkId, "adtl")) { - listType = drwav_metadata_location_inside_adtl_list; - continue; - } else if (drwav_fourcc_equal(subchunkId, "INFO")) { - listType = drwav_metadata_location_inside_info_list; - continue; - } - bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); - if (bytesJustRead != sizeof(subchunkSizeBuffer)) { - break; - } - subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer); - if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) { - if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) { - drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; - if (pParser->stage == drwav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); - } else { - subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note); - if (subchunkBytesRead == subchunkDataSize) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) { - if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) { - drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES; - if (pParser->stage == drwav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); - } else { - subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); - if (subchunkBytesRead == subchunkDataSize) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber); - } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { - subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); - } - bytesRead += subchunkBytesRead; - DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize); - if (subchunkBytesRead < subchunkDataSize) { - drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) { - break; - } - bytesRead += bytesToSeek; - } - if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) { - break; - } - bytesRead += 1; - } - } - } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { - bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level); - } - return bytesRead; -} -DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) -{ - drwav_uint32 bytesPerFrame; - if ((pWav->bitsPerSample & 0x7) == 0) { - bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; - } else { - bytesPerFrame = pWav->fmt.blockAlign; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { - if (bytesPerFrame != pWav->fmt.channels) { - return 0; - } - } - return bytesPerFrame; -} -DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT) -{ - if (pFMT == NULL) { - return 0; - } - if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) { - return pFMT->formatTag; - } else { - return drwav_bytes_to_u16(pFMT->subFormat); - } -} -DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onRead == NULL || onSeek == NULL) { - return DRWAV_FALSE; - } - DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->pUserData = pReadSeekUserData; - pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return DRWAV_FALSE; - } - return DRWAV_TRUE; -} -DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) -{ - drwav_uint64 cursor; - drwav_bool32 sequential; - drwav_uint8 riff[4]; - drwav_fmt fmt; - unsigned short translatedFormatTag; - drwav_bool32 foundDataChunk; - drwav_uint64 dataChunkSize = 0; - drwav_uint64 sampleCountFromFactChunk = 0; - drwav_uint64 chunkSize; - drwav__metadata_parser metadataParser; - cursor = 0; - sequential = (flags & DRWAV_SEQUENTIAL) != 0; - if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { - return DRWAV_FALSE; - } - if (drwav_fourcc_equal(riff, "RIFF")) { - pWav->container = drwav_container_riff; - } else if (drwav_fourcc_equal(riff, "riff")) { - int i; - drwav_uint8 riff2[12]; - pWav->container = drwav_container_w64; - if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { - return DRWAV_FALSE; - } - for (i = 0; i < 12; ++i) { - if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { - return DRWAV_FALSE; - } - } - } else if (drwav_fourcc_equal(riff, "RF64")) { - pWav->container = drwav_container_rf64; - } else { - return DRWAV_FALSE; - } - if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { - drwav_uint8 chunkSizeBytes[4]; - drwav_uint8 wave[4]; - if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return DRWAV_FALSE; - } - if (pWav->container == drwav_container_riff) { - if (drwav_bytes_to_u32(chunkSizeBytes) < 36) { - return DRWAV_FALSE; - } - } else { - if (drwav_bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) { - return DRWAV_FALSE; - } - } - if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return DRWAV_FALSE; - } - if (!drwav_fourcc_equal(wave, "WAVE")) { - return DRWAV_FALSE; - } - } else { - drwav_uint8 chunkSizeBytes[8]; - drwav_uint8 wave[16]; - if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return DRWAV_FALSE; - } - if (drwav_bytes_to_u64(chunkSizeBytes) < 80) { - return DRWAV_FALSE; - } - if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return DRWAV_FALSE; - } - if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) { - return DRWAV_FALSE; - } - } - if (pWav->container == drwav_container_rf64) { - drwav_uint8 sizeBytes[8]; - drwav_uint64 bytesRemainingInChunk; - drwav_chunk_header header; - drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) { - return DRWAV_FALSE; - } - bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; - if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { - return DRWAV_FALSE; - } - bytesRemainingInChunk -= 8; - cursor += 8; - if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return DRWAV_FALSE; - } - bytesRemainingInChunk -= 8; - dataChunkSize = drwav_bytes_to_u64(sizeBytes); - if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return DRWAV_FALSE; - } - bytesRemainingInChunk -= 8; - sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes); - if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { - return DRWAV_FALSE; - } - cursor += bytesRemainingInChunk; - } - if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) { - return DRWAV_FALSE; - } - if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) || - (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) || - (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) || - fmt.blockAlign == 0) { - return DRWAV_FALSE; - } - translatedFormatTag = fmt.formatTag; - if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0); - } - DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); - if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { - drwav_uint64 cursorForMetadata = cursor; - metadataParser.onRead = pWav->onRead; - metadataParser.onSeek = pWav->onSeek; - metadataParser.pReadSeekUserData = pWav->pUserData; - metadataParser.stage = drwav__metadata_parser_stage_count; - for (;;) { - drwav_result result; - drwav_uint64 bytesRead; - drwav_uint64 remainingBytes; - drwav_chunk_header header; - result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursorForMetadata, &header); - if (result != DRWAV_SUCCESS) { - break; - } - bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); - DRWAV_ASSERT(bytesRead <= header.sizeInBytes); - remainingBytes = header.sizeInBytes - bytesRead + header.paddingSize; - if (!drwav__seek_forward(pWav->onSeek, remainingBytes, pWav->pUserData)) { - break; - } - cursorForMetadata += remainingBytes; - } - if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { - return DRWAV_FALSE; - } - drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); - metadataParser.stage = drwav__metadata_parser_stage_read; - } - foundDataChunk = DRWAV_FALSE; - for (;;) { - drwav_chunk_header header; - drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != DRWAV_SUCCESS) { - if (!foundDataChunk) { - return DRWAV_FALSE; - } else { - break; - } - } - if (!sequential && onChunk != NULL) { - drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); - if (callbackBytesRead > 0) { - if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { - return DRWAV_FALSE; - } - } - } - if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { - drwav_uint64 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); - if (bytesRead > 0) { - if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { - return DRWAV_FALSE; - } - } - } - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - chunkSize = header.sizeInBytes; - if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { - if (drwav_fourcc_equal(header.id.fourcc, "data")) { - foundDataChunk = DRWAV_TRUE; - if (pWav->container != drwav_container_rf64) { - dataChunkSize = chunkSize; - } - } - } else { - if (drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA)) { - foundDataChunk = DRWAV_TRUE; - dataChunkSize = chunkSize; - } - } - if (foundDataChunk && sequential) { - break; - } - if (pWav->container == drwav_container_riff) { - if (drwav_fourcc_equal(header.id.fourcc, "fact")) { - drwav_uint32 sampleCount; - if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { - return DRWAV_FALSE; - } - chunkSize -= 4; - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - sampleCountFromFactChunk = sampleCount; - } else { - sampleCountFromFactChunk = 0; - } - } - } else if (pWav->container == drwav_container_w64) { - if (drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT)) { - if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { - return DRWAV_FALSE; - } - chunkSize -= 8; - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - } - } else if (pWav->container == drwav_container_rf64) { - } - chunkSize += header.paddingSize; - if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) { - break; - } - cursor += chunkSize; - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - } - pWav->pMetadata = metadataParser.pMetadata; - pWav->metadataCount = metadataParser.metadataCount; - if (!foundDataChunk) { - return DRWAV_FALSE; - } - if (!sequential) { - if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { - return DRWAV_FALSE; - } - cursor = pWav->dataChunkDataPos; - } - pWav->fmt = fmt; - pWav->sampleRate = fmt.sampleRate; - pWav->channels = fmt.channels; - pWav->bitsPerSample = fmt.bitsPerSample; - pWav->bytesRemaining = dataChunkSize; - pWav->translatedFormatTag = translatedFormatTag; - pWav->dataChunkDataSize = dataChunkSize; - if (sampleCountFromFactChunk != 0) { - pWav->totalPCMFrameCount = sampleCountFromFactChunk; - } else { - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return DRWAV_FALSE; - } - pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - drwav_uint64 totalBlockHeaderSizeInBytes; - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - drwav_uint64 totalBlockHeaderSizeInBytes; - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - pWav->totalPCMFrameCount += blockCount; - } - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - if (pWav->channels > 2) { - return DRWAV_FALSE; - } - } - if (drwav_get_bytes_per_pcm_frame(pWav) == 0) { - return DRWAV_FALSE; - } -#ifdef DR_WAV_LIBSNDFILE_COMPAT - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; - } -#endif - return DRWAV_TRUE; -} -DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); -} -DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; - return drwav_init__internal(pWav, NULL, NULL, flags); -} -DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav) -{ - drwav_metadata *result = pWav->pMetadata; - pWav->pMetadata = NULL; - pWav->metadataCount = 0; - return result; -} -DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize) -{ - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - return pWav->onWrite(pWav->pUserData, pData, dataSize); -} -DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte) -{ - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - return pWav->onWrite(pWav->pUserData, &byte, 1); -} -DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value) -{ - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - if (!drwav__is_little_endian()) { - value = drwav__bswap16(value); - } - return drwav__write(pWav, &value, 2); -} -DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value) -{ - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - if (!drwav__is_little_endian()) { - value = drwav__bswap32(value); - } - return drwav__write(pWav, &value, 4); -} -DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value) -{ - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - if (!drwav__is_little_endian()) { - value = drwav__bswap64(value); - } - return drwav__write(pWav, &value, 8); -} -DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value) -{ - union { - drwav_uint32 u32; - float f32; - } u; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - u.f32 = value; - if (!drwav__is_little_endian()) { - u.u32 = drwav__bswap32(u.u32); - } - return drwav__write(pWav, &u.u32, 4); -} -DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize) -{ - if (pWav == NULL) { - return dataSize; - } - return drwav__write(pWav, pData, dataSize); -} -DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte) -{ - if (pWav == NULL) { - return 1; - } - return drwav__write_byte(pWav, byte); -} -DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value) -{ - if (pWav == NULL) { - return 2; - } - return drwav__write_u16ne_to_le(pWav, value); -} -DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value) -{ - if (pWav == NULL) { - return 4; - } - return drwav__write_u32ne_to_le(pWav, value); -} -#if 0 -DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value) -{ - if (pWav == NULL) { - return 8; - } - return drwav__write_u64ne_to_le(pWav, value); -} -#endif -DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value) -{ - if (pWav == NULL) { - return 4; - } - return drwav__write_f32ne_to_le(pWav, value); -} -DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize) -{ - size_t len; - if (pWav == NULL) { - return bufFixedSize; - } - len = drwav__strlen_clamped(str, bufFixedSize); - drwav__write_or_count(pWav, str, len); - if (len < bufFixedSize) { - size_t i; - for (i = 0; i < bufFixedSize - len; ++i) { - drwav__write_byte(pWav, 0); - } - } - return bufFixedSize; -} -DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount) -{ - size_t bytesWritten = 0; - drwav_bool32 hasListAdtl = DRWAV_FALSE; - drwav_bool32 hasListInfo = DRWAV_FALSE; - drwav_uint32 iMetadata; - if (pMetadatas == NULL || metadataCount == 0) { - return 0; - } - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - drwav_uint32 chunkSize = 0; - if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) { - hasListInfo = DRWAV_TRUE; - } - if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) { - hasListAdtl = DRWAV_TRUE; - } - switch (pMetadata->type) { - case drwav_metadata_type_smpl: - { - drwav_uint32 iLoop; - chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; - bytesWritten += drwav__write_or_count(pWav, "smpl", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); - for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - bytesWritten += drwav__write(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); - } - } break; - case drwav_metadata_type_inst: - { - chunkSize = DRWAV_INST_BYTES; - bytesWritten += drwav__write_or_count(pWav, "inst", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); - } break; - case drwav_metadata_type_cue: - { - drwav_uint32 iCuePoint; - chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; - bytesWritten += drwav__write_or_count(pWav, "cue ", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); - } - } break; - case drwav_metadata_type_acid: - { - chunkSize = DRWAV_ACID_BYTES; - bytesWritten += drwav__write_or_count(pWav, "acid", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); - bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); - bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); - } break; - case drwav_metadata_type_bext: - { - char reservedBuf[DRWAV_BEXT_RESERVED_BYTES]; - drwav_uint32 timeReferenceLow; - drwav_uint32 timeReferenceHigh; - chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; - bytesWritten += drwav__write_or_count(pWav, "bext", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES); - bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); - bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); - timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); - timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); - DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); - bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); - if (pMetadata->data.bext.codingHistorySize > 0) { - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); - } - } break; - case drwav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) { - chunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); - } - } break; - default: break; - } - if ((chunkSize % 2) != 0) { - bytesWritten += drwav__write_or_count_byte(pWav, 0); - } - } - if (hasListInfo) { - drwav_uint32 chunkSize = 4; - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) { - chunkSize += 8; - chunkSize += pMetadata->data.infoText.stringLength + 1; - } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { - chunkSize += 8; - chunkSize += pMetadata->data.unknown.dataSizeInBytes; - } - if ((chunkSize % 2) != 0) { - chunkSize += 1; - } - } - bytesWritten += drwav__write_or_count(pWav, "LIST", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, "INFO", 4); - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - drwav_uint32 subchunkSize = 0; - if (pMetadata->type & drwav_metadata_type_list_all_info_strings) { - const char* pID = NULL; - switch (pMetadata->type) { - case drwav_metadata_type_list_info_software: pID = "ISFT"; break; - case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break; - case drwav_metadata_type_list_info_title: pID = "INAM"; break; - case drwav_metadata_type_list_info_artist: pID = "IART"; break; - case drwav_metadata_type_list_info_comment: pID = "ICMT"; break; - case drwav_metadata_type_list_info_date: pID = "ICRD"; break; - case drwav_metadata_type_list_info_genre: pID = "IGNR"; break; - case drwav_metadata_type_list_info_album: pID = "IPRD"; break; - case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; - default: break; - } - DRWAV_ASSERT(pID != NULL); - if (pMetadata->data.infoText.stringLength) { - subchunkSize = pMetadata->data.infoText.stringLength + 1; - bytesWritten += drwav__write_or_count(pWav, pID, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); - bytesWritten += drwav__write_or_count_byte(pWav, '\0'); - } - } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { - if (pMetadata->data.unknown.dataSizeInBytes) { - subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); - } - } - if ((subchunkSize % 2) != 0) { - bytesWritten += drwav__write_or_count_byte(pWav, 0); - } - } - } - if (hasListAdtl) { - drwav_uint32 chunkSize = 4; - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - switch (pMetadata->type) - { - case drwav_metadata_type_list_label: - case drwav_metadata_type_list_note: - { - chunkSize += 8; - chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES; - if (pMetadata->data.labelOrNote.stringLength > 0) { - chunkSize += pMetadata->data.labelOrNote.stringLength + 1; - } - } break; - case drwav_metadata_type_list_labelled_cue_region: - { - chunkSize += 8; - chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES; - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; - } - } break; - case drwav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { - chunkSize += 8; - chunkSize += pMetadata->data.unknown.dataSizeInBytes; - } - } break; - default: break; - } - if ((chunkSize % 2) != 0) { - chunkSize += 1; - } - } - bytesWritten += drwav__write_or_count(pWav, "LIST", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, "adtl", 4); - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - drwav_uint32 subchunkSize = 0; - switch (pMetadata->type) - { - case drwav_metadata_type_list_label: - case drwav_metadata_type_list_note: - { - if (pMetadata->data.labelOrNote.stringLength > 0) { - const char *pID = NULL; - if (pMetadata->type == drwav_metadata_type_list_label) { - pID = "labl"; - } - else if (pMetadata->type == drwav_metadata_type_list_note) { - pID = "note"; - } - DRWAV_ASSERT(pID != NULL); - DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES; - bytesWritten += drwav__write_or_count(pWav, pID, 4); - subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); - bytesWritten += drwav__write_or_count_byte(pWav, '\0'); - } - } break; - case drwav_metadata_type_list_labelled_cue_region: - { - subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES; - bytesWritten += drwav__write_or_count(pWav, "ltxt", 4); - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; - } - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); - bytesWritten += drwav__write_or_count_byte(pWav, '\0'); - } - } break; - case drwav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { - subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); - } - } break; - default: break; - } - if ((subchunkSize % 2) != 0) { - bytesWritten += drwav__write_or_count_byte(pWav, 0); - } - } - } - DRWAV_ASSERT((bytesWritten % 2) == 0); - return bytesWritten; -} -DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount) -{ - drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); - if (chunkSize > 0xFFFFFFFFUL) { - chunkSize = 0xFFFFFFFFUL; - } - return (drwav_uint32)chunkSize; -} -DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize) -{ - if (dataChunkSize <= 0xFFFFFFFFUL) { - return (drwav_uint32)dataChunkSize; - } else { - return 0xFFFFFFFFUL; - } -} -DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize) -{ - drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize); - return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; -} -DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize) -{ - return 24 + dataChunkSize; -} -DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata) -{ - drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); - if (chunkSize > 0xFFFFFFFFUL) { - chunkSize = 0xFFFFFFFFUL; - } - return chunkSize; -} -DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize) -{ - return dataChunkSize; -} -DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onWrite == NULL) { - return DRWAV_FALSE; - } - if (!isSequential && onSeek == NULL) { - return DRWAV_FALSE; - } - if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { - return DRWAV_FALSE; - } - if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) { - return DRWAV_FALSE; - } - DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onWrite = onWrite; - pWav->onSeek = onSeek; - pWav->pUserData = pUserData; - pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return DRWAV_FALSE; - } - pWav->fmt.formatTag = (drwav_uint16)pFormat->format; - pWav->fmt.channels = (drwav_uint16)pFormat->channels; - pWav->fmt.sampleRate = pFormat->sampleRate; - pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); - pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); - pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; - pWav->fmt.extendedSize = 0; - pWav->isSequentialWrite = isSequential; - return DRWAV_TRUE; -} -DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) -{ - size_t runningPos = 0; - drwav_uint64 initialDataChunkSize = 0; - drwav_uint64 chunkSizeFMT; - if (pWav->isSequentialWrite) { - initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; - if (pFormat->container == drwav_container_riff) { - if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { - return DRWAV_FALSE; - } - } - } - pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; - if (pFormat->container == drwav_container_riff) { - drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; - runningPos += drwav__write(pWav, "RIFF", 4); - runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF); - runningPos += drwav__write(pWav, "WAVE", 4); - } else if (pFormat->container == drwav_container_w64) { - drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; - runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16); - runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF); - runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16); - } else if (pFormat->container == drwav_container_rf64) { - runningPos += drwav__write(pWav, "RF64", 4); - runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - runningPos += drwav__write(pWav, "WAVE", 4); - } - if (pFormat->container == drwav_container_rf64) { - drwav_uint32 initialds64ChunkSize = 28; - drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; - runningPos += drwav__write(pWav, "ds64", 4); - runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize); - runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize); - runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize); - runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount); - runningPos += drwav__write_u32ne_to_le(pWav, 0); - } - if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) { - chunkSizeFMT = 16; - runningPos += drwav__write(pWav, "fmt ", 4); - runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT); - } else if (pFormat->container == drwav_container_w64) { - chunkSizeFMT = 40; - runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16); - runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT); - } - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels); - runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); - runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); - if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) { - runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); - } - pWav->dataChunkDataPos = runningPos; - if (pFormat->container == drwav_container_riff) { - drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; - runningPos += drwav__write(pWav, "data", 4); - runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == drwav_container_w64) { - drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; - runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16); - runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == drwav_container_rf64) { - runningPos += drwav__write(pWav, "data", 4); - runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - } - pWav->container = pFormat->container; - pWav->channels = (drwav_uint16)pFormat->channels; - pWav->sampleRate = pFormat->sampleRate; - pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; - pWav->translatedFormatTag = (drwav_uint16)pFormat->format; - pWav->dataChunkDataPos = runningPos; - return DRWAV_TRUE; -} -DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - return drwav_init_write__internal(pWav, pFormat, 0); -} -DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - return drwav_init_write__internal(pWav, pFormat, totalSampleCount); -} -DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return DRWAV_FALSE; - } - return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount) -{ - if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - pWav->pMetadata = pMetadata; - pWav->metadataCount = metadataCount; - return drwav_init_write__internal(pWav, pFormat, 0); -} -DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount) -{ - drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); - drwav_uint64 riffChunkSizeBytes; - drwav_uint64 fileSizeBytes = 0; - if (pFormat->container == drwav_container_riff) { - riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); - fileSizeBytes = (8 + riffChunkSizeBytes); - } else if (pFormat->container == drwav_container_w64) { - riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes); - fileSizeBytes = riffChunkSizeBytes; - } else if (pFormat->container == drwav_container_rf64) { - riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); - fileSizeBytes = (8 + riffChunkSizeBytes); - } - return fileSizeBytes; -} -#ifndef DR_WAV_NO_STDIO -#include -DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e) -{ - switch (e) - { - case 0: return DRWAV_SUCCESS; - #ifdef EPERM - case EPERM: return DRWAV_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return DRWAV_INTERRUPT; - #endif - #ifdef EIO - case EIO: return DRWAV_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return DRWAV_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return DRWAV_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return DRWAV_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return DRWAV_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return DRWAV_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return DRWAV_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return DRWAV_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return DRWAV_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return DRWAV_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return DRWAV_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return DRWAV_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return DRWAV_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return DRWAV_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return DRWAV_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return DRWAV_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return DRWAV_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return DRWAV_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return DRWAV_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return DRWAV_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return DRWAV_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return DRWAV_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return DRWAV_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return DRWAV_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return DRWAV_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return DRWAV_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return DRWAV_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return DRWAV_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return DRWAV_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return DRWAV_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return DRWAV_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return DRWAV_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return DRWAV_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return DRWAV_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return DRWAV_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return DRWAV_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return DRWAV_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return DRWAV_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return DRWAV_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return DRWAV_ERROR; - #endif - #ifdef EBADE - case EBADE: return DRWAV_ERROR; - #endif - #ifdef EBADR - case EBADR: return DRWAV_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return DRWAV_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return DRWAV_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return DRWAV_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return DRWAV_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return DRWAV_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return DRWAV_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return DRWAV_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return DRWAV_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return DRWAV_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return DRWAV_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return DRWAV_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return DRWAV_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return DRWAV_ERROR; - #endif - #ifdef EADV - case EADV: return DRWAV_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return DRWAV_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return DRWAV_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return DRWAV_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return DRWAV_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return DRWAV_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return DRWAV_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return DRWAV_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return DRWAV_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return DRWAV_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return DRWAV_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return DRWAV_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return DRWAV_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return DRWAV_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return DRWAV_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return DRWAV_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return DRWAV_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return DRWAV_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return DRWAV_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return DRWAV_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return DRWAV_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return DRWAV_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return DRWAV_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return DRWAV_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return DRWAV_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return DRWAV_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return DRWAV_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return DRWAV_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return DRWAV_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return DRWAV_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return DRWAV_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return DRWAV_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return DRWAV_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return DRWAV_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return DRWAV_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return DRWAV_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return DRWAV_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return DRWAV_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return DRWAV_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return DRWAV_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return DRWAV_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return DRWAV_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return DRWAV_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return DRWAV_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return DRWAV_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return DRWAV_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return DRWAV_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return DRWAV_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return DRWAV_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return DRWAV_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return DRWAV_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return DRWAV_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return DRWAV_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return DRWAV_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return DRWAV_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return DRWAV_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return DRWAV_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return DRWAV_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return DRWAV_ERROR; - #endif - default: return DRWAV_ERROR; - } -} -DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRWAV_INVALID_ARGS; - } -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drwav_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - drwav_result result = drwav_result_from_errno(errno); - if (result == DRWAV_SUCCESS) { - result = DRWAV_ERROR; - } - return result; - } -#endif - return DRWAV_SUCCESS; -} -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define DRWAV_HAS_WFOPEN - #endif -#endif -DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRWAV_INVALID_ARGS; - } -#if defined(DRWAV_HAS_WFOPEN) - { - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drwav_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return drwav_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - DRWAV_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return drwav_result_from_errno(errno); - } - pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return DRWAV_OUT_OF_MEMORY; - } - pFilePathTemp = pFilePath; - DRWAV_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - *ppFile = fopen(pFilePathMB, pOpenModeMB); - drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); - } - if (*ppFile == NULL) { - return DRWAV_ERROR; - } -#endif - return DRWAV_SUCCESS; -} -DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} -DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) -{ - return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); -} -DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} -DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, drwav_metadata_type allowedMetadataTypes, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav_bool32 result; - result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRWAV_TRUE) { - fclose(pFile); - return result; - } - pWav->allowedMetadataTypes = allowedMetadataTypes; - result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); - if (result != DRWAV_TRUE) { - fclose(pFile); - return result; - } - return DRWAV_TRUE; -} -DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); -} -DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav_bool32 result; - result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRWAV_TRUE) { - fclose(pFile); - return result; - } - result = drwav_init_write__internal(pWav, pFormat, totalSampleCount); - if (result != DRWAV_TRUE) { - fclose(pFile); - return result; - } - return DRWAV_TRUE; -} -DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} -DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return DRWAV_FALSE; - } - return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return DRWAV_FALSE; - } - return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -#endif -DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - drwav* pWav = (drwav*)pUserData; - size_t bytesRemaining; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); - bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); - pWav->memoryStream.currentReadPos += bytesToRead; - } - return bytesToRead; -} -DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) -{ - drwav* pWav = (drwav*)pUserData; - DRWAV_ASSERT(pWav != NULL); - if (origin == drwav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return DRWAV_FALSE; - } - } else { - if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return DRWAV_FALSE; - } - } - pWav->memoryStream.currentReadPos += offset; - } else { - if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) { - pWav->memoryStream.currentReadPos = offset; - } else { - return DRWAV_FALSE; - } - } - return DRWAV_TRUE; -} -DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) -{ - drwav* pWav = (drwav*)pUserData; - size_t bytesRemaining; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); - bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; - if (bytesRemaining < bytesToWrite) { - void* pNewData; - size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; - if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { - newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; - } - pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - *pWav->memoryStreamWrite.ppData = pNewData; - pWav->memoryStreamWrite.dataCapacity = newDataCapacity; - } - DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); - pWav->memoryStreamWrite.currentWritePos += bytesToWrite; - if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { - pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; - } - *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; - return bytesToWrite; -} -DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) -{ - drwav* pWav = (drwav*)pUserData; - DRWAV_ASSERT(pWav != NULL); - if (origin == drwav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { - offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); - } - } else { - if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { - offset = -(int)pWav->memoryStreamWrite.currentWritePos; - } - } - pWav->memoryStreamWrite.currentWritePos += offset; - } else { - if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) { - pWav->memoryStreamWrite.currentWritePos = offset; - } else { - pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; - } - } - return DRWAV_TRUE; -} -DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return DRWAV_FALSE; - } - if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - pWav->memoryStream.data = (const drwav_uint8*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); -} -DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return DRWAV_FALSE; - } - if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - pWav->memoryStream.data = (const drwav_uint8*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; - return drwav_init__internal(pWav, NULL, NULL, flags); -} -DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (ppData == NULL || pDataSize == NULL) { - return DRWAV_FALSE; - } - *ppData = NULL; - *pDataSize = 0; - if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - pWav->memoryStreamWrite.ppData = ppData; - pWav->memoryStreamWrite.pDataSize = pDataSize; - pWav->memoryStreamWrite.dataSize = 0; - pWav->memoryStreamWrite.dataCapacity = 0; - pWav->memoryStreamWrite.currentWritePos = 0; - return drwav_init_write__internal(pWav, pFormat, totalSampleCount); -} -DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); -} -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return DRWAV_FALSE; - } - return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -DRWAV_API drwav_result drwav_uninit(drwav* pWav) -{ - drwav_result result = DRWAV_SUCCESS; - if (pWav == NULL) { - return DRWAV_INVALID_ARGS; - } - if (pWav->onWrite != NULL) { - drwav_uint32 paddingSize = 0; - if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { - paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize); - } else { - paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize); - } - if (paddingSize > 0) { - drwav_uint64 paddingData = 0; - drwav__write(pWav, &paddingData, paddingSize); - } - if (pWav->onSeek && !pWav->isSequentialWrite) { - if (pWav->container == drwav_container_riff) { - if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { - drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - drwav__write_u32ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) { - drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); - drwav__write_u32ne_to_le(pWav, dataChunkSize); - } - } else if (pWav->container == drwav_container_w64) { - if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { - drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); - drwav__write_u64ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) { - drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); - drwav__write_u64ne_to_le(pWav, dataChunkSize); - } - } else if (pWav->container == drwav_container_rf64) { - int ds64BodyPos = 12 + 8; - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) { - drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - drwav__write_u64ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) { - drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize); - drwav__write_u64ne_to_le(pWav, dataChunkSize); - } - } - } - if (pWav->isSequentialWrite) { - if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { - result = DRWAV_INVALID_FILE; - } - } - } else { - if (pWav->pMetadata != NULL) { - pWav->allocationCallbacks.onFree(pWav->pMetadata, pWav->allocationCallbacks.pUserData); - } - } -#ifndef DR_WAV_NO_STDIO - if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) { - fclose((FILE*)pWav->pUserData); - } -#endif - return result; -} -DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) -{ - size_t bytesRead; - drwav_uint32 bytesPerFrame; - if (pWav == NULL || bytesToRead == 0) { - return 0; - } - if (bytesToRead > pWav->bytesRemaining) { - bytesToRead = (size_t)pWav->bytesRemaining; - } - if (bytesToRead == 0) { - return 0; - } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - if (pBufferOut != NULL) { - bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); - } else { - bytesRead = 0; - while (bytesRead < bytesToRead) { - size_t bytesToSeek = (bytesToRead - bytesRead); - if (bytesToSeek > 0x7FFFFFFF) { - bytesToSeek = 0x7FFFFFFF; - } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) { - break; - } - bytesRead += bytesToSeek; - } - while (bytesRead < bytesToRead) { - drwav_uint8 buffer[4096]; - size_t bytesSeeked; - size_t bytesToSeek = (bytesToRead - bytesRead); - if (bytesToSeek > sizeof(buffer)) { - bytesToSeek = sizeof(buffer); - } - bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); - bytesRead += bytesSeeked; - if (bytesSeeked < bytesToSeek) { - break; - } - } - } - pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; - pWav->bytesRemaining -= bytesRead; - return bytesRead; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) -{ - drwav_uint32 bytesPerFrame; - drwav_uint64 bytesToRead; - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { - return 0; - } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesToRead = framesToRead * bytesPerFrame; - if (bytesToRead > DRWAV_SIZE_MAX) { - bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; - } - if (bytesToRead == 0) { - return 0; - } - return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) -{ - drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL) { - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels, pWav->translatedFormatTag); - } - return framesRead; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) -{ - if (drwav__is_little_endian()) { - return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } else { - return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } -} -DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) -{ - if (pWav->onWrite != NULL) { - return DRWAV_FALSE; - } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { - return DRWAV_FALSE; - } - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - DRWAV_ZERO_OBJECT(&pWav->msadpcm); - } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - DRWAV_ZERO_OBJECT(&pWav->ima); - } else { - DRWAV_ASSERT(DRWAV_FALSE); - } - } - pWav->readCursorInPCMFrames = 0; - pWav->bytesRemaining = pWav->dataChunkDataSize; - return DRWAV_TRUE; -} -DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex) -{ - if (pWav == NULL || pWav->onSeek == NULL) { - return DRWAV_FALSE; - } - if (pWav->onWrite != NULL) { - return DRWAV_FALSE; - } - if (pWav->totalPCMFrameCount == 0) { - return DRWAV_TRUE; - } - if (targetFrameIndex > pWav->totalPCMFrameCount) { - targetFrameIndex = pWav->totalPCMFrameCount; - } - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (targetFrameIndex < pWav->readCursorInPCMFrames) { - if (!drwav_seek_to_first_pcm_frame(pWav)) { - return DRWAV_FALSE; - } - } - if (targetFrameIndex > pWav->readCursorInPCMFrames) { - drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; - drwav_int16 devnull[2048]; - while (offsetInFrames > 0) { - drwav_uint64 framesRead = 0; - drwav_uint64 framesToRead = offsetInFrames; - if (framesToRead > drwav_countof(devnull)/pWav->channels) { - framesToRead = drwav_countof(devnull)/pWav->channels; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); - } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); - } else { - DRWAV_ASSERT(DRWAV_FALSE); - } - if (framesRead != framesToRead) { - return DRWAV_FALSE; - } - offsetInFrames -= framesRead; - } - } - } else { - drwav_uint64 totalSizeInBytes; - drwav_uint64 currentBytePos; - drwav_uint64 targetBytePos; - drwav_uint64 offset; - drwav_uint32 bytesPerFrame; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return DRWAV_FALSE; - } - totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; - DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining); - currentBytePos = totalSizeInBytes - pWav->bytesRemaining; - targetBytePos = targetFrameIndex * bytesPerFrame; - if (currentBytePos < targetBytePos) { - offset = (targetBytePos - currentBytePos); - } else { - if (!drwav_seek_to_first_pcm_frame(pWav)) { - return DRWAV_FALSE; - } - offset = targetBytePos; - } - while (offset > 0) { - int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; - pWav->bytesRemaining -= offset32; - offset -= offset32; - } - } - return DRWAV_TRUE; -} -DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor) -{ - if (pCursor == NULL) { - return DRWAV_INVALID_ARGS; - } - *pCursor = 0; - if (pWav == NULL) { - return DRWAV_INVALID_ARGS; - } - *pCursor = pWav->readCursorInPCMFrames; - return DRWAV_SUCCESS; -} -DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength) -{ - if (pLength == NULL) { - return DRWAV_INVALID_ARGS; - } - *pLength = 0; - if (pWav == NULL) { - return DRWAV_INVALID_ARGS; - } - *pLength = pWav->totalPCMFrameCount; - return DRWAV_SUCCESS; -} -DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData) -{ - size_t bytesWritten; - if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { - return 0; - } - bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); - pWav->dataChunkDataSize += bytesWritten; - return bytesWritten; -} -DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) -{ - drwav_uint64 bytesToWrite; - drwav_uint64 bytesWritten; - const drwav_uint8* pRunningData; - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > DRWAV_SIZE_MAX) { - return 0; - } - bytesWritten = 0; - pRunningData = (const drwav_uint8*)pData; - while (bytesToWrite > 0) { - size_t bytesJustWritten; - drwav_uint64 bytesToWriteThisIteration; - bytesToWriteThisIteration = bytesToWrite; - DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); - bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); - if (bytesJustWritten == 0) { - break; - } - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} -DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) -{ - drwav_uint64 bytesToWrite; - drwav_uint64 bytesWritten; - drwav_uint32 bytesPerSample; - const drwav_uint8* pRunningData; - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > DRWAV_SIZE_MAX) { - return 0; - } - bytesWritten = 0; - pRunningData = (const drwav_uint8*)pData; - bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; - if (bytesPerSample == 0) { - return 0; - } - while (bytesToWrite > 0) { - drwav_uint8 temp[4096]; - drwav_uint32 sampleCount; - size_t bytesJustWritten; - drwav_uint64 bytesToWriteThisIteration; - bytesToWriteThisIteration = bytesToWrite; - DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); - sampleCount = sizeof(temp)/bytesPerSample; - if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) { - bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample; - } - DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); - drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag); - bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); - if (bytesJustWritten == 0) { - break; - } - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} -DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) -{ - if (drwav__is_little_endian()) { - return drwav_write_pcm_frames_le(pWav, framesToWrite, pData); - } else { - return drwav_write_pcm_frames_be(pWav, framesToWrite, pData); - } -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead = 0; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(framesToRead > 0); - while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - DRWAV_ASSERT(framesToRead > 0); - if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - drwav_uint8 header[7]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1); - pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3); - pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5); - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrameCount = 2; - } else { - drwav_uint8 header[14]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.predictor[1] = header[1]; - pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2); - pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4); - pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6); - pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8); - pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10); - pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12); - pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.cachedFrameCount = 2; - } - } - while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - if (pBufferOut != NULL) { - drwav_uint32 iSample = 0; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; - } - pBufferOut += pWav->channels; - } - framesToRead -= 1; - totalFramesRead += 1; - pWav->readCursorInPCMFrames += 1; - pWav->msadpcm.cachedFrameCount -= 1; - } - if (framesToRead == 0) { - break; - } - if (pWav->msadpcm.cachedFrameCount == 0) { - if (pWav->msadpcm.bytesRemainingInBlock == 0) { - continue; - } else { - static drwav_int32 adaptationTable[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; - drwav_uint8 nibbles; - drwav_int32 nibble0; - drwav_int32 nibble1; - if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock -= 1; - nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } - nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } - if (pWav->channels == 1) { - drwav_int32 newSample0; - drwav_int32 newSample1; - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = drwav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[0]; - newSample1 = drwav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 2; - } else { - drwav_int32 newSample0; - drwav_int32 newSample1; - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = drwav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[1]; - newSample1 = drwav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; - if (pWav->msadpcm.delta[1] < 16) { - pWav->msadpcm.delta[1] = 16; - } - pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.prevFrames[1][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 1; - } - } - } - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead = 0; - drwav_uint32 iChannel; - static drwav_int32 indexTable[16] = { - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 - }; - static drwav_int32 stepTable[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, - 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, - 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 - }; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(framesToRead > 0); - while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - DRWAV_ASSERT(framesToRead > 0); - if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - drwav_uint8 header[4]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= drwav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); - pWav->ima.bytesRemainingInBlock = 0; - return totalFramesRead; - } - pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; - pWav->ima.cachedFrameCount = 1; - } else { - drwav_uint8 header[8]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); - pWav->ima.bytesRemainingInBlock = 0; - return totalFramesRead; - } - pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4); - pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; - pWav->ima.cachedFrameCount = 1; - } - } - while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - if (pBufferOut != NULL) { - drwav_uint32 iSample; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; - } - pBufferOut += pWav->channels; - } - framesToRead -= 1; - totalFramesRead += 1; - pWav->readCursorInPCMFrames += 1; - pWav->ima.cachedFrameCount -= 1; - } - if (framesToRead == 0) { - break; - } - if (pWav->ima.cachedFrameCount == 0) { - if (pWav->ima.bytesRemainingInBlock == 0) { - continue; - } else { - pWav->ima.cachedFrameCount = 8; - for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { - drwav_uint32 iByte; - drwav_uint8 nibbles[4]; - if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { - pWav->ima.cachedFrameCount = 0; - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock -= 4; - for (iByte = 0; iByte < 4; ++iByte) { - drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); - drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); - drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; - drwav_int32 predictor = pWav->ima.predictor[iChannel]; - drwav_int32 diff = step >> 3; - if (nibble0 & 1) diff += step >> 2; - if (nibble0 & 2) diff += step >> 1; - if (nibble0 & 4) diff += step; - if (nibble0 & 8) diff = -diff; - predictor = drwav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; - step = stepTable[pWav->ima.stepIndex[iChannel]]; - predictor = pWav->ima.predictor[iChannel]; - diff = step >> 3; - if (nibble1 & 1) diff += step >> 2; - if (nibble1 & 2) diff += step >> 1; - if (nibble1 & 4) diff += step; - if (nibble1 & 8) diff = -diff; - predictor = drwav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; - } - } - } - } - } - return totalFramesRead; -} -#ifndef DR_WAV_NO_CONVERSION_API -static unsigned short g_drwavAlawTable[256] = { - 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, - 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, - 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, - 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, - 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, - 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, - 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, - 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, - 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, - 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, - 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, - 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, - 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, - 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, - 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, - 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 -}; -static unsigned short g_drwavMulawTable[256] = { - 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, - 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, - 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, - 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, - 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, - 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, - 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, - 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, - 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, - 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, - 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, - 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, - 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, - 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, - 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, - 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 -}; -static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) -{ - return (short)g_drwavAlawTable[sampleIn]; -} -static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) -{ - return (short)g_drwavMulawTable[sampleIn]; -} -DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - size_t i; - if (bytesPerSample == 1) { - drwav_u8_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const drwav_int16*)pIn)[i]; - } - return; - } - if (bytesPerSample == 3) { - drwav_s24_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); - return; - } - if (bytesPerSample > 8) { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - drwav_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - DRWAV_ASSERT(j < 8); - sample |= (drwav_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (drwav_int16)((drwav_int64)sample >> 48); - } -} -DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { - return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { - return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { - return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { - return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); - } - return 0; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { - drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { - drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = pIn[i]; - r = x << 8; - r = r - 32768; - pOut[i] = (short)r; - } -} -DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8; - r = x >> 8; - pOut[i] = (short)r; - } -} -DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = pIn[i]; - r = x >> 16; - pOut[i] = (short)r; - } -} -DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - float c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5f); - r = r - 32768; - pOut[i] = (short)r; - } -} -DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - double x = pIn[i]; - double c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5); - r = r - 32768; - pOut[i] = (short)r; - } -} -DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - size_t i; - for (i = 0; i < sampleCount; ++i) { - pOut[i] = drwav__alaw_to_s16(pIn[i]); - } -} -DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - size_t i; - for (i = 0; i < sampleCount; ++i) { - pOut[i] = drwav__mulaw_to_s16(pIn[i]); - } -} -DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - drwav_u8_to_f32(pOut, pIn, sampleCount); - return; - } - if (bytesPerSample == 2) { - drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount); - return; - } - if (bytesPerSample == 3) { - drwav_s24_to_f32(pOut, pIn, sampleCount); - return; - } - if (bytesPerSample == 4) { - drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount); - return; - } - if (bytesPerSample > 8) { - DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < sampleCount; ++i) { - drwav_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - DRWAV_ASSERT(j < 8); - sample |= (drwav_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0); - } -} -DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - unsigned int i; - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((const float*)pIn)[i]; - } - return; - } else if (bytesPerSample == 8) { - drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount); - return; - } else { - DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); - return; - } -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_int16 samples16[2048]; - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { - return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { - return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { - return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { - return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { - return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) -{ - drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { - drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) -{ - drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { - drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } -#ifdef DR_WAV_LIBSNDFILE_COMPAT - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (pIn[i] / 256.0f) * 2 - 1; - } -#else - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - x = x * 0.00784313725490196078f; - x = x - 1; - *pOut++ = x; - } -#endif -} -DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] * 0.000030517578125f; - } -} -DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - double x; - drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8); - drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16); - drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24); - x = (double)((drwav_int32)(a | b | c) >> 8); - *pOut++ = (float)(x * 0.00000011920928955078125); - } -} -DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)(pIn[i] / 2147483648.0); - } -} -DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)pIn[i]; - } -} -DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f; - } -} -DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f; - } -} -DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - drwav_u8_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount); - return; - } - if (bytesPerSample == 3) { - drwav_s24_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const drwav_int32*)pIn)[i]; - } - return; - } - if (bytesPerSample > 8) { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - drwav_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - DRWAV_ASSERT(j < 8); - sample |= (drwav_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (drwav_int32)((drwav_int64)sample >> 32); - } -} -DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { - return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) -{ - drwav_uint64 totalFramesRead = 0; - drwav_int16 samples16[2048]; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) -{ - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); - break; - } - drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { - return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { - return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { - return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { - return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) -{ - drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { - drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) -{ - drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { - drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((int)pIn[i] - 128) << 24; - } -} -DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] << 16; - } -} -DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - unsigned int s0 = pIn[i*3 + 0]; - unsigned int s1 = pIn[i*3 + 1]; - unsigned int s2 = pIn[i*3 + 2]; - drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); - *pOut++ = sample32; - } -} -DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); - } -} -DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); - } -} -DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16; - } -} -DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i= 0; i < sampleCount; ++i) { - *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16; - } -} -DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) -{ - drwav_uint64 sampleDataSize; - drwav_int16* pSampleData; - drwav_uint64 framesRead; - DRWAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16); - if (sampleDataSize > DRWAV_SIZE_MAX) { - drwav_uninit(pWav); - return NULL; - } - pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - drwav_uninit(pWav); - return NULL; - } - framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - drwav_uninit(pWav); - return NULL; - } - drwav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) -{ - drwav_uint64 sampleDataSize; - float* pSampleData; - drwav_uint64 framesRead; - DRWAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); - if (sampleDataSize > DRWAV_SIZE_MAX) { - drwav_uninit(pWav); - return NULL; - } - pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - drwav_uninit(pWav); - return NULL; - } - framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - drwav_uninit(pWav); - return NULL; - } - drwav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) -{ - drwav_uint64 sampleDataSize; - drwav_int32* pSampleData; - drwav_uint64 framesRead; - DRWAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32); - if (sampleDataSize > DRWAV_SIZE_MAX) { - drwav_uninit(pWav); - return NULL; - } - pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - drwav_uninit(pWav); - return NULL; - } - framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - drwav_uninit(pWav); - return NULL; - } - drwav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#ifndef DR_WAV_NO_STDIO -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#endif -DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - drwav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#endif -DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - drwav__free_from_callbacks(p, pAllocationCallbacks); - } else { - drwav__free_default(p, NULL); - } -} -DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data) -{ - return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8); -} -DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data) -{ - return (drwav_int16)drwav_bytes_to_u16(data); -} -DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data) -{ - return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24); -} -DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data) -{ - union { - drwav_uint32 u32; - float f32; - } value; - value.u32 = drwav_bytes_to_u32(data); - return value.f32; -} -DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data) -{ - return (drwav_int32)drwav_bytes_to_u32(data); -} -DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data) -{ - return - ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) | - ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56); -} -DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data) -{ - return (drwav_int64)drwav_bytes_to_u64(data); -} -DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) -{ - int i; - for (i = 0; i < 16; i += 1) { - if (a[i] != b[i]) { - return DRWAV_FALSE; - } - } - return DRWAV_TRUE; -} -DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) -{ - return - a[0] == b[0] && - a[1] == b[1] && - a[2] == b[2] && - a[3] == b[3]; -} -#endif -/* dr_wav_c end */ -#endif /* DRWAV_IMPLEMENTATION */ -#endif /* MA_NO_WAV */ - -#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -#if !defined(DR_FLAC_IMPLEMENTATION) && !defined(DRFLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ -/* dr_flac_c begin */ -#ifndef dr_flac_c -#define dr_flac_c -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #if __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #endif -#endif -#ifdef __linux__ - #ifndef _BSD_SOURCE - #define _BSD_SOURCE - #endif - #ifndef _DEFAULT_SOURCE - #define _DEFAULT_SOURCE - #endif - #ifndef __USE_BSD - #define __USE_BSD - #endif - #include -#endif -#include -#include -#ifdef _MSC_VER - #define DRFLAC_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define DRFLAC_GNUC_INLINE_HINT __inline__ - #else - #define DRFLAC_GNUC_INLINE_HINT inline - #endif - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) - #else - #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT - #endif -#elif defined(__WATCOMC__) - #define DRFLAC_INLINE __inline -#else - #define DRFLAC_INLINE -#endif -#if defined(__x86_64__) || defined(_M_X64) - #define DRFLAC_X64 -#elif defined(__i386) || defined(_M_IX86) - #define DRFLAC_X86 -#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) - #define DRFLAC_ARM -#endif -#if !defined(DR_FLAC_NO_SIMD) - #if defined(DRFLAC_X64) || defined(DRFLAC_X86) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) - #define DRFLAC_SUPPORT_SSE2 - #endif - #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) - #define DRFLAC_SUPPORT_SSE41 - #endif - #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) - #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2) - #define DRFLAC_SUPPORT_SSE2 - #endif - #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41) - #define DRFLAC_SUPPORT_SSE41 - #endif - #endif - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include() - #define DRFLAC_SUPPORT_SSE2 - #endif - #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include() - #define DRFLAC_SUPPORT_SSE41 - #endif - #endif - #if defined(DRFLAC_SUPPORT_SSE41) - #include - #elif defined(DRFLAC_SUPPORT_SSE2) - #include - #endif - #endif - #if defined(DRFLAC_ARM) - #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define DRFLAC_SUPPORT_NEON - #include - #endif - #endif -#endif -#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 - #include - static void drflac__cpuid(int info[4], int fid) - { - __cpuid(info, fid); - } - #else - #define DRFLAC_NO_CPUID - #endif - #else - #if defined(__GNUC__) || defined(__clang__) - static void drflac__cpuid(int info[4], int fid) - { - #if defined(DRFLAC_X86) && defined(__PIC__) - __asm__ __volatile__ ( - "xchg{l} {%%}ebx, %k1;" - "cpuid;" - "xchg{l} {%%}ebx, %k1;" - : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #else - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #endif - } - #else - #define DRFLAC_NO_CPUID - #endif - #endif -#else - #define DRFLAC_NO_CPUID -#endif -static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2) - #if defined(DRFLAC_X64) - return DRFLAC_TRUE; - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return DRFLAC_TRUE; - #else - #if defined(DRFLAC_NO_CPUID) - return DRFLAC_FALSE; - #else - int info[4]; - drflac__cpuid(info, 1); - return (info[3] & (1 << 26)) != 0; - #endif - #endif - #else - return DRFLAC_FALSE; - #endif -#else - return DRFLAC_FALSE; -#endif -} -static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) -{ -#if defined(DRFLAC_SUPPORT_SSE41) - #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) - #if defined(DRFLAC_X64) - return DRFLAC_TRUE; - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE4_1__) - return DRFLAC_TRUE; - #else - #if defined(DRFLAC_NO_CPUID) - return DRFLAC_FALSE; - #else - int info[4]; - drflac__cpuid(info, 1); - return (info[2] & (1 << 19)) != 0; - #endif - #endif - #else - return DRFLAC_FALSE; - #endif -#else - return DRFLAC_FALSE; -#endif -} -#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__) - #define DRFLAC_HAS_LZCNT_INTRINSIC -#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define DRFLAC_HAS_LZCNT_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) - #define DRFLAC_HAS_LZCNT_INTRINSIC - #endif - #endif -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC - #endif -#elif defined(__WATCOMC__) && defined(__386__) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC - extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16); - extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32); - extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); -#pragma aux _watcom_bswap16 = \ - "xchg al, ah" \ - parm [ax] \ - modify [ax]; -#pragma aux _watcom_bswap32 = \ - "bswap eax" \ - parm [eax] \ - modify [eax]; -#pragma aux _watcom_bswap64 = \ - "bswap eax" \ - "bswap edx" \ - "xchg eax,edx" \ - parm [eax edx] \ - modify [eax edx]; -#endif -#ifndef DRFLAC_ASSERT -#include -#define DRFLAC_ASSERT(expression) assert(expression) -#endif -#ifndef DRFLAC_MALLOC -#define DRFLAC_MALLOC(sz) malloc((sz)) -#endif -#ifndef DRFLAC_REALLOC -#define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef DRFLAC_FREE -#define DRFLAC_FREE(p) free((p)) -#endif -#ifndef DRFLAC_COPY_MEMORY -#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef DRFLAC_ZERO_MEMORY -#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef DRFLAC_ZERO_OBJECT -#define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p))) -#endif -#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 -typedef drflac_int32 drflac_result; -#define DRFLAC_SUCCESS 0 -#define DRFLAC_ERROR -1 -#define DRFLAC_INVALID_ARGS -2 -#define DRFLAC_INVALID_OPERATION -3 -#define DRFLAC_OUT_OF_MEMORY -4 -#define DRFLAC_OUT_OF_RANGE -5 -#define DRFLAC_ACCESS_DENIED -6 -#define DRFLAC_DOES_NOT_EXIST -7 -#define DRFLAC_ALREADY_EXISTS -8 -#define DRFLAC_TOO_MANY_OPEN_FILES -9 -#define DRFLAC_INVALID_FILE -10 -#define DRFLAC_TOO_BIG -11 -#define DRFLAC_PATH_TOO_LONG -12 -#define DRFLAC_NAME_TOO_LONG -13 -#define DRFLAC_NOT_DIRECTORY -14 -#define DRFLAC_IS_DIRECTORY -15 -#define DRFLAC_DIRECTORY_NOT_EMPTY -16 -#define DRFLAC_END_OF_FILE -17 -#define DRFLAC_NO_SPACE -18 -#define DRFLAC_BUSY -19 -#define DRFLAC_IO_ERROR -20 -#define DRFLAC_INTERRUPT -21 -#define DRFLAC_UNAVAILABLE -22 -#define DRFLAC_ALREADY_IN_USE -23 -#define DRFLAC_BAD_ADDRESS -24 -#define DRFLAC_BAD_SEEK -25 -#define DRFLAC_BAD_PIPE -26 -#define DRFLAC_DEADLOCK -27 -#define DRFLAC_TOO_MANY_LINKS -28 -#define DRFLAC_NOT_IMPLEMENTED -29 -#define DRFLAC_NO_MESSAGE -30 -#define DRFLAC_BAD_MESSAGE -31 -#define DRFLAC_NO_DATA_AVAILABLE -32 -#define DRFLAC_INVALID_DATA -33 -#define DRFLAC_TIMEOUT -34 -#define DRFLAC_NO_NETWORK -35 -#define DRFLAC_NOT_UNIQUE -36 -#define DRFLAC_NOT_SOCKET -37 -#define DRFLAC_NO_ADDRESS -38 -#define DRFLAC_BAD_PROTOCOL -39 -#define DRFLAC_PROTOCOL_UNAVAILABLE -40 -#define DRFLAC_PROTOCOL_NOT_SUPPORTED -41 -#define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define DRFLAC_SOCKET_NOT_SUPPORTED -44 -#define DRFLAC_CONNECTION_RESET -45 -#define DRFLAC_ALREADY_CONNECTED -46 -#define DRFLAC_NOT_CONNECTED -47 -#define DRFLAC_CONNECTION_REFUSED -48 -#define DRFLAC_NO_HOST -49 -#define DRFLAC_IN_PROGRESS -50 -#define DRFLAC_CANCELLED -51 -#define DRFLAC_MEMORY_ALREADY_MAPPED -52 -#define DRFLAC_AT_END -53 -#define DRFLAC_CRC_MISMATCH -128 -#define DRFLAC_SUBFRAME_CONSTANT 0 -#define DRFLAC_SUBFRAME_VERBATIM 1 -#define DRFLAC_SUBFRAME_FIXED 8 -#define DRFLAC_SUBFRAME_LPC 32 -#define DRFLAC_SUBFRAME_RESERVED 255 -#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 -#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 -#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 -#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 -#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 -#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 -#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) -{ - if (pMajor) { - *pMajor = DRFLAC_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = DRFLAC_VERSION_MINOR; - } - if (pRevision) { - *pRevision = DRFLAC_VERSION_REVISION; - } -} -DRFLAC_API const char* drflac_version_string(void) -{ - return DRFLAC_VERSION_STRING; -} -#if defined(__has_feature) - #if __has_feature(thread_sanitizer) - #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) - #else - #define DRFLAC_NO_THREAD_SANITIZE - #endif -#else - #define DRFLAC_NO_THREAD_SANITIZE -#endif -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) -static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; -#endif -#ifndef DRFLAC_NO_CPUID -static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE; -static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE; -DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) -{ - static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; - if (!isCPUCapsInitialized) { -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) - int info[4] = {0}; - drflac__cpuid(info, 0x80000001); - drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; -#endif - drflac__gIsSSE2Supported = drflac_has_sse2(); - drflac__gIsSSE41Supported = drflac_has_sse41(); - isCPUCapsInitialized = DRFLAC_TRUE; - } -} -#else -static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE; -static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void) -{ -#if defined(DRFLAC_SUPPORT_NEON) - #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON) - #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return DRFLAC_TRUE; - #else - return DRFLAC_FALSE; - #endif - #else - return DRFLAC_FALSE; - #endif -#else - return DRFLAC_FALSE; -#endif -} -DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) -{ - drflac__gIsNEONSupported = drflac__has_neon(); -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - drflac__gIsLZCNTSupported = DRFLAC_TRUE; -#endif -} -#endif -static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) -{ -#if defined(DRFLAC_X86) || defined(DRFLAC_X64) - return DRFLAC_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return DRFLAC_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} -static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) -{ -#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} -static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) -{ -#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT) - drflac_uint32 r; - __asm__ __volatile__ ( - #if defined(DRFLAC_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap32(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} -static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) -{ -#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) | - ((n & ((drflac_uint64)0xFF000000 )) << 8) | - ((n & ((drflac_uint64)0x00FF0000 )) << 24) | - ((n & ((drflac_uint64)0x0000FF00 )) << 40) | - ((n & ((drflac_uint64)0x000000FF )) << 56); -#endif -} -static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) -{ - if (drflac__is_little_endian()) { - return drflac__swap_endian_uint16(n); - } - return n; -} -static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) -{ - if (drflac__is_little_endian()) { - return drflac__swap_endian_uint32(n); - } - return n; -} -static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) -{ - const drflac_uint8* pNum = (drflac_uint8*)pData; - return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); -} -static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) -{ - if (drflac__is_little_endian()) { - return drflac__swap_endian_uint64(n); - } - return n; -} -static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) -{ - if (!drflac__is_little_endian()) { - return drflac__swap_endian_uint32(n); - } - return n; -} -static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) -{ - const drflac_uint8* pNum = (drflac_uint8*)pData; - return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; -} -static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) -{ - drflac_uint32 result = 0; - result |= (n & 0x7F000000) >> 3; - result |= (n & 0x007F0000) >> 2; - result |= (n & 0x00007F00) >> 1; - result |= (n & 0x0000007F) >> 0; - return result; -} -static drflac_uint8 drflac__crc8_table[] = { - 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, - 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, - 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, - 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, - 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, - 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, - 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, - 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, - 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, - 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, - 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, - 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, - 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, - 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, - 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, - 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 -}; -static drflac_uint16 drflac__crc16_table[] = { - 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, - 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, - 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, - 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, - 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, - 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, - 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, - 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, - 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, - 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, - 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, - 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, - 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, - 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, - 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, - 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, - 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, - 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, - 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, - 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, - 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, - 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, - 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, - 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, - 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, - 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, - 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, - 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, - 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, - 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, - 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, - 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 -}; -static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) -{ - return drflac__crc8_table[crc ^ data]; -} -static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) -{ -#ifdef DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else -#if 0 - drflac_uint8 p = 0x07; - for (int i = count-1; i >= 0; --i) { - drflac_uint8 bit = (data & (1 << i)) >> i; - if (crc & 0x80) { - crc = ((crc << 1) | bit) ^ p; - } else { - crc = ((crc << 1) | bit); - } - } - return crc; -#else - drflac_uint32 wholeBytes; - drflac_uint32 leftoverBits; - drflac_uint64 leftoverDataMask; - static drflac_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - DRFLAC_ASSERT(count <= 32); - wholeBytes = count >> 3; - leftoverBits = count - (wholeBytes*8); - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); - } - return crc; -#endif -#endif -} -static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) -{ - return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; -} -static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data) -{ -#ifdef DRFLAC_64BIT - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); -#endif - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); - return crc; -} -static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) -{ - switch (byteCount) - { -#ifdef DRFLAC_64BIT - case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); - case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); - case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); - case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); -#endif - case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); - case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); - case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); - case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); - } - return crc; -} -#if 0 -static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) -{ -#ifdef DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else -#if 0 - drflac_uint16 p = 0x8005; - for (int i = count-1; i >= 0; --i) { - drflac_uint16 bit = (data & (1ULL << i)) >> i; - if (r & 0x8000) { - r = ((r << 1) | bit) ^ p; - } else { - r = ((r << 1) | bit); - } - } - return crc; -#else - drflac_uint32 wholeBytes; - drflac_uint32 leftoverBits; - drflac_uint64 leftoverDataMask; - static drflac_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - DRFLAC_ASSERT(count <= 64); - wholeBytes = count >> 3; - leftoverBits = count & 7; - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - default: - case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; - } - return crc; -#endif -#endif -} -static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) -{ -#ifdef DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else - drflac_uint32 wholeBytes; - drflac_uint32 leftoverBits; - drflac_uint64 leftoverDataMask; - static drflac_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - DRFLAC_ASSERT(count <= 64); - wholeBytes = count >> 3; - leftoverBits = count & 7; - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - default: - case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); - case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); - case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); - case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); - case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; - } - return crc; -#endif -} -static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) -{ -#ifdef DRFLAC_64BIT - return drflac_crc16__64bit(crc, data, count); -#else - return drflac_crc16__32bit(crc, data, count); -#endif -} -#endif -#ifdef DRFLAC_64BIT -#define drflac__be2host__cache_line drflac__be2host_64 -#else -#define drflac__be2host__cache_line drflac__be2host_32 -#endif -#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) -#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) -#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) -#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount))) -#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) -#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) -#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) -#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1))) -#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) -#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) -#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) -#ifndef DR_FLAC_NO_CRC -static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) -{ - bs->crc16 = 0; - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; -} -static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) -{ - if (bs->crc16CacheIgnoredBytes == 0) { - bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache); - } else { - bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); - bs->crc16CacheIgnoredBytes = 0; - } -} -static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) -{ - DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); - if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { - drflac__update_crc16(bs); - } else { - bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; - } - return bs->crc16; -} -#endif -static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) -{ - size_t bytesRead; - size_t alignedL1LineCount; - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; - } - if (bs->unalignedByteCount > 0) { - return DRFLAC_FALSE; - } - bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); - bs->nextL2Line = 0; - if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; - } - alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); - bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); - if (bs->unalignedByteCount > 0) { - bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; - } - if (alignedL1LineCount > 0) { - size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; - size_t i; - for (i = alignedL1LineCount; i > 0; --i) { - bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; - } - bs->nextL2Line = (drflac_uint32)offset; - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; - } else { - bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); - return DRFLAC_FALSE; - } -} -static drflac_bool32 drflac__reload_cache(drflac_bs* bs) -{ - size_t bytesRead; -#ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); -#endif - if (drflac__reload_l1_cache_from_l2(bs)) { - bs->cache = drflac__be2host__cache_line(bs->cache); - bs->consumedBits = 0; -#ifndef DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; -#endif - return DRFLAC_TRUE; - } - bytesRead = bs->unalignedByteCount; - if (bytesRead == 0) { - bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); - return DRFLAC_FALSE; - } - DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); - bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; - bs->cache = drflac__be2host__cache_line(bs->unalignedCache); - bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); - bs->unalignedByteCount = 0; -#ifndef DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache >> bs->consumedBits; - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; -#endif - return DRFLAC_TRUE; -} -static void drflac__reset_cache(drflac_bs* bs) -{ - bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); - bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); - bs->cache = 0; - bs->unalignedByteCount = 0; - bs->unalignedCache = 0; -#ifndef DR_FLAC_NO_CRC - bs->crc16Cache = 0; - bs->crc16CacheIgnoredBytes = 0; -#endif -} -static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) -{ - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResultOut != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 32); - if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - } - if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { -#ifdef DRFLAC_64BIT - *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); - bs->consumedBits += bitCount; - bs->cache <<= bitCount; -#else - if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); - bs->consumedBits += bitCount; - bs->cache <<= bitCount; - } else { - *pResultOut = (drflac_uint32)bs->cache; - bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); - bs->cache = 0; - } -#endif - return DRFLAC_TRUE; - } else { - drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); - drflac_uint32 bitCountLo = bitCount - bitCountHi; - drflac_uint32 resultHi; - DRFLAC_ASSERT(bitCountHi > 0); - DRFLAC_ASSERT(bitCountHi < 32); - resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; - } - *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - return DRFLAC_TRUE; - } -} -static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) -{ - drflac_uint32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 32); - if (!drflac__read_uint32(bs, bitCount, &result)) { - return DRFLAC_FALSE; - } - if (bitCount < 32) { - drflac_uint32 signbit; - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; - } - *pResult = (drflac_int32)result; - return DRFLAC_TRUE; -} -#ifdef DRFLAC_64BIT -static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) -{ - drflac_uint32 resultHi; - drflac_uint32 resultLo; - DRFLAC_ASSERT(bitCount <= 64); - DRFLAC_ASSERT(bitCount > 32); - if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { - return DRFLAC_FALSE; - } - if (!drflac__read_uint32(bs, 32, &resultLo)) { - return DRFLAC_FALSE; - } - *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); - return DRFLAC_TRUE; -} -#endif -#if 0 -static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) -{ - drflac_uint64 result; - drflac_uint64 signbit; - DRFLAC_ASSERT(bitCount <= 64); - if (!drflac__read_uint64(bs, bitCount, &result)) { - return DRFLAC_FALSE; - } - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; - *pResultOut = (drflac_int64)result; - return DRFLAC_TRUE; -} -#endif -static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) -{ - drflac_uint32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 16); - if (!drflac__read_uint32(bs, bitCount, &result)) { - return DRFLAC_FALSE; - } - *pResult = (drflac_uint16)result; - return DRFLAC_TRUE; -} -#if 0 -static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) -{ - drflac_int32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 16); - if (!drflac__read_int32(bs, bitCount, &result)) { - return DRFLAC_FALSE; - } - *pResult = (drflac_int16)result; - return DRFLAC_TRUE; -} -#endif -static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) -{ - drflac_uint32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 8); - if (!drflac__read_uint32(bs, bitCount, &result)) { - return DRFLAC_FALSE; - } - *pResult = (drflac_uint8)result; - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) -{ - drflac_int32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 8); - if (!drflac__read_int32(bs, bitCount, &result)) { - return DRFLAC_FALSE; - } - *pResult = (drflac_int8)result; - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) -{ - if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - bs->consumedBits += (drflac_uint32)bitsToSeek; - bs->cache <<= bitsToSeek; - return DRFLAC_TRUE; - } else { - bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); - bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); - bs->cache = 0; -#ifdef DRFLAC_64BIT - while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - drflac_uint64 bin; - if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return DRFLAC_FALSE; - } - bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); - } -#else - while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - drflac_uint32 bin; - if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return DRFLAC_FALSE; - } - bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); - } -#endif - while (bitsToSeek >= 8) { - drflac_uint8 bin; - if (!drflac__read_uint8(bs, 8, &bin)) { - return DRFLAC_FALSE; - } - bitsToSeek -= 8; - } - if (bitsToSeek > 0) { - drflac_uint8 bin; - if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { - return DRFLAC_FALSE; - } - bitsToSeek = 0; - } - DRFLAC_ASSERT(bitsToSeek == 0); - return DRFLAC_TRUE; - } -} -static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) -{ - DRFLAC_ASSERT(bs != NULL); - if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return DRFLAC_FALSE; - } - for (;;) { - drflac_uint8 hi; -#ifndef DR_FLAC_NO_CRC - drflac__reset_crc16(bs); -#endif - if (!drflac__read_uint8(bs, 8, &hi)) { - return DRFLAC_FALSE; - } - if (hi == 0xFF) { - drflac_uint8 lo; - if (!drflac__read_uint8(bs, 6, &lo)) { - return DRFLAC_FALSE; - } - if (lo == 0x3E) { - return DRFLAC_TRUE; - } else { - if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return DRFLAC_FALSE; - } - } - } - } -} -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) -#define DRFLAC_IMPLEMENT_CLZ_LZCNT -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__) -#define DRFLAC_IMPLEMENT_CLZ_MSVC -#endif -#if defined(__WATCOMC__) && defined(__386__) -#define DRFLAC_IMPLEMENT_CLZ_WATCOM -#endif -static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) -{ - drflac_uint32 n; - static drflac_uint32 clz_table_4[] = { - 0, - 4, - 3, 3, - 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1 - }; - if (x == 0) { - return sizeof(x)*8; - } - n = clz_table_4[x >> (sizeof(x)*8 - 4)]; - if (n == 0) { -#ifdef DRFLAC_64BIT - if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } - if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } - if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } - if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } -#else - if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } - if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } - if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } -#endif - n += clz_table_4[x >> (sizeof(x)*8 - 4)]; - } - return n - 1; -} -#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT -static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) -{ -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - return DRFLAC_TRUE; -#else - #ifdef DRFLAC_HAS_LZCNT_INTRINSIC - return drflac__gIsLZCNTSupported; - #else - return DRFLAC_FALSE; - #endif -#endif -} -static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) -{ -#if defined(_MSC_VER) - #ifdef DRFLAC_64BIT - return (drflac_uint32)__lzcnt64(x); - #else - return (drflac_uint32)__lzcnt(x); - #endif -#else - #if defined(__GNUC__) || defined(__clang__) - #if defined(DRFLAC_X64) - { - drflac_uint64 r; - __asm__ __volatile__ ( - "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" - ); - return (drflac_uint32)r; - } - #elif defined(DRFLAC_X86) - { - drflac_uint32 r; - __asm__ __volatile__ ( - "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" - ); - return r; - } - #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT) - { - unsigned int r; - __asm__ __volatile__ ( - #if defined(DRFLAC_64BIT) - "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) - #else - "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) - #endif - ); - return r; - } - #else - if (x == 0) { - return sizeof(x)*8; - } - #ifdef DRFLAC_64BIT - return (drflac_uint32)__builtin_clzll((drflac_uint64)x); - #else - return (drflac_uint32)__builtin_clzl((drflac_uint32)x); - #endif - #endif - #else - #error "This compiler does not support the lzcnt intrinsic." - #endif -#endif -} -#endif -#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC -#include -static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) -{ - drflac_uint32 n; - if (x == 0) { - return sizeof(x)*8; - } -#ifdef DRFLAC_64BIT - _BitScanReverse64((unsigned long*)&n, x); -#else - _BitScanReverse((unsigned long*)&n, x); -#endif - return sizeof(x)*8 - n - 1; -} -#endif -#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM -static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); -#pragma aux drflac__clz_watcom = \ - "bsr eax, eax" \ - "xor eax, 31" \ - parm [eax] nomemory \ - value [eax] \ - modify exact [eax] nomemory; -#endif -static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) -{ -#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT - if (drflac__is_lzcnt_supported()) { - return drflac__clz_lzcnt(x); - } else -#endif - { -#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC - return drflac__clz_msvc(x); -#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) - return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); -#else - return drflac__clz_software(x); -#endif - } -} -static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) -{ - drflac_uint32 zeroCounter = 0; - drflac_uint32 setBitOffsetPlus1; - while (bs->cache == 0) { - zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - } - if (bs->cache == 1) { - *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - return DRFLAC_TRUE; - } - setBitOffsetPlus1 = drflac__clz(bs->cache); - setBitOffsetPlus1 += 1; - if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; - } - bs->consumedBits += setBitOffsetPlus1; - bs->cache <<= setBitOffsetPlus1; - *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) -{ - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(offsetFromStart > 0); - if (offsetFromStart > 0x7FFFFFFF) { - drflac_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { - return DRFLAC_FALSE; - } - bytesRemaining -= 0x7FFFFFFF; - while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - bytesRemaining -= 0x7FFFFFFF; - } - if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - } - } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { - return DRFLAC_FALSE; - } - } - drflac__reset_cache(bs); - return DRFLAC_TRUE; -} -static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) -{ - drflac_uint8 crc; - drflac_uint64 result; - drflac_uint8 utf8[7] = {0}; - int byteCount; - int i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pNumberOut != NULL); - DRFLAC_ASSERT(pCRCOut != NULL); - crc = *pCRCOut; - if (!drflac__read_uint8(bs, 8, utf8)) { - *pNumberOut = 0; - return DRFLAC_AT_END; - } - crc = drflac_crc8(crc, utf8[0], 8); - if ((utf8[0] & 0x80) == 0) { - *pNumberOut = utf8[0]; - *pCRCOut = crc; - return DRFLAC_SUCCESS; - } - if ((utf8[0] & 0xE0) == 0xC0) { - byteCount = 2; - } else if ((utf8[0] & 0xF0) == 0xE0) { - byteCount = 3; - } else if ((utf8[0] & 0xF8) == 0xF0) { - byteCount = 4; - } else if ((utf8[0] & 0xFC) == 0xF8) { - byteCount = 5; - } else if ((utf8[0] & 0xFE) == 0xFC) { - byteCount = 6; - } else if ((utf8[0] & 0xFF) == 0xFE) { - byteCount = 7; - } else { - *pNumberOut = 0; - return DRFLAC_CRC_MISMATCH; - } - DRFLAC_ASSERT(byteCount > 1); - result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); - for (i = 1; i < byteCount; ++i) { - if (!drflac__read_uint8(bs, 8, utf8 + i)) { - *pNumberOut = 0; - return DRFLAC_AT_END; - } - crc = drflac_crc8(crc, utf8[i], 8); - result = (result << 6) | (utf8[i] & 0x3F); - } - *pNumberOut = result; - *pCRCOut = crc; - return DRFLAC_SUCCESS; -} -static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) -{ -#if 1 - drflac_uint32 result = 0; - while (x > 0) { - result += 1; - x >>= 1; - } - return result; -#endif -} -static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) -{ - return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; -} -#if defined(__clang__) -__attribute__((no_sanitize("signed-integer-overflow"))) -#endif -static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) -{ - drflac_int32 prediction = 0; - DRFLAC_ASSERT(order <= 32); - switch (order) - { - case 32: prediction += coefficients[31] * pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; - } - return (drflac_int32)(prediction >> shift); -} -static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) -{ - drflac_int64 prediction; - DRFLAC_ASSERT(order <= 32); -#ifndef DRFLAC_64BIT - if (order == 8) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - } - else if (order == 7) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - } - else if (order == 3) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - } - else if (order == 6) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - } - else if (order == 5) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - } - else if (order == 4) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - } - else if (order == 12) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; - prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; - } - else if (order == 2) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - } - else if (order == 1) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - } - else if (order == 10) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; - } - else if (order == 9) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; - } - else if (order == 11) - { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; - } - else - { - int j; - prediction = 0; - for (j = 0; j < (int)order; ++j) { - prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; - } - } -#endif -#ifdef DRFLAC_64BIT - prediction = 0; - switch (order) - { - case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; - } -#endif - return (drflac_int32)(prediction >> shift); -} -#if 0 -static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); - for (i = 0; i < count; ++i) { - drflac_uint32 zeroCounter = 0; - for (;;) { - drflac_uint8 bit; - if (!drflac__read_uint8(bs, 1, &bit)) { - return DRFLAC_FALSE; - } - if (bit == 0) { - zeroCounter += 1; - } else { - break; - } - } - drflac_uint32 decodedRice; - if (riceParam > 0) { - if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { - return DRFLAC_FALSE; - } - } else { - decodedRice = 0; - } - decodedRice |= (zeroCounter << riceParam); - if ((decodedRice & 0x01)) { - decodedRice = ~(decodedRice >> 1); - } else { - decodedRice = (decodedRice >> 1); - } - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } - } - return DRFLAC_TRUE; -} -#endif -#if 0 -static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) -{ - drflac_uint32 zeroCounter = 0; - drflac_uint32 decodedRice; - for (;;) { - drflac_uint8 bit; - if (!drflac__read_uint8(bs, 1, &bit)) { - return DRFLAC_FALSE; - } - if (bit == 0) { - zeroCounter += 1; - } else { - break; - } - } - if (riceParam > 0) { - if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { - return DRFLAC_FALSE; - } - } else { - decodedRice = 0; - } - *pZeroCounterOut = zeroCounter; - *pRiceParamPartOut = decodedRice; - return DRFLAC_TRUE; -} -#endif -#if 0 -static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) -{ - drflac_cache_t riceParamMask; - drflac_uint32 zeroCounter; - drflac_uint32 setBitOffsetPlus1; - drflac_uint32 riceParamPart; - drflac_uint32 riceLength; - DRFLAC_ASSERT(riceParam > 0); - riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); - zeroCounter = 0; - while (bs->cache == 0) { - zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - } - setBitOffsetPlus1 = drflac__clz(bs->cache); - zeroCounter += setBitOffsetPlus1; - setBitOffsetPlus1 += 1; - riceLength = setBitOffsetPlus1 + riceParam; - if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); - bs->consumedBits += riceLength; - bs->cache <<= riceLength; - } else { - drflac_uint32 bitCountLo; - drflac_cache_t resultHi; - bs->consumedBits += riceLength; - bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); - bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); - resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { -#ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); -#endif - bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs->consumedBits = 0; -#ifndef DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; -#endif - } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; - } - } - riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - } - pZeroCounterOut[0] = zeroCounter; - pRiceParamPartOut[0] = riceParamPart; - return DRFLAC_TRUE; -} -#endif -static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) -{ - drflac_uint32 riceParamPlus1 = riceParam + 1; - drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); - drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - drflac_cache_t bs_cache = bs->cache; - drflac_uint32 bs_consumedBits = bs->consumedBits; - drflac_uint32 lzcount = drflac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - pZeroCounterOut[0] = lzcount; - extract_rice_param_part: - bs_cache <<= lzcount; - bs_consumedBits += lzcount; - if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); - bs_cache <<= riceParamPlus1; - bs_consumedBits += riceParamPlus1; - } else { - drflac_uint32 riceParamPartHi; - drflac_uint32 riceParamPartLo; - drflac_uint32 riceParamPartLoBitCount; - riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); - riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); - #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = riceParamPartLoBitCount; - #ifndef DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; - } - riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); - pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; - bs_cache <<= riceParamPartLoBitCount; - } - } else { - drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); - for (;;) { - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); - #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = 0; - #ifndef DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits; - } - lzcount = drflac__clz(bs_cache); - zeroCounter += lzcount; - if (lzcount < sizeof(bs_cache)*8) { - break; - } - } - pZeroCounterOut[0] = zeroCounter; - goto extract_rice_param_part; - } - bs->cache = bs_cache; - bs->consumedBits = bs_consumedBits; - return DRFLAC_TRUE; -} -static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam) -{ - drflac_uint32 riceParamPlus1 = riceParam + 1; - drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - drflac_cache_t bs_cache = bs->cache; - drflac_uint32 bs_consumedBits = bs->consumedBits; - drflac_uint32 lzcount = drflac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - extract_rice_param_part: - bs_cache <<= lzcount; - bs_consumedBits += lzcount; - if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - bs_cache <<= riceParamPlus1; - bs_consumedBits += riceParamPlus1; - } else { - drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); - #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = riceParamPartLoBitCount; - #ifndef DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; - } - bs_cache <<= riceParamPartLoBitCount; - } - } else { - for (;;) { - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); - #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = 0; - #ifndef DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits; - } - lzcount = drflac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - break; - } - } - goto extract_rice_param_part; - } - bs->cache = bs_cache; - bs->consumedBits = bs_consumedBits; - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ - drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - drflac_uint32 zeroCountPart0; - drflac_uint32 riceParamPart0; - drflac_uint32 riceParamMask; - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); - (void)bitsPerSample; - (void)order; - (void)shift; - (void)coefficients; - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); - i = 0; - while (i < count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return DRFLAC_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - pSamplesOut[i] = riceParamPart0; - i += 1; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ - drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - drflac_uint32 zeroCountPart0 = 0; - drflac_uint32 zeroCountPart1 = 0; - drflac_uint32 zeroCountPart2 = 0; - drflac_uint32 zeroCountPart3 = 0; - drflac_uint32 riceParamPart0 = 0; - drflac_uint32 riceParamPart1 = 0; - drflac_uint32 riceParamPart2 = 0; - drflac_uint32 riceParamPart3 = 0; - drflac_uint32 riceParamMask; - const drflac_int32* pSamplesOutEnd; - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder == 0) { - return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); - pSamplesOutEnd = pSamplesOut + (count & ~3); - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - while (pSamplesOut < pSamplesOutEnd) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return DRFLAC_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart1 &= riceParamMask; - riceParamPart2 &= riceParamMask; - riceParamPart3 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart1 |= (zeroCountPart1 << riceParam); - riceParamPart2 |= (zeroCountPart2 << riceParam); - riceParamPart3 |= (zeroCountPart3 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; - riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; - riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); - pSamplesOut += 4; - } - } else { - while (pSamplesOut < pSamplesOutEnd) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return DRFLAC_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart1 &= riceParamMask; - riceParamPart2 &= riceParamMask; - riceParamPart3 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart1 |= (zeroCountPart1 << riceParam); - riceParamPart2 |= (zeroCountPart2 << riceParam); - riceParamPart3 |= (zeroCountPart3 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; - riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; - riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); - pSamplesOut += 4; - } - } - i = (count & ~3); - while (i < count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return DRFLAC_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - } else { - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - } - i += 1; - pSamplesOut += 1; - } - return DRFLAC_TRUE; -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b) -{ - __m128i r; - r = _mm_packs_epi32(a, b); - r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); - r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); - r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); - return r; -} -#endif -#if defined(DRFLAC_SUPPORT_SSE41) -static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a) -{ - return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); -} -static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x) -{ - __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); - __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); - return _mm_add_epi32(x64, x32); -} -static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x) -{ - return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); -} -static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count) -{ - __m128i lo = _mm_srli_epi64(x, count); - __m128i hi = _mm_srai_epi32(x, count); - hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); - return _mm_or_si128(lo, hi); -} -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ - int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts0 = 0; - drflac_uint32 zeroCountParts1 = 0; - drflac_uint32 zeroCountParts2 = 0; - drflac_uint32 zeroCountParts3 = 0; - drflac_uint32 riceParamParts0 = 0; - drflac_uint32 riceParamParts1 = 0; - drflac_uint32 riceParamParts2 = 0; - drflac_uint32 riceParamParts3 = 0; - __m128i coefficients128_0; - __m128i coefficients128_4; - __m128i coefficients128_8; - __m128i samples128_0; - __m128i samples128_4; - __m128i samples128_8; - __m128i riceParamMask128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); - riceParamMask128 = _mm_set1_epi32(riceParamMask); - coefficients128_0 = _mm_setzero_si128(); - coefficients128_4 = _mm_setzero_si128(); - coefficients128_8 = _mm_setzero_si128(); - samples128_0 = _mm_setzero_si128(); - samples128_4 = _mm_setzero_si128(); - samples128_8 = _mm_setzero_si128(); -#if 1 - { - int runningOrder = order; - if (runningOrder >= 4) { - coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); - samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; - case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; - case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); - samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; - case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; - case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); - samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; - case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; - case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; - } - runningOrder = 0; - } - coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); - } -#else - switch (order) - { - case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; - } -#endif - while (pDecodedSamples < pDecodedSamplesEnd) { - __m128i prediction128; - __m128i zeroCountPart128; - __m128i riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return DRFLAC_FALSE; - } - zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); - riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); - riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); - riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); - if (order <= 4) { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); - prediction128 = drflac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } else if (order <= 8) { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = drflac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } else { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = drflac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } - _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return DRFLAC_FALSE; - } - riceParamParts0 &= riceParamMask; - riceParamParts0 |= (zeroCountParts0 << riceParam); - riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ - int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts0 = 0; - drflac_uint32 zeroCountParts1 = 0; - drflac_uint32 zeroCountParts2 = 0; - drflac_uint32 zeroCountParts3 = 0; - drflac_uint32 riceParamParts0 = 0; - drflac_uint32 riceParamParts1 = 0; - drflac_uint32 riceParamParts2 = 0; - drflac_uint32 riceParamParts3 = 0; - __m128i coefficients128_0; - __m128i coefficients128_4; - __m128i coefficients128_8; - __m128i samples128_0; - __m128i samples128_4; - __m128i samples128_8; - __m128i prediction128; - __m128i riceParamMask128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - DRFLAC_ASSERT(order <= 12); - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); - riceParamMask128 = _mm_set1_epi32(riceParamMask); - prediction128 = _mm_setzero_si128(); - coefficients128_0 = _mm_setzero_si128(); - coefficients128_4 = _mm_setzero_si128(); - coefficients128_8 = _mm_setzero_si128(); - samples128_0 = _mm_setzero_si128(); - samples128_4 = _mm_setzero_si128(); - samples128_8 = _mm_setzero_si128(); -#if 1 - { - int runningOrder = order; - if (runningOrder >= 4) { - coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); - samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; - case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; - case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); - samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; - case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; - case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); - samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; - case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; - case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; - } - runningOrder = 0; - } - coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); - } -#else - switch (order) - { - case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; - } -#endif - while (pDecodedSamples < pDecodedSamplesEnd) { - __m128i zeroCountPart128; - __m128i riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return DRFLAC_FALSE; - } - zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); - riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); - riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); - riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_xor_si128(prediction128, prediction128); - switch (order) - { - case 12: - case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); - case 10: - case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); - case 8: - case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); - case 6: - case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); - case 4: - case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); - case 2: - case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); - } - prediction128 = drflac__mm_hadd_epi64(prediction128); - prediction128 = drflac__mm_srai_epi64(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return DRFLAC_FALSE; - } - riceParamParts0 &= riceParamMask; - riceParamParts0 |= (zeroCountParts0 << riceParam); - riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder > 0 && lpcOrder <= 12) { - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } else { - return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - } else { - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x) -{ - vst1q_s32(p+0, x.val[0]); - vst1q_s32(p+4, x.val[1]); -} -static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x) -{ - vst1q_u32(p+0, x.val[0]); - vst1q_u32(p+4, x.val[1]); -} -static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x) -{ - vst1q_f32(p+0, x.val[0]); - vst1q_f32(p+4, x.val[1]); -} -static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x) -{ - vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); -} -static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x) -{ - vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); -} -static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0) -{ - drflac_int32 x[4]; - x[3] = x3; - x[2] = x2; - x[1] = x1; - x[0] = x0; - return vld1q_s32(x); -} -static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b) -{ - return vextq_s32(b, a, 1); -} -static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) -{ - return vextq_u32(b, a, 1); -} -static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x) -{ - int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); - return vpadd_s32(r, r); -} -static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x) -{ - return vadd_s64(vget_high_s64(x), vget_low_s64(x)); -} -static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x) -{ - return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); -} -static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x) -{ - return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); -} -static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x) -{ - return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); -} -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ - int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts[4]; - drflac_uint32 riceParamParts[4]; - int32x4_t coefficients128_0; - int32x4_t coefficients128_4; - int32x4_t coefficients128_8; - int32x4_t samples128_0; - int32x4_t samples128_4; - int32x4_t samples128_8; - uint32x4_t riceParamMask128; - int32x4_t riceParam128; - int32x2_t shift64; - uint32x4_t one128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = ~((~0UL) << riceParam); - riceParamMask128 = vdupq_n_u32(riceParamMask); - riceParam128 = vdupq_n_s32(riceParam); - shift64 = vdup_n_s32(-shift); - one128 = vdupq_n_u32(1); - { - int runningOrder = order; - drflac_int32 tempC[4] = {0, 0, 0, 0}; - drflac_int32 tempS[4] = {0, 0, 0, 0}; - if (runningOrder >= 4) { - coefficients128_0 = vld1q_s32(coefficients + 0); - samples128_0 = vld1q_s32(pSamplesOut - 4); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; - case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; - case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; - } - coefficients128_0 = vld1q_s32(tempC); - samples128_0 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = vld1q_s32(coefficients + 4); - samples128_4 = vld1q_s32(pSamplesOut - 8); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; - case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; - case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; - } - coefficients128_4 = vld1q_s32(tempC); - samples128_4 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = vld1q_s32(coefficients + 8); - samples128_8 = vld1q_s32(pSamplesOut - 12); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; - case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; - case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; - } - coefficients128_8 = vld1q_s32(tempC); - samples128_8 = vld1q_s32(tempS); - runningOrder = 0; - } - coefficients128_0 = drflac__vrevq_s32(coefficients128_0); - coefficients128_4 = drflac__vrevq_s32(coefficients128_4); - coefficients128_8 = drflac__vrevq_s32(coefficients128_8); - } - while (pDecodedSamples < pDecodedSamplesEnd) { - int32x4_t prediction128; - int32x2_t prediction64; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return DRFLAC_FALSE; - } - zeroCountPart128 = vld1q_u32(zeroCountParts); - riceParamPart128 = vld1q_u32(riceParamParts); - riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); - riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); - if (order <= 4) { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_0, samples128_0); - prediction64 = drflac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } else if (order <= 8) { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_4, samples128_4); - prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = drflac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } else { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_8, samples128_8); - prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); - prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = drflac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } - vst1q_s32(pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return DRFLAC_FALSE; - } - riceParamParts[0] &= riceParamMask; - riceParamParts[0] |= (zeroCountParts[0] << riceParam); - riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ - int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts[4]; - drflac_uint32 riceParamParts[4]; - int32x4_t coefficients128_0; - int32x4_t coefficients128_4; - int32x4_t coefficients128_8; - int32x4_t samples128_0; - int32x4_t samples128_4; - int32x4_t samples128_8; - uint32x4_t riceParamMask128; - int32x4_t riceParam128; - int64x1_t shift64; - uint32x4_t one128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = ~((~0UL) << riceParam); - riceParamMask128 = vdupq_n_u32(riceParamMask); - riceParam128 = vdupq_n_s32(riceParam); - shift64 = vdup_n_s64(-shift); - one128 = vdupq_n_u32(1); - { - int runningOrder = order; - drflac_int32 tempC[4] = {0, 0, 0, 0}; - drflac_int32 tempS[4] = {0, 0, 0, 0}; - if (runningOrder >= 4) { - coefficients128_0 = vld1q_s32(coefficients + 0); - samples128_0 = vld1q_s32(pSamplesOut - 4); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; - case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; - case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; - } - coefficients128_0 = vld1q_s32(tempC); - samples128_0 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = vld1q_s32(coefficients + 4); - samples128_4 = vld1q_s32(pSamplesOut - 8); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; - case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; - case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; - } - coefficients128_4 = vld1q_s32(tempC); - samples128_4 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = vld1q_s32(coefficients + 8); - samples128_8 = vld1q_s32(pSamplesOut - 12); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; - case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; - case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; - } - coefficients128_8 = vld1q_s32(tempC); - samples128_8 = vld1q_s32(tempS); - runningOrder = 0; - } - coefficients128_0 = drflac__vrevq_s32(coefficients128_0); - coefficients128_4 = drflac__vrevq_s32(coefficients128_4); - coefficients128_8 = drflac__vrevq_s32(coefficients128_8); - } - while (pDecodedSamples < pDecodedSamplesEnd) { - int64x2_t prediction128; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return DRFLAC_FALSE; - } - zeroCountPart128 = vld1q_u32(zeroCountParts); - riceParamPart128 = vld1q_u32(riceParamParts); - riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); - riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); - for (i = 0; i < 4; i += 1) { - int64x1_t prediction64; - prediction128 = veorq_s64(prediction128, prediction128); - switch (order) - { - case 12: - case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); - case 10: - case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); - case 8: - case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); - case 6: - case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); - case 4: - case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); - case 2: - case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); - } - prediction64 = drflac__vhaddq_s64(prediction128); - prediction64 = vshl_s64(prediction64, shift64); - prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); - samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - vst1q_s32(pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return DRFLAC_FALSE; - } - riceParamParts[0] &= riceParamMask; - riceParamParts[0] |= (zeroCountParts[0] << riceParam); - riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder > 0 && lpcOrder <= 12) { - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } else { - return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - } else { - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } -} -#endif -static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ -#if defined(DRFLAC_SUPPORT_SSE41) - if (drflac__gIsSSE41Supported) { - return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported) { - return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } else -#endif - { - #if 0 - return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - #else - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - #endif - } -} -static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) -{ - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - for (i = 0; i < count; ++i) { - if (!drflac__seek_rice_parts(bs, riceParam)) { - return DRFLAC_FALSE; - } - } - return DRFLAC_TRUE; -} -#if defined(__clang__) -__attribute__((no_sanitize("signed-integer-overflow"))) -#endif -static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) -{ - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(unencodedBitsPerSample <= 31); - DRFLAC_ASSERT(pSamplesOut != NULL); - for (i = 0; i < count; ++i) { - if (unencodedBitsPerSample > 0) { - if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { - return DRFLAC_FALSE; - } - } else { - pSamplesOut[i] = 0; - } - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) -{ - drflac_uint8 residualMethod; - drflac_uint8 partitionOrder; - drflac_uint32 samplesInPartition; - drflac_uint32 partitionsRemaining; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(blockSize != 0); - DRFLAC_ASSERT(pDecodedSamples != NULL); - if (!drflac__read_uint8(bs, 2, &residualMethod)) { - return DRFLAC_FALSE; - } - if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return DRFLAC_FALSE; - } - pDecodedSamples += lpcOrder; - if (!drflac__read_uint8(bs, 4, &partitionOrder)) { - return DRFLAC_FALSE; - } - if (partitionOrder > 8) { - return DRFLAC_FALSE; - } - if ((blockSize / (1 << partitionOrder)) < lpcOrder) { - return DRFLAC_FALSE; - } - samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; - partitionsRemaining = (1 << partitionOrder); - for (;;) { - drflac_uint8 riceParam = 0; - if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!drflac__read_uint8(bs, 4, &riceParam)) { - return DRFLAC_FALSE; - } - if (riceParam == 15) { - riceParam = 0xFF; - } - } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!drflac__read_uint8(bs, 5, &riceParam)) { - return DRFLAC_FALSE; - } - if (riceParam == 31) { - riceParam = 0xFF; - } - } - if (riceParam != 0xFF) { - if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; - } - } else { - drflac_uint8 unencodedBitsPerSample = 0; - if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return DRFLAC_FALSE; - } - if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; - } - } - pDecodedSamples += samplesInPartition; - if (partitionsRemaining == 1) { - break; - } - partitionsRemaining -= 1; - if (partitionOrder != 0) { - samplesInPartition = blockSize / (1 << partitionOrder); - } - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) -{ - drflac_uint8 residualMethod; - drflac_uint8 partitionOrder; - drflac_uint32 samplesInPartition; - drflac_uint32 partitionsRemaining; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(blockSize != 0); - if (!drflac__read_uint8(bs, 2, &residualMethod)) { - return DRFLAC_FALSE; - } - if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return DRFLAC_FALSE; - } - if (!drflac__read_uint8(bs, 4, &partitionOrder)) { - return DRFLAC_FALSE; - } - if (partitionOrder > 8) { - return DRFLAC_FALSE; - } - if ((blockSize / (1 << partitionOrder)) <= order) { - return DRFLAC_FALSE; - } - samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - partitionsRemaining = (1 << partitionOrder); - for (;;) - { - drflac_uint8 riceParam = 0; - if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!drflac__read_uint8(bs, 4, &riceParam)) { - return DRFLAC_FALSE; - } - if (riceParam == 15) { - riceParam = 0xFF; - } - } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!drflac__read_uint8(bs, 5, &riceParam)) { - return DRFLAC_FALSE; - } - if (riceParam == 31) { - riceParam = 0xFF; - } - } - if (riceParam != 0xFF) { - if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { - return DRFLAC_FALSE; - } - } else { - drflac_uint8 unencodedBitsPerSample = 0; - if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return DRFLAC_FALSE; - } - if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { - return DRFLAC_FALSE; - } - } - if (partitionsRemaining == 1) { - break; - } - partitionsRemaining -= 1; - samplesInPartition = blockSize / (1 << partitionOrder); - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) -{ - drflac_uint32 i; - drflac_int32 sample; - if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { - return DRFLAC_FALSE; - } - for (i = 0; i < blockSize; ++i) { - pDecodedSamples[i] = sample; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) -{ - drflac_uint32 i; - for (i = 0; i < blockSize; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { - return DRFLAC_FALSE; - } - pDecodedSamples[i] = sample; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) -{ - drflac_uint32 i; - static drflac_int32 lpcCoefficientsTable[5][4] = { - {0, 0, 0, 0}, - {1, 0, 0, 0}, - {2, -1, 0, 0}, - {3, -3, 1, 0}, - {4, -6, 4, -1} - }; - for (i = 0; i < lpcOrder; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { - return DRFLAC_FALSE; - } - pDecodedSamples[i] = sample; - } - if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { - return DRFLAC_FALSE; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) -{ - drflac_uint8 i; - drflac_uint8 lpcPrecision; - drflac_int8 lpcShift; - drflac_int32 coefficients[32]; - for (i = 0; i < lpcOrder; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, bitsPerSample, &sample)) { - return DRFLAC_FALSE; - } - pDecodedSamples[i] = sample; - } - if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { - return DRFLAC_FALSE; - } - if (lpcPrecision == 15) { - return DRFLAC_FALSE; - } - lpcPrecision += 1; - if (!drflac__read_int8(bs, 5, &lpcShift)) { - return DRFLAC_FALSE; - } - if (lpcShift < 0) { - return DRFLAC_FALSE; - } - DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); - for (i = 0; i < lpcOrder; ++i) { - if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { - return DRFLAC_FALSE; - } - } - if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) -{ - const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; - const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(header != NULL); - for (;;) { - drflac_uint8 crc8 = 0xCE; - drflac_uint8 reserved = 0; - drflac_uint8 blockingStrategy = 0; - drflac_uint8 blockSize = 0; - drflac_uint8 sampleRate = 0; - drflac_uint8 channelAssignment = 0; - drflac_uint8 bitsPerSample = 0; - drflac_bool32 isVariableBlockSize; - if (!drflac__find_and_seek_to_next_sync_code(bs)) { - return DRFLAC_FALSE; - } - if (!drflac__read_uint8(bs, 1, &reserved)) { - return DRFLAC_FALSE; - } - if (reserved == 1) { - continue; - } - crc8 = drflac_crc8(crc8, reserved, 1); - if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, blockingStrategy, 1); - if (!drflac__read_uint8(bs, 4, &blockSize)) { - return DRFLAC_FALSE; - } - if (blockSize == 0) { - continue; - } - crc8 = drflac_crc8(crc8, blockSize, 4); - if (!drflac__read_uint8(bs, 4, &sampleRate)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, sampleRate, 4); - if (!drflac__read_uint8(bs, 4, &channelAssignment)) { - return DRFLAC_FALSE; - } - if (channelAssignment > 10) { - continue; - } - crc8 = drflac_crc8(crc8, channelAssignment, 4); - if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { - return DRFLAC_FALSE; - } - if (bitsPerSample == 3 || bitsPerSample == 7) { - continue; - } - crc8 = drflac_crc8(crc8, bitsPerSample, 3); - if (!drflac__read_uint8(bs, 1, &reserved)) { - return DRFLAC_FALSE; - } - if (reserved == 1) { - continue; - } - crc8 = drflac_crc8(crc8, reserved, 1); - isVariableBlockSize = blockingStrategy == 1; - if (isVariableBlockSize) { - drflac_uint64 pcmFrameNumber; - drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_AT_END) { - return DRFLAC_FALSE; - } else { - continue; - } - } - header->flacFrameNumber = 0; - header->pcmFrameNumber = pcmFrameNumber; - } else { - drflac_uint64 flacFrameNumber = 0; - drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_AT_END) { - return DRFLAC_FALSE; - } else { - continue; - } - } - header->flacFrameNumber = (drflac_uint32)flacFrameNumber; - header->pcmFrameNumber = 0; - } - DRFLAC_ASSERT(blockSize > 0); - if (blockSize == 1) { - header->blockSizeInPCMFrames = 192; - } else if (blockSize <= 5) { - DRFLAC_ASSERT(blockSize >= 2); - header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); - } else if (blockSize == 6) { - if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8); - header->blockSizeInPCMFrames += 1; - } else if (blockSize == 7) { - if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); - if (header->blockSizeInPCMFrames == 0xFFFF) { - return DRFLAC_FALSE; - } - header->blockSizeInPCMFrames += 1; - } else { - DRFLAC_ASSERT(blockSize >= 8); - header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); - } - if (sampleRate <= 11) { - header->sampleRate = sampleRateTable[sampleRate]; - } else if (sampleRate == 12) { - if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, header->sampleRate, 8); - header->sampleRate *= 1000; - } else if (sampleRate == 13) { - if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, header->sampleRate, 16); - } else if (sampleRate == 14) { - if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { - return DRFLAC_FALSE; - } - crc8 = drflac_crc8(crc8, header->sampleRate, 16); - header->sampleRate *= 10; - } else { - continue; - } - header->channelAssignment = channelAssignment; - header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; - if (header->bitsPerSample == 0) { - header->bitsPerSample = streaminfoBitsPerSample; - } - if (header->bitsPerSample != streaminfoBitsPerSample) { - return DRFLAC_FALSE; - } - if (!drflac__read_uint8(bs, 8, &header->crc8)) { - return DRFLAC_FALSE; - } -#ifndef DR_FLAC_NO_CRC - if (header->crc8 != crc8) { - continue; - } -#endif - return DRFLAC_TRUE; - } -} -static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) -{ - drflac_uint8 header; - int type; - if (!drflac__read_uint8(bs, 8, &header)) { - return DRFLAC_FALSE; - } - if ((header & 0x80) != 0) { - return DRFLAC_FALSE; - } - type = (header & 0x7E) >> 1; - if (type == 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; - } else if (type == 1) { - pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; - } else { - if ((type & 0x20) != 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; - pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1; - } else if ((type & 0x08) != 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; - pSubframe->lpcOrder = (drflac_uint8)(type & 0x07); - if (pSubframe->lpcOrder > 4) { - pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; - pSubframe->lpcOrder = 0; - } - } else { - pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; - } - } - if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { - return DRFLAC_FALSE; - } - pSubframe->wastedBitsPerSample = 0; - if ((header & 0x01) == 1) { - unsigned int wastedBitsPerSample; - if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { - return DRFLAC_FALSE; - } - pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) -{ - drflac_subframe* pSubframe; - drflac_uint32 subframeBitsPerSample; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(frame != NULL); - pSubframe = frame->subframes + subframeIndex; - if (!drflac__read_subframe_header(bs, pSubframe)) { - return DRFLAC_FALSE; - } - subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - subframeBitsPerSample += 1; - } - if (subframeBitsPerSample > 32) { - return DRFLAC_FALSE; - } - if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return DRFLAC_FALSE; - } - subframeBitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pSamplesS32 = pDecodedSamplesOut; - switch (pSubframe->subframeType) - { - case DRFLAC_SUBFRAME_CONSTANT: - { - drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); - } break; - case DRFLAC_SUBFRAME_VERBATIM: - { - drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); - } break; - case DRFLAC_SUBFRAME_FIXED: - { - drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); - } break; - case DRFLAC_SUBFRAME_LPC: - { - drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); - } break; - default: return DRFLAC_FALSE; - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) -{ - drflac_subframe* pSubframe; - drflac_uint32 subframeBitsPerSample; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(frame != NULL); - pSubframe = frame->subframes + subframeIndex; - if (!drflac__read_subframe_header(bs, pSubframe)) { - return DRFLAC_FALSE; - } - subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - subframeBitsPerSample += 1; - } - if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return DRFLAC_FALSE; - } - subframeBitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pSamplesS32 = NULL; - switch (pSubframe->subframeType) - { - case DRFLAC_SUBFRAME_CONSTANT: - { - if (!drflac__seek_bits(bs, subframeBitsPerSample)) { - return DRFLAC_FALSE; - } - } break; - case DRFLAC_SUBFRAME_VERBATIM: - { - unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; - } - } break; - case DRFLAC_SUBFRAME_FIXED: - { - unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; - } - if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return DRFLAC_FALSE; - } - } break; - case DRFLAC_SUBFRAME_LPC: - { - drflac_uint8 lpcPrecision; - unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; - } - if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { - return DRFLAC_FALSE; - } - if (lpcPrecision == 15) { - return DRFLAC_FALSE; - } - lpcPrecision += 1; - bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; - } - if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return DRFLAC_FALSE; - } - } break; - default: return DRFLAC_FALSE; - } - return DRFLAC_TRUE; -} -static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) -{ - drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; - DRFLAC_ASSERT(channelAssignment <= 10); - return lookup[channelAssignment]; -} -static drflac_result drflac__decode_flac_frame(drflac* pFlac) -{ - int channelCount; - int i; - drflac_uint8 paddingSizeInBits; - drflac_uint16 desiredCRC16; -#ifndef DR_FLAC_NO_CRC - drflac_uint16 actualCRC16; -#endif - DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); - if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { - return DRFLAC_ERROR; - } - channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - if (channelCount != (int)pFlac->channels) { - return DRFLAC_ERROR; - } - for (i = 0; i < channelCount; ++i) { - if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { - return DRFLAC_ERROR; - } - } - paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); - if (paddingSizeInBits > 0) { - drflac_uint8 padding = 0; - if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { - return DRFLAC_AT_END; - } - } -#ifndef DR_FLAC_NO_CRC - actualCRC16 = drflac__flush_crc16(&pFlac->bs); -#endif - if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return DRFLAC_AT_END; - } -#ifndef DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return DRFLAC_CRC_MISMATCH; - } -#endif - pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - return DRFLAC_SUCCESS; -} -static drflac_result drflac__seek_flac_frame(drflac* pFlac) -{ - int channelCount; - int i; - drflac_uint16 desiredCRC16; -#ifndef DR_FLAC_NO_CRC - drflac_uint16 actualCRC16; -#endif - channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - for (i = 0; i < channelCount; ++i) { - if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { - return DRFLAC_ERROR; - } - } - if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { - return DRFLAC_ERROR; - } -#ifndef DR_FLAC_NO_CRC - actualCRC16 = drflac__flush_crc16(&pFlac->bs); -#endif - if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return DRFLAC_AT_END; - } -#ifndef DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return DRFLAC_CRC_MISMATCH; - } -#endif - return DRFLAC_SUCCESS; -} -static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac) -{ - DRFLAC_ASSERT(pFlac != NULL); - for (;;) { - drflac_result result; - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; - } - result = drflac__decode_flac_frame(pFlac); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_CRC_MISMATCH) { - continue; - } else { - return DRFLAC_FALSE; - } - } - return DRFLAC_TRUE; - } -} -static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame) -{ - drflac_uint64 firstPCMFrame; - drflac_uint64 lastPCMFrame; - DRFLAC_ASSERT(pFlac != NULL); - firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; - if (firstPCMFrame == 0) { - firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; - } - lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - if (lastPCMFrame > 0) { - lastPCMFrame -= 1; - } - if (pFirstPCMFrame) { - *pFirstPCMFrame = firstPCMFrame; - } - if (pLastPCMFrame) { - *pLastPCMFrame = lastPCMFrame; - } -} -static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) -{ - drflac_bool32 result; - DRFLAC_ASSERT(pFlac != NULL); - result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); - DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); - pFlac->currentPCMFrame = 0; - return result; -} -static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac) -{ - DRFLAC_ASSERT(pFlac != NULL); - return drflac__seek_flac_frame(pFlac); -} -static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) -{ - drflac_uint64 pcmFramesRead = 0; - while (pcmFramesToSeek > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { - pcmFramesRead += pcmFramesToSeek; - pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek; - pcmFramesToSeek = 0; - } else { - pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; - pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - } - } - } - pFlac->currentPCMFrame += pcmFramesRead; - return pcmFramesRead; -} -static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex) -{ - drflac_bool32 isMidFrame = DRFLAC_FALSE; - drflac_uint64 runningPCMFrameCount; - DRFLAC_ASSERT(pFlac != NULL); - if (pcmFrameIndex >= pFlac->currentPCMFrame) { - runningPCMFrameCount = pFlac->currentPCMFrame; - if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; - } - } else { - isMidFrame = DRFLAC_TRUE; - } - } else { - runningPCMFrameCount = 0; - if (!drflac__seek_to_first_frame(pFlac)) { - return DRFLAC_FALSE; - } - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; - } - } - for (;;) { - drflac_uint64 pcmFrameCountInThisFLACFrame; - drflac_uint64 firstPCMFrameInFLACFrame = 0; - drflac_uint64 lastPCMFrameInFLACFrame = 0; - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; - if (!isMidFrame) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - goto next_iteration; - } else { - return DRFLAC_FALSE; - } - } - } else { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } - } else { - if (!isMidFrame) { - drflac_result result = drflac__seek_to_next_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFLACFrame; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - goto next_iteration; - } else { - return DRFLAC_FALSE; - } - } - } else { - runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = DRFLAC_FALSE; - } - if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return DRFLAC_TRUE; - } - } - next_iteration: - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; - } - } -} -#if !defined(DR_FLAC_NO_CRC) -#define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f -static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset) -{ - DRFLAC_ASSERT(pFlac != NULL); - DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); - DRFLAC_ASSERT(targetByte >= rangeLo); - DRFLAC_ASSERT(targetByte <= rangeHi); - *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; - for (;;) { - drflac_uint64 lastTargetByte = targetByte; - if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) { - if (targetByte == 0) { - drflac__seek_to_first_frame(pFlac); - return DRFLAC_FALSE; - } - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); -#if 1 - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - break; - } -#else - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - break; - } -#endif - } - if(targetByte == lastTargetByte) { - return DRFLAC_FALSE; - } - } - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - DRFLAC_ASSERT(targetByte <= rangeHi); - *pLastSuccessfulSeekOffset = targetByte; - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset) -{ -#if 0 - if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) { - if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) { - return DRFLAC_FALSE; - } - } -#endif - return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset; -} -static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi) -{ - drflac_uint64 targetByte; - drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; - drflac_uint64 pcmRangeHi = 0; - drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1; - drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; - drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); - if (targetByte > byteRangeHi) { - targetByte = byteRangeHi; - } - for (;;) { - if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { - drflac_uint64 newPCMRangeLo; - drflac_uint64 newPCMRangeHi; - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); - if (pcmRangeLo == newPCMRangeLo) { - if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { - break; - } - if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return DRFLAC_TRUE; - } else { - break; - } - } - pcmRangeLo = newPCMRangeLo; - pcmRangeHi = newPCMRangeHi; - if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { - if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { - return DRFLAC_TRUE; - } else { - break; - } - } else { - const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); - if (pcmRangeLo > pcmFrameIndex) { - byteRangeHi = lastSuccessfulSeekOffset; - if (byteRangeLo > byteRangeHi) { - byteRangeLo = byteRangeHi; - } - targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); - if (targetByte < byteRangeLo) { - targetByte = byteRangeLo; - } - } else { - if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { - if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return DRFLAC_TRUE; - } else { - break; - } - } else { - byteRangeLo = lastSuccessfulSeekOffset; - if (byteRangeHi < byteRangeLo) { - byteRangeHi = byteRangeLo; - } - targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); - if (targetByte > byteRangeHi) { - targetByte = byteRangeHi; - } - if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { - closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; - } - } - } - } - } else { - break; - } - } - drflac__seek_to_first_frame(pFlac); - return DRFLAC_FALSE; -} -static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex) -{ - drflac_uint64 byteRangeLo; - drflac_uint64 byteRangeHi; - drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) { - return DRFLAC_FALSE; - } - if (pcmFrameIndex < seekForwardThreshold) { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; - } - byteRangeLo = pFlac->firstFLACFramePosInBytes; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); -} -#endif -static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex) -{ - drflac_uint32 iClosestSeekpoint = 0; - drflac_bool32 isMidFrame = DRFLAC_FALSE; - drflac_uint64 runningPCMFrameCount; - drflac_uint32 iSeekpoint; - DRFLAC_ASSERT(pFlac != NULL); - if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { - return DRFLAC_FALSE; - } - if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { - return DRFLAC_FALSE; - } - for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { - if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { - break; - } - iClosestSeekpoint = iSeekpoint; - } - if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { - return DRFLAC_FALSE; - } - if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { - return DRFLAC_FALSE; - } -#if !defined(DR_FLAC_NO_CRC) - if (pFlac->totalPCMFrameCount > 0) { - drflac_uint64 byteRangeLo; - drflac_uint64 byteRangeHi; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; - if (iClosestSeekpoint < pFlac->seekpointCount-1) { - drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1; - if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { - return DRFLAC_FALSE; - } - if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { - byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; - } - } - if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { - return DRFLAC_TRUE; - } - } - } - } -#endif - if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { - runningPCMFrameCount = pFlac->currentPCMFrame; - if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; - } - } else { - isMidFrame = DRFLAC_TRUE; - } - } else { - runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - return DRFLAC_FALSE; - } - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; - } - } - for (;;) { - drflac_uint64 pcmFrameCountInThisFLACFrame; - drflac_uint64 firstPCMFrameInFLACFrame = 0; - drflac_uint64 lastPCMFrameInFLACFrame = 0; - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; - if (!isMidFrame) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - goto next_iteration; - } else { - return DRFLAC_FALSE; - } - } - } else { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } - } else { - if (!isMidFrame) { - drflac_result result = drflac__seek_to_next_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFLACFrame; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - goto next_iteration; - } else { - return DRFLAC_FALSE; - } - } - } else { - runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = DRFLAC_FALSE; - } - if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return DRFLAC_TRUE; - } - } - next_iteration: - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; - } - } -} -#ifndef DR_FLAC_NO_OGG -typedef struct -{ - drflac_uint8 capturePattern[4]; - drflac_uint8 structureVersion; - drflac_uint8 headerType; - drflac_uint64 granulePosition; - drflac_uint32 serialNumber; - drflac_uint32 sequenceNumber; - drflac_uint32 checksum; - drflac_uint8 segmentCount; - drflac_uint8 segmentTable[255]; -} drflac_ogg_page_header; -#endif -typedef struct -{ - drflac_read_proc onRead; - drflac_seek_proc onSeek; - drflac_meta_proc onMeta; - drflac_container container; - void* pUserData; - void* pUserDataMD; - drflac_uint32 sampleRate; - drflac_uint8 channels; - drflac_uint8 bitsPerSample; - drflac_uint64 totalPCMFrameCount; - drflac_uint16 maxBlockSizeInPCMFrames; - drflac_uint64 runningFilePos; - drflac_bool32 hasStreamInfoBlock; - drflac_bool32 hasMetadataBlocks; - drflac_bs bs; - drflac_frame_header firstFrameHeader; -#ifndef DR_FLAC_NO_OGG - drflac_uint32 oggSerial; - drflac_uint64 oggFirstBytePos; - drflac_ogg_page_header oggBosHeader; -#endif -} drflac_init_info; -static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) -{ - blockHeader = drflac__be2host_32(blockHeader); - *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31); - *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24); - *blockSize = (blockHeader & 0x00FFFFFFUL); -} -static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) -{ - drflac_uint32 blockHeader; - *blockSize = 0; - if (onRead(pUserData, &blockHeader, 4) != 4) { - return DRFLAC_FALSE; - } - drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) -{ - drflac_uint32 blockSizes; - drflac_uint64 frameSizes = 0; - drflac_uint64 importantProps; - drflac_uint8 md5[16]; - if (onRead(pUserData, &blockSizes, 4) != 4) { - return DRFLAC_FALSE; - } - if (onRead(pUserData, &frameSizes, 6) != 6) { - return DRFLAC_FALSE; - } - if (onRead(pUserData, &importantProps, 8) != 8) { - return DRFLAC_FALSE; - } - if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { - return DRFLAC_FALSE; - } - blockSizes = drflac__be2host_32(blockSizes); - frameSizes = drflac__be2host_64(frameSizes); - importantProps = drflac__be2host_64(importantProps); - pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16); - pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF); - pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40); - pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16); - pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44); - pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1; - pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1; - pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); - DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); - return DRFLAC_TRUE; -} -static void* drflac__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return DRFLAC_MALLOC(sz); -} -static void* drflac__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return DRFLAC_REALLOC(p, sz); -} -static void drflac__free_default(void* p, void* pUserData) -{ - (void)pUserData; - DRFLAC_FREE(p); -} -static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - DRFLAC_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize, drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac_uint64 runningFilePos = 42; - drflac_uint64 seektablePos = 0; - drflac_uint32 seektableSize = 0; - for (;;) { - drflac_metadata metadata; - drflac_uint8 isLastBlock = 0; - drflac_uint8 blockType; - drflac_uint32 blockSize; - if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) { - return DRFLAC_FALSE; - } - runningFilePos += 4; - metadata.type = blockType; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - switch (blockType) - { - case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: - { - if (blockSize < 4) { - return DRFLAC_FALSE; - } - if (onMeta) { - void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); - metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); - metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); - onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: - { - seektablePos = runningFilePos; - seektableSize = blockSize; - if (onMeta) { - drflac_uint32 iSeekpoint; - void* pRawData; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint); - metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; - for (iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { - drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; - pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); - pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); - pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); - } - onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: - { - if (blockSize < 8) { - return DRFLAC_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - drflac_uint32 i; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.data.vorbis_comment.pComments = pRunningData; - for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { - drflac_uint32 commentLength; - if (pRunningDataEnd - pRunningData < 4) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - pRunningData += commentLength; - } - onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: - { - if (blockSize < 396) { - return DRFLAC_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - drflac_uint8 iTrack; - drflac_uint8 iIndex; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; - metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; - metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; - metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; - metadata.data.cuesheet.pTrackData = pRunningData; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - drflac_uint8 indexCount; - drflac_uint32 indexPointSize; - if (pRunningDataEnd - pRunningData < 36) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - pRunningData += 35; - indexCount = pRunningData[0]; pRunningData += 1; - indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index); - if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - for (iIndex = 0; iIndex < indexCount; ++iIndex) { - drflac_cuesheet_track_index* pTrack = (drflac_cuesheet_track_index*)pRunningData; - pRunningData += sizeof(drflac_cuesheet_track_index); - pTrack->offset = drflac__be2host_64(pTrack->offset); - } - } - onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: - { - if (blockSize < 32) { - return DRFLAC_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; - if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case DRFLAC_METADATA_BLOCK_TYPE_PADDING: - { - if (onMeta) { - metadata.data.padding.unused = 0; - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; - } else { - onMeta(pUserDataMD, &metadata); - } - } - } break; - case DRFLAC_METADATA_BLOCK_TYPE_INVALID: - { - if (onMeta) { - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; - } - } - } break; - default: - { - if (onMeta) { - void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - } - if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; - } - } - runningFilePos += blockSize; - if (isLastBlock) { - break; - } - } - *pSeektablePos = seektablePos; - *pSeektableSize = seektableSize; - *pFirstFramePos = runningFilePos; - return DRFLAC_TRUE; -} -static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) -{ - drflac_uint8 isLastBlock; - drflac_uint8 blockType; - drflac_uint32 blockSize; - (void)onSeek; - pInit->container = drflac_container_native; - if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return DRFLAC_FALSE; - } - if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - if (!relaxed) { - return DRFLAC_FALSE; - } else { - pInit->hasStreamInfoBlock = DRFLAC_FALSE; - pInit->hasMetadataBlocks = DRFLAC_FALSE; - if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { - return DRFLAC_FALSE; - } - if (pInit->firstFrameHeader.bitsPerSample == 0) { - return DRFLAC_FALSE; - } - pInit->sampleRate = pInit->firstFrameHeader.sampleRate; - pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); - pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; - pInit->maxBlockSizeInPCMFrames = 65535; - return DRFLAC_TRUE; - } - } else { - drflac_streaminfo streaminfo; - if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { - return DRFLAC_FALSE; - } - pInit->hasStreamInfoBlock = DRFLAC_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; - pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; - pInit->hasMetadataBlocks = !isLastBlock; - if (onMeta) { - drflac_metadata metadata; - metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - return DRFLAC_TRUE; - } -} -#ifndef DR_FLAC_NO_OGG -#define DRFLAC_OGG_MAX_PAGE_SIZE 65307 -#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 -typedef enum -{ - drflac_ogg_recover_on_crc_mismatch, - drflac_ogg_fail_on_crc_mismatch -} drflac_ogg_crc_mismatch_recovery; -#ifndef DR_FLAC_NO_CRC -static drflac_uint32 drflac__crc32_table[] = { - 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, - 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, - 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, - 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, - 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, - 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, - 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, - 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, - 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, - 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, - 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, - 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, - 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, - 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, - 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, - 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, - 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, - 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, - 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, - 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, - 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, - 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, - 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, - 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, - 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, - 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, - 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, - 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, - 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, - 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, - 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, - 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, - 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, - 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, - 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, - 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, - 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, - 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, - 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, - 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, - 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, - 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, - 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, - 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, - 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, - 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, - 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, - 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, - 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, - 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, - 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, - 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, - 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, - 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, - 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, - 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, - 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, - 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, - 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, - 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, - 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, - 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, - 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, - 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L -}; -#endif -static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) -{ -#ifndef DR_FLAC_NO_CRC - return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; -#else - (void)data; - return crc32; -#endif -} -#if 0 -static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) -{ - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); - return crc32; -} -static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) -{ - crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); - crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); - return crc32; -} -#endif -static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) -{ - drflac_uint32 i; - for (i = 0; i < dataSize; ++i) { - crc32 = drflac_crc32_byte(crc32, pData[i]); - } - return crc32; -} -static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) -{ - return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; -} -static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) -{ - return 27 + pHeader->segmentCount; -} -static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) -{ - drflac_uint32 pageBodySize = 0; - int i; - for (i = 0; i < pHeader->segmentCount; ++i) { - pageBodySize += pHeader->segmentTable[i]; - } - return pageBodySize; -} -static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) -{ - drflac_uint8 data[23]; - drflac_uint32 i; - DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); - if (onRead(pUserData, data, 23) != 23) { - return DRFLAC_AT_END; - } - *pBytesRead += 23; - pHeader->capturePattern[0] = 'O'; - pHeader->capturePattern[1] = 'g'; - pHeader->capturePattern[2] = 'g'; - pHeader->capturePattern[3] = 'S'; - pHeader->structureVersion = data[0]; - pHeader->headerType = data[1]; - DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); - DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); - DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); - DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); - pHeader->segmentCount = data[22]; - data[18] = 0; - data[19] = 0; - data[20] = 0; - data[21] = 0; - for (i = 0; i < 23; ++i) { - *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); - } - if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { - return DRFLAC_AT_END; - } - *pBytesRead += pHeader->segmentCount; - for (i = 0; i < pHeader->segmentCount; ++i) { - *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); - } - return DRFLAC_SUCCESS; -} -static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) -{ - drflac_uint8 id[4]; - *pBytesRead = 0; - if (onRead(pUserData, id, 4) != 4) { - return DRFLAC_AT_END; - } - *pBytesRead += 4; - for (;;) { - if (drflac_ogg__is_capture_pattern(id)) { - drflac_result result; - *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; - result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); - if (result == DRFLAC_SUCCESS) { - return DRFLAC_SUCCESS; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; - } else { - return result; - } - } - } else { - id[0] = id[1]; - id[1] = id[2]; - id[2] = id[3]; - if (onRead(pUserData, &id[3], 1) != 1) { - return DRFLAC_AT_END; - } - *pBytesRead += 1; - } - } -} -typedef struct -{ - drflac_read_proc onRead; - drflac_seek_proc onSeek; - void* pUserData; - drflac_uint64 currentBytePos; - drflac_uint64 firstBytePos; - drflac_uint32 serialNumber; - drflac_ogg_page_header bosPageHeader; - drflac_ogg_page_header currentPageHeader; - drflac_uint32 bytesRemainingInPage; - drflac_uint32 pageDataSize; - drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; -} drflac_oggbs; -static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) -{ - size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); - oggbs->currentBytePos += bytesActuallyRead; - return bytesActuallyRead; -} -static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) -{ - if (origin == drflac_seek_origin_start) { - if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { - return DRFLAC_FALSE; - } - oggbs->currentBytePos = offset; - return DRFLAC_TRUE; - } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { - return DRFLAC_FALSE; - } - oggbs->currentBytePos = offset; - return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); - } - } else { - while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - oggbs->currentBytePos += 0x7FFFFFFF; - offset -= 0x7FFFFFFF; - } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - oggbs->currentBytePos += offset; - return DRFLAC_TRUE; - } -} -static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) -{ - drflac_ogg_page_header header; - for (;;) { - drflac_uint32 crc32 = 0; - drflac_uint32 bytesRead; - drflac_uint32 pageBodySize; -#ifndef DR_FLAC_NO_CRC - drflac_uint32 actualCRC32; -#endif - if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { - return DRFLAC_FALSE; - } - oggbs->currentBytePos += bytesRead; - pageBodySize = drflac_ogg__get_page_body_size(&header); - if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { - continue; - } - if (header.serialNumber != oggbs->serialNumber) { - if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - continue; - } - if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { - return DRFLAC_FALSE; - } - oggbs->pageDataSize = pageBodySize; -#ifndef DR_FLAC_NO_CRC - actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); - if (actualCRC32 != header.checksum) { - if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { - continue; - } else { - drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); - return DRFLAC_FALSE; - } - } -#else - (void)recoveryMethod; -#endif - oggbs->currentPageHeader = header; - oggbs->bytesRemainingInPage = pageBodySize; - return DRFLAC_TRUE; - } -} -#if 0 -static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) -{ - drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; - drflac_uint8 iSeg = 0; - drflac_uint32 iByte = 0; - while (iByte < bytesConsumedInPage) { - drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (iByte + segmentSize > bytesConsumedInPage) { - break; - } else { - iSeg += 1; - iByte += segmentSize; - } - } - *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); - return iSeg; -} -static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) -{ - for (;;) { - drflac_bool32 atEndOfPage = DRFLAC_FALSE; - drflac_uint8 bytesRemainingInSeg; - drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); - drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; - for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { - drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (segmentSize < 255) { - if (iSeg == oggbs->currentPageHeader.segmentCount-1) { - atEndOfPage = DRFLAC_TRUE; - } - break; - } - bytesToEndOfPacketOrPage += segmentSize; - } - drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); - oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; - if (atEndOfPage) { - if (!drflac_oggbs__goto_next_page(oggbs)) { - return DRFLAC_FALSE; - } - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - return DRFLAC_TRUE; - } - } else { - return DRFLAC_TRUE; - } - } -} -static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) -{ - return drflac_oggbs__seek_to_next_packet(oggbs); -} -#endif -static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; - drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; - size_t bytesRead = 0; - DRFLAC_ASSERT(oggbs != NULL); - DRFLAC_ASSERT(pRunningBufferOut != NULL); - while (bytesRead < bytesToRead) { - size_t bytesRemainingToRead = bytesToRead - bytesRead; - if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { - DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); - bytesRead += bytesRemainingToRead; - oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); - bytesRead += oggbs->bytesRemainingInPage; - pRunningBufferOut += oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - DRFLAC_ASSERT(bytesRemainingToRead > 0); - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { - break; - } - } - return bytesRead; -} -static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) -{ - drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; - int bytesSeeked = 0; - DRFLAC_ASSERT(oggbs != NULL); - DRFLAC_ASSERT(offset >= 0); - if (origin == drflac_seek_origin_start) { - if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { - return DRFLAC_FALSE; - } - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { - return DRFLAC_FALSE; - } - return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); - } - DRFLAC_ASSERT(origin == drflac_seek_origin_current); - while (bytesSeeked < offset) { - int bytesRemainingToSeek = offset - bytesSeeked; - DRFLAC_ASSERT(bytesRemainingToSeek >= 0); - if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { - bytesSeeked += bytesRemainingToSeek; - (void)bytesSeeked; - oggbs->bytesRemainingInPage -= bytesRemainingToSeek; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - bytesSeeked += (int)oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - DRFLAC_ASSERT(bytesRemainingToSeek > 0); - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { - return DRFLAC_FALSE; - } - } - return DRFLAC_TRUE; -} -static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) -{ - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - drflac_uint64 originalBytePos; - drflac_uint64 runningGranulePosition; - drflac_uint64 runningFrameBytePos; - drflac_uint64 runningPCMFrameCount; - DRFLAC_ASSERT(oggbs != NULL); - originalBytePos = oggbs->currentBytePos; - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { - return DRFLAC_FALSE; - } - oggbs->bytesRemainingInPage = 0; - runningGranulePosition = 0; - for (;;) { - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { - drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); - return DRFLAC_FALSE; - } - runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; - if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { - break; - } - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - if (oggbs->currentPageHeader.segmentTable[0] >= 2) { - drflac_uint8 firstBytesInPage[2]; - firstBytesInPage[0] = oggbs->pageData[0]; - firstBytesInPage[1] = oggbs->pageData[1]; - if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { - runningGranulePosition = oggbs->currentPageHeader.granulePosition; - } - continue; - } - } - } - if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { - return DRFLAC_FALSE; - } - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { - return DRFLAC_FALSE; - } - runningPCMFrameCount = runningGranulePosition; - for (;;) { - drflac_uint64 firstPCMFrameInFLACFrame = 0; - drflac_uint64 lastPCMFrameInFLACFrame = 0; - drflac_uint64 pcmFrameCountInThisFrame; - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; - } - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - pFlac->currentPCMFrame = pcmFrameIndex; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - return DRFLAC_TRUE; - } else { - return DRFLAC_FALSE; - } - } - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); - if (pcmFramesToDecode == 0) { - return DRFLAC_TRUE; - } - pFlac->currentPCMFrame = runningPCMFrameCount; - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; - } else { - return DRFLAC_FALSE; - } - } - } else { - drflac_result result = drflac__seek_to_next_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFrame; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; - } else { - return DRFLAC_FALSE; - } - } - } - } -} -static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) -{ - drflac_ogg_page_header header; - drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; - drflac_uint32 bytesRead = 0; - (void)relaxed; - pInit->container = drflac_container_ogg; - pInit->oggFirstBytePos = 0; - if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { - return DRFLAC_FALSE; - } - pInit->runningFilePos += bytesRead; - for (;;) { - int pageBodySize; - if ((header.headerType & 0x02) == 0) { - return DRFLAC_FALSE; - } - pageBodySize = drflac_ogg__get_page_body_size(&header); - if (pageBodySize == 51) { - drflac_uint32 bytesRemainingInPage = pageBodySize; - drflac_uint8 packetType; - if (onRead(pUserData, &packetType, 1) != 1) { - return DRFLAC_FALSE; - } - bytesRemainingInPage -= 1; - if (packetType == 0x7F) { - drflac_uint8 sig[4]; - if (onRead(pUserData, sig, 4) != 4) { - return DRFLAC_FALSE; - } - bytesRemainingInPage -= 4; - if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { - drflac_uint8 mappingVersion[2]; - if (onRead(pUserData, mappingVersion, 2) != 2) { - return DRFLAC_FALSE; - } - if (mappingVersion[0] != 1) { - return DRFLAC_FALSE; - } - if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - if (onRead(pUserData, sig, 4) != 4) { - return DRFLAC_FALSE; - } - if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { - drflac_streaminfo streaminfo; - drflac_uint8 isLastBlock; - drflac_uint8 blockType; - drflac_uint32 blockSize; - if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return DRFLAC_FALSE; - } - if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return DRFLAC_FALSE; - } - if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { - pInit->hasStreamInfoBlock = DRFLAC_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; - pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; - pInit->hasMetadataBlocks = !isLastBlock; - if (onMeta) { - drflac_metadata metadata; - metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - pInit->runningFilePos += pageBodySize; - pInit->oggFirstBytePos = pInit->runningFilePos - 79; - pInit->oggSerial = header.serialNumber; - pInit->oggBosHeader = header; - break; - } else { - return DRFLAC_FALSE; - } - } else { - return DRFLAC_FALSE; - } - } else { - if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - } - } else { - if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - } - } else { - if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - } - pInit->runningFilePos += pageBodySize; - if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { - return DRFLAC_FALSE; - } - pInit->runningFilePos += bytesRead; - } - pInit->hasMetadataBlocks = DRFLAC_TRUE; - return DRFLAC_TRUE; -} -#endif -static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) -{ - drflac_bool32 relaxed; - drflac_uint8 id[4]; - if (pInit == NULL || onRead == NULL || onSeek == NULL) { - return DRFLAC_FALSE; - } - DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); - pInit->onRead = onRead; - pInit->onSeek = onSeek; - pInit->onMeta = onMeta; - pInit->container = container; - pInit->pUserData = pUserData; - pInit->pUserDataMD = pUserDataMD; - pInit->bs.onRead = onRead; - pInit->bs.onSeek = onSeek; - pInit->bs.pUserData = pUserData; - drflac__reset_cache(&pInit->bs); - relaxed = container != drflac_container_unknown; - for (;;) { - if (onRead(pUserData, id, 4) != 4) { - return DRFLAC_FALSE; - } - pInit->runningFilePos += 4; - if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { - drflac_uint8 header[6]; - drflac_uint8 flags; - drflac_uint32 headerSize; - if (onRead(pUserData, header, 6) != 6) { - return DRFLAC_FALSE; - } - pInit->runningFilePos += 6; - flags = header[1]; - DRFLAC_COPY_MEMORY(&headerSize, header+2, 4); - headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); - if (flags & 0x10) { - headerSize += 10; - } - if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; - } - pInit->runningFilePos += headerSize; - } else { - break; - } - } - if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { - return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#ifndef DR_FLAC_NO_OGG - if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { - return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#endif - if (relaxed) { - if (container == drflac_container_native) { - return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#ifndef DR_FLAC_NO_OGG - if (container == drflac_container_ogg) { - return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#endif - } - return DRFLAC_FALSE; -} -static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) -{ - DRFLAC_ASSERT(pFlac != NULL); - DRFLAC_ASSERT(pInit != NULL); - DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); - pFlac->bs = pInit->bs; - pFlac->onMeta = pInit->onMeta; - pFlac->pUserDataMD = pInit->pUserDataMD; - pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; - pFlac->sampleRate = pInit->sampleRate; - pFlac->channels = (drflac_uint8)pInit->channels; - pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; - pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; - pFlac->container = pInit->container; -} -static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac_init_info init; - drflac_uint32 allocationSize; - drflac_uint32 wholeSIMDVectorCountPerChannel; - drflac_uint32 decodedSamplesAllocationSize; -#ifndef DR_FLAC_NO_OGG - drflac_oggbs oggbs; -#endif - drflac_uint64 firstFramePos; - drflac_uint64 seektablePos; - drflac_uint32 seektableSize; - drflac_allocation_callbacks allocationCallbacks; - drflac* pFlac; - drflac__init_cpu_caps(); - if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { - return NULL; - } - if (pAllocationCallbacks != NULL) { - allocationCallbacks = *pAllocationCallbacks; - if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { - return NULL; - } - } else { - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = drflac__malloc_default; - allocationCallbacks.onRealloc = drflac__realloc_default; - allocationCallbacks.onFree = drflac__free_default; - } - allocationSize = sizeof(drflac); - if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); - } else { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; - } - decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; - allocationSize += decodedSamplesAllocationSize; - allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) { - allocationSize += sizeof(drflac_oggbs); - } - DRFLAC_ZERO_MEMORY(&oggbs, sizeof(oggbs)); - if (init.container == drflac_container_ogg) { - oggbs.onRead = onRead; - oggbs.onSeek = onSeek; - oggbs.pUserData = pUserData; - oggbs.currentBytePos = init.oggFirstBytePos; - oggbs.firstBytePos = init.oggFirstBytePos; - oggbs.serialNumber = init.oggSerial; - oggbs.bosPageHeader = init.oggBosHeader; - oggbs.bytesRemainingInPage = 0; - } -#endif - firstFramePos = 42; - seektablePos = 0; - seektableSize = 0; - if (init.hasMetadataBlocks) { - drflac_read_proc onReadOverride = onRead; - drflac_seek_proc onSeekOverride = onSeek; - void* pUserDataOverride = pUserData; -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) { - onReadOverride = drflac__on_read_ogg; - onSeekOverride = drflac__on_seek_ogg; - pUserDataOverride = (void*)&oggbs; - } -#endif - if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize, &allocationCallbacks)) { - return NULL; - } - allocationSize += seektableSize; - } - pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - drflac__init_from_info(pFlac, &init); - pFlac->allocationCallbacks = allocationCallbacks; - pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) { - drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize); - DRFLAC_COPY_MEMORY(pInternalOggbs, &oggbs, sizeof(oggbs)); - pFlac->bs.onRead = drflac__on_read_ogg; - pFlac->bs.onSeek = drflac__on_seek_ogg; - pFlac->bs.pUserData = (void*)pInternalOggbs; - pFlac->_oggbs = (void*)pInternalOggbs; - } -#endif - pFlac->firstFLACFramePosInBytes = firstFramePos; -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) - { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - else -#endif - { - if (seektablePos != 0) { - pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints); - pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); - DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); - DRFLAC_ASSERT(pFlac->bs.onRead != NULL); - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { - if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) { - drflac_uint32 iSeekpoint; - for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { - pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); - pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); - pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); - } - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { - drflac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - } - } - if (!init.hasStreamInfoBlock) { - pFlac->currentFLACFrame.header = init.firstFrameHeader; - for (;;) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - break; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - drflac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - continue; - } else { - drflac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - } - } - } - return pFlac; -} -#ifndef DR_FLAC_NO_STDIO -#include -#include -#include -static drflac_result drflac_result_from_errno(int e) -{ - switch (e) - { - case 0: return DRFLAC_SUCCESS; - #ifdef EPERM - case EPERM: return DRFLAC_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return DRFLAC_INTERRUPT; - #endif - #ifdef EIO - case EIO: return DRFLAC_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return DRFLAC_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return DRFLAC_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return DRFLAC_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return DRFLAC_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return DRFLAC_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return DRFLAC_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return DRFLAC_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return DRFLAC_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return DRFLAC_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return DRFLAC_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return DRFLAC_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return DRFLAC_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return DRFLAC_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return DRFLAC_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return DRFLAC_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return DRFLAC_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return DRFLAC_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return DRFLAC_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return DRFLAC_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return DRFLAC_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return DRFLAC_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return DRFLAC_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return DRFLAC_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return DRFLAC_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return DRFLAC_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return DRFLAC_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return DRFLAC_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return DRFLAC_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return DRFLAC_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return DRFLAC_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return DRFLAC_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return DRFLAC_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return DRFLAC_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return DRFLAC_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return DRFLAC_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return DRFLAC_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return DRFLAC_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return DRFLAC_ERROR; - #endif - #ifdef EBADE - case EBADE: return DRFLAC_ERROR; - #endif - #ifdef EBADR - case EBADR: return DRFLAC_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return DRFLAC_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return DRFLAC_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return DRFLAC_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return DRFLAC_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return DRFLAC_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return DRFLAC_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return DRFLAC_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return DRFLAC_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return DRFLAC_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return DRFLAC_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return DRFLAC_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return DRFLAC_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return DRFLAC_ERROR; - #endif - #ifdef EADV - case EADV: return DRFLAC_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return DRFLAC_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return DRFLAC_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return DRFLAC_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return DRFLAC_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return DRFLAC_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return DRFLAC_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return DRFLAC_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return DRFLAC_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return DRFLAC_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return DRFLAC_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return DRFLAC_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return DRFLAC_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return DRFLAC_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return DRFLAC_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return DRFLAC_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return DRFLAC_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return DRFLAC_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return DRFLAC_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return DRFLAC_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return DRFLAC_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return DRFLAC_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return DRFLAC_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return DRFLAC_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return DRFLAC_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return DRFLAC_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return DRFLAC_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return DRFLAC_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return DRFLAC_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return DRFLAC_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return DRFLAC_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return DRFLAC_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return DRFLAC_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return DRFLAC_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return DRFLAC_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return DRFLAC_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return DRFLAC_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return DRFLAC_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return DRFLAC_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return DRFLAC_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return DRFLAC_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return DRFLAC_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return DRFLAC_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return DRFLAC_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return DRFLAC_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return DRFLAC_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return DRFLAC_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return DRFLAC_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return DRFLAC_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return DRFLAC_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return DRFLAC_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return DRFLAC_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return DRFLAC_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return DRFLAC_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return DRFLAC_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return DRFLAC_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return DRFLAC_ERROR; - #endif - default: return DRFLAC_ERROR; - } -} -static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRFLAC_INVALID_ARGS; - } -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drflac_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - drflac_result result = drflac_result_from_errno(errno); - if (result == DRFLAC_SUCCESS) { - result = DRFLAC_ERROR; - } - return result; - } -#endif - return DRFLAC_SUCCESS; -} -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define DRFLAC_HAS_WFOPEN - #endif -#endif -static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRFLAC_INVALID_ARGS; - } -#if defined(DRFLAC_HAS_WFOPEN) - { - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drflac_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return drflac_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - DRFLAC_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return drflac_result_from_errno(errno); - } - pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return DRFLAC_OUT_OF_MEMORY; - } - pFilePathTemp = pFilePath; - DRFLAC_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - *ppFile = fopen(pFilePathMB, pOpenModeMB); - drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); - } - if (*ppFile == NULL) { - return DRFLAC_ERROR; - } -#endif - return DRFLAC_SUCCESS; -} -static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); -} -static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) -{ - DRFLAC_ASSERT(offset >= 0); - return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - FILE* pFile; - if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { - return NULL; - } - pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return NULL; - } - return pFlac; -} -DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - FILE* pFile; - if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { - return NULL; - } - pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return NULL; - } - return pFlac; -} -DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - FILE* pFile; - if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { - return NULL; - } - pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return pFlac; - } - return pFlac; -} -DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - FILE* pFile; - if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { - return NULL; - } - pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return pFlac; - } - return pFlac; -} -#endif -static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; - size_t bytesRemaining; - DRFLAC_ASSERT(memoryStream != NULL); - DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); - bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); - memoryStream->currentReadPos += bytesToRead; - } - return bytesToRead; -} -static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) -{ - drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; - DRFLAC_ASSERT(memoryStream != NULL); - DRFLAC_ASSERT(offset >= 0); - if (offset > (drflac_int64)memoryStream->dataSize) { - return DRFLAC_FALSE; - } - if (origin == drflac_seek_origin_current) { - if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { - memoryStream->currentReadPos += offset; - } else { - return DRFLAC_FALSE; - } - } else { - if ((drflac_uint32)offset <= memoryStream->dataSize) { - memoryStream->currentReadPos = offset; - } else { - return DRFLAC_FALSE; - } - } - return DRFLAC_TRUE; -} -DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac__memory_stream memoryStream; - drflac* pFlac; - memoryStream.data = (const drflac_uint8*)pData; - memoryStream.dataSize = dataSize; - memoryStream.currentReadPos = 0; - pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - pFlac->memoryStream = memoryStream; -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) - { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - oggbs->pUserData = &pFlac->memoryStream; - } - else -#endif - { - pFlac->bs.pUserData = &pFlac->memoryStream; - } - return pFlac; -} -DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac__memory_stream memoryStream; - drflac* pFlac; - memoryStream.data = (const drflac_uint8*)pData; - memoryStream.dataSize = dataSize; - memoryStream.currentReadPos = 0; - pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - pFlac->memoryStream = memoryStream; -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) - { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - oggbs->pUserData = &pFlac->memoryStream; - } - else -#endif - { - pFlac->bs.pUserData = &pFlac->memoryStream; - } - return pFlac; -} -DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); -} -DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); -} -DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); -} -DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); -} -DRFLAC_API void drflac_close(drflac* pFlac) -{ - if (pFlac == NULL) { - return; - } -#ifndef DR_FLAC_NO_STDIO - if (pFlac->bs.onRead == drflac__on_read_stdio) { - fclose((FILE*)pFlac->bs.pUserData); - } -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg); - if (oggbs->onRead == drflac__on_read_stdio) { - fclose((FILE*)oggbs->pUserData); - } - } -#endif -#endif - drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - for (i = 0; i < frameCount; ++i) { - drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 right0 = left0 - side0; - drflac_uint32 right1 = left1 - side1; - drflac_uint32 right2 = left2 - side2; - drflac_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (drflac_int32)left0; - pOutputSamples[i*8+1] = (drflac_int32)right0; - pOutputSamples[i*8+2] = (drflac_int32)left1; - pOutputSamples[i*8+3] = (drflac_int32)right1; - pOutputSamples[i*8+4] = (drflac_int32)left2; - pOutputSamples[i*8+5] = (drflac_int32)right2; - pOutputSamples[i*8+6] = (drflac_int32)left3; - pOutputSamples[i*8+7] = (drflac_int32)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - for (i = 0; i < frameCount; ++i) { - drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 left0 = right0 + side0; - drflac_uint32 left1 = right1 + side1; - drflac_uint32 left2 = right2 + side2; - drflac_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (drflac_int32)left0; - pOutputSamples[i*8+1] = (drflac_int32)right0; - pOutputSamples[i*8+2] = (drflac_int32)left1; - pOutputSamples[i*8+3] = (drflac_int32)right1; - pOutputSamples[i*8+4] = (drflac_int32)left2; - pOutputSamples[i*8+5] = (drflac_int32)right2; - pOutputSamples[i*8+6] = (drflac_int32)left3; - pOutputSamples[i*8+7] = (drflac_int32)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_int32 shift = unusedBitsPerSample; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (drflac_int32)temp0L; - pOutputSamples[i*8+1] = (drflac_int32)temp0R; - pOutputSamples[i*8+2] = (drflac_int32)temp1L; - pOutputSamples[i*8+3] = (drflac_int32)temp1R; - pOutputSamples[i*8+4] = (drflac_int32)temp2L; - pOutputSamples[i*8+5] = (drflac_int32)temp2R; - pOutputSamples[i*8+6] = (drflac_int32)temp3L; - pOutputSamples[i*8+7] = (drflac_int32)temp3R; - } - } else { - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); - temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); - temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); - temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); - temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); - temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); - temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); - temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (drflac_int32)temp0L; - pOutputSamples[i*8+1] = (drflac_int32)temp0R; - pOutputSamples[i*8+2] = (drflac_int32)temp1L; - pOutputSamples[i*8+3] = (drflac_int32)temp1R; - pOutputSamples[i*8+4] = (drflac_int32)temp2L; - pOutputSamples[i*8+5] = (drflac_int32)temp2R; - pOutputSamples[i*8+6] = (drflac_int32)temp3L; - pOutputSamples[i*8+7] = (drflac_int32)temp3R; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_int32 shift = unusedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); - } - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_int32 shift = unusedBitsPerSample; - int32x4_t wbpsShift0_4; - int32x4_t wbpsShift1_4; - uint32x4_t one4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - one4 = vdupq_n_u32(1); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; - } - } else { - int32x4_t shift4; - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); - } - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (drflac_int32)tempL0; - pOutputSamples[i*8+1] = (drflac_int32)tempR0; - pOutputSamples[i*8+2] = (drflac_int32)tempL1; - pOutputSamples[i*8+3] = (drflac_int32)tempR1; - pOutputSamples[i*8+4] = (drflac_int32)tempL2; - pOutputSamples[i*8+5] = (drflac_int32)tempR2; - pOutputSamples[i*8+6] = (drflac_int32)tempL3; - pOutputSamples[i*8+7] = (drflac_int32)tempR3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift4_0 = vdupq_n_s32(shift0); - int32x4_t shift4_1 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t right; - left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); - right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) -{ - drflac_uint64 framesRead; - drflac_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - drflac_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - drflac_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; - } - } - return framesRead; -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - for (i = 0; i < frameCount; ++i) { - drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 right0 = left0 - side0; - drflac_uint32 right1 = left1 - side1; - drflac_uint32 right2 = left2 - side2; - drflac_uint32 right3 = left3 - side3; - left0 >>= 16; - left1 >>= 16; - left2 >>= 16; - left3 >>= 16; - right0 >>= 16; - right1 >>= 16; - right2 >>= 16; - right3 >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)left0; - pOutputSamples[i*8+1] = (drflac_int16)right0; - pOutputSamples[i*8+2] = (drflac_int16)left1; - pOutputSamples[i*8+3] = (drflac_int16)right1; - pOutputSamples[i*8+4] = (drflac_int16)left2; - pOutputSamples[i*8+5] = (drflac_int16)right2; - pOutputSamples[i*8+6] = (drflac_int16)left3; - pOutputSamples[i*8+7] = (drflac_int16)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - left = vshrq_n_u32(left, 16); - right = vshrq_n_u32(right, 16); - drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - for (i = 0; i < frameCount; ++i) { - drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 left0 = right0 + side0; - drflac_uint32 left1 = right1 + side1; - drflac_uint32 left2 = right2 + side2; - drflac_uint32 left3 = right3 + side3; - left0 >>= 16; - left1 >>= 16; - left2 >>= 16; - left3 >>= 16; - right0 >>= 16; - right1 >>= 16; - right2 >>= 16; - right3 >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)left0; - pOutputSamples[i*8+1] = (drflac_int16)right0; - pOutputSamples[i*8+2] = (drflac_int16)left1; - pOutputSamples[i*8+3] = (drflac_int16)right1; - pOutputSamples[i*8+4] = (drflac_int16)left2; - pOutputSamples[i*8+5] = (drflac_int16)right2; - pOutputSamples[i*8+6] = (drflac_int16)left3; - pOutputSamples[i*8+7] = (drflac_int16)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - left = vshrq_n_u32(left, 16); - right = vshrq_n_u32(right, 16); - drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - temp0L >>= 16; - temp1L >>= 16; - temp2L >>= 16; - temp3L >>= 16; - temp0R >>= 16; - temp1R >>= 16; - temp2R >>= 16; - temp3R >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)temp0L; - pOutputSamples[i*8+1] = (drflac_int16)temp0R; - pOutputSamples[i*8+2] = (drflac_int16)temp1L; - pOutputSamples[i*8+3] = (drflac_int16)temp1R; - pOutputSamples[i*8+4] = (drflac_int16)temp2L; - pOutputSamples[i*8+5] = (drflac_int16)temp2R; - pOutputSamples[i*8+6] = (drflac_int16)temp3L; - pOutputSamples[i*8+7] = (drflac_int16)temp3R; - } - } else { - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = ((drflac_int32)(mid0 + side0) >> 1); - temp1L = ((drflac_int32)(mid1 + side1) >> 1); - temp2L = ((drflac_int32)(mid2 + side2) >> 1); - temp3L = ((drflac_int32)(mid3 + side3) >> 1); - temp0R = ((drflac_int32)(mid0 - side0) >> 1); - temp1R = ((drflac_int32)(mid1 - side1) >> 1); - temp2R = ((drflac_int32)(mid2 - side2) >> 1); - temp3R = ((drflac_int32)(mid3 - side3) >> 1); - temp0L >>= 16; - temp1L >>= 16; - temp2L >>= 16; - temp3L >>= 16; - temp0R >>= 16; - temp1R >>= 16; - temp2R >>= 16; - temp3R >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)temp0L; - pOutputSamples[i*8+1] = (drflac_int16)temp0R; - pOutputSamples[i*8+2] = (drflac_int16)temp1L; - pOutputSamples[i*8+3] = (drflac_int16)temp1R; - pOutputSamples[i*8+4] = (drflac_int16)temp2L; - pOutputSamples[i*8+5] = (drflac_int16)temp2R; - pOutputSamples[i*8+6] = (drflac_int16)temp3L; - pOutputSamples[i*8+7] = (drflac_int16)temp3R; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); - } - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; - int32x4_t wbpsShift0_4; - int32x4_t wbpsShift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); - } - } else { - int32x4_t shift4; - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); - } - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - tempL0 >>= 16; - tempL1 >>= 16; - tempL2 >>= 16; - tempL3 >>= 16; - tempR0 >>= 16; - tempR1 >>= 16; - tempR2 >>= 16; - tempR3 >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)tempL0; - pOutputSamples[i*8+1] = (drflac_int16)tempR0; - pOutputSamples[i*8+2] = (drflac_int16)tempL1; - pOutputSamples[i*8+3] = (drflac_int16)tempR1; - pOutputSamples[i*8+4] = (drflac_int16)tempL2; - pOutputSamples[i*8+5] = (drflac_int16)tempR2; - pOutputSamples[i*8+6] = (drflac_int16)tempL3; - pOutputSamples[i*8+7] = (drflac_int16)tempR3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t right; - left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); - right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) -{ - drflac_uint64 framesRead; - drflac_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - drflac_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - drflac_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; - } - } - return framesRead; -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - for (i = 0; i < frameCount; ++i) { - drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 right0 = left0 - side0; - drflac_uint32 right1 = left1 - side1; - drflac_uint32 right2 = left2 - side2; - drflac_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; - pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; - pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; - pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; - pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; - pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; - pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; - pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left * factor; - pOutputSamples[i*2+1] = (drflac_int32)right * factor; - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - __m128 factor; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = _mm_set1_ps(1.0f / 8388608.0f); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); - __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float32x4_t factor4; - int32x4_t shift0_4; - int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - float32x4_t leftf; - float32x4_t rightf; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - for (i = 0; i < frameCount; ++i) { - drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 left0 = right0 + side0; - drflac_uint32 left1 = right1 + side1; - drflac_uint32 left2 = right2 + side2; - drflac_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; - pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; - pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; - pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; - pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; - pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; - pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; - pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left * factor; - pOutputSamples[i*2+1] = (drflac_int32)right * factor; - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - __m128 factor; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = _mm_set1_ps(1.0f / 8388608.0f); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); - __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float32x4_t factor4; - int32x4_t shift0_4; - int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - float32x4_t leftf; - float32x4_t rightf; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; - float factor = 1 / 2147483648.0; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; - pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; - pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; - pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; - pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; - pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; - pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; - pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; - } - } else { - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); - temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); - temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); - temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); - temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); - temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); - temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); - temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; - pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; - pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; - pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; - pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; - pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; - pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; - pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample - 8; - float factor; - __m128 factor128; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = 1.0f / 8388608.0f; - factor128 = _mm_set1_ps(factor); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i tempL; - __m128i tempR; - __m128 leftf; - __m128 rightf; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i tempL; - __m128i tempR; - __m128 leftf; - __m128 rightf; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; - } - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample - 8; - float factor; - float32x4_t factor4; - int32x4_t shift4; - int32x4_t wbps0_4; - int32x4_t wbps1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = 1.0f / 8388608.0f; - factor4 = vdupq_n_f32(factor); - wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); - uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; - } - } else { - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; - } - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor; - pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor; - pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor; - pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor; - pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor; - pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor; - pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor; - pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float factor = 1.0f / 8388608.0f; - __m128 factor128 = _mm_set1_ps(factor); - for (i = 0; i < frameCount4; ++i) { - __m128i lefti; - __m128i righti; - __m128 leftf; - __m128 rightf; - lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float factor = 1.0f / 8388608.0f; - float32x4_t factor4 = vdupq_n_f32(factor); - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); - righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) -{ - drflac_uint64 framesRead; - drflac_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - drflac_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - drflac_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; - } - } - return framesRead; -} -DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) -{ - if (pFlac == NULL) { - return DRFLAC_FALSE; - } - if (pFlac->currentPCMFrame == pcmFrameIndex) { - return DRFLAC_TRUE; - } - if (pFlac->firstFLACFramePosInBytes == 0) { - return DRFLAC_FALSE; - } - if (pcmFrameIndex == 0) { - pFlac->currentPCMFrame = 0; - return drflac__seek_to_first_frame(pFlac); - } else { - drflac_bool32 wasSuccessful = DRFLAC_FALSE; - drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame; - if (pcmFrameIndex > pFlac->totalPCMFrameCount) { - pcmFrameIndex = pFlac->totalPCMFrameCount; - } - if (pcmFrameIndex > pFlac->currentPCMFrame) { - drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); - if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { - pFlac->currentFLACFrame.pcmFramesRemaining -= offset; - pFlac->currentPCMFrame = pcmFrameIndex; - return DRFLAC_TRUE; - } - } else { - drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); - drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; - if (currentFLACFramePCMFramesConsumed > offsetAbs) { - pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; - pFlac->currentPCMFrame = pcmFrameIndex; - return DRFLAC_TRUE; - } - } -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) - { - wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); - } - else -#endif - { - if (!pFlac->_noSeekTableSeek) { - wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); - } -#if !defined(DR_FLAC_NO_CRC) - if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { - wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); - } -#endif - if (!wasSuccessful && !pFlac->_noBruteForceSeek) { - wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); - } - } - if (wasSuccessful) { - pFlac->currentPCMFrame = pcmFrameIndex; - } else { - if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) { - drflac_seek_to_pcm_frame(pFlac, 0); - } - } - return wasSuccessful; - } -} -#if defined(SIZE_MAX) - #define DRFLAC_SIZE_MAX SIZE_MAX -#else - #if defined(DRFLAC_64BIT) - #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) - #else - #define DRFLAC_SIZE_MAX 0xFFFFFFFF - #endif -#endif -#define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ -static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\ -{ \ - type* pSampleData = NULL; \ - drflac_uint64 totalPCMFrameCount; \ - \ - DRFLAC_ASSERT(pFlac != NULL); \ - \ - totalPCMFrameCount = pFlac->totalPCMFrameCount; \ - \ - if (totalPCMFrameCount == 0) { \ - type buffer[4096]; \ - drflac_uint64 pcmFramesRead; \ - size_t sampleDataBufferSize = sizeof(buffer); \ - \ - pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ - if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ - type* pNewSampleData; \ - size_t newSampleDataBufferSize; \ - \ - newSampleDataBufferSize = sampleDataBufferSize * 2; \ - pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pNewSampleData == NULL) { \ - drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ - goto on_error; \ - } \ - \ - sampleDataBufferSize = newSampleDataBufferSize; \ - pSampleData = pNewSampleData; \ - } \ - \ - DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ - totalPCMFrameCount += pcmFramesRead; \ - } \ - \ - \ - DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ - } else { \ - drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ - if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \ - goto on_error; \ - } \ - \ - pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ - } \ - \ - if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ - if (channelsOut) *channelsOut = pFlac->channels; \ - if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ - \ - drflac_close(pFlac); \ - return pSampleData; \ - \ -on_error: \ - drflac_close(pFlac); \ - return NULL; \ -} -DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) -DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16) -DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -#ifndef DR_FLAC_NO_STDIO -DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = drflac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = drflac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); -} -DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = drflac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -#endif -DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); -} -DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - drflac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - drflac__free_from_callbacks(p, pAllocationCallbacks); - } else { - drflac__free_default(p, NULL); - } -} -DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) -{ - if (pIter == NULL) { - return; - } - pIter->countRemaining = commentCount; - pIter->pRunningData = (const char*)pComments; -} -DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) -{ - drflac_int32 length; - const char* pComment; - if (pCommentLengthOut) { - *pCommentLengthOut = 0; - } - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return NULL; - } - length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); - pIter->pRunningData += 4; - pComment = pIter->pRunningData; - pIter->pRunningData += length; - pIter->countRemaining -= 1; - if (pCommentLengthOut) { - *pCommentLengthOut = length; - } - return pComment; -} -DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) -{ - if (pIter == NULL) { - return; - } - pIter->countRemaining = trackCount; - pIter->pRunningData = (const char*)pTrackData; -} -DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) -{ - drflac_cuesheet_track cuesheetTrack; - const char* pRunningData; - drflac_uint64 offsetHi; - drflac_uint64 offsetLo; - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return DRFLAC_FALSE; - } - pRunningData = pIter->pRunningData; - offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - cuesheetTrack.offset = offsetLo | (offsetHi << 32); - cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; - DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; - cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; - cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; - cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; - cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index); - pIter->pRunningData = pRunningData; - pIter->countRemaining -= 1; - if (pCuesheetTrack) { - *pCuesheetTrack = cuesheetTrack; - } - return DRFLAC_TRUE; -} -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif -#endif -/* dr_flac_c end */ -#endif /* DRFLAC_IMPLEMENTATION */ -#endif /* MA_NO_FLAC */ - -#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -#if !defined(DR_MP3_IMPLEMENTATION) && !defined(DRMP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ -/* dr_mp3_c begin */ -#ifndef dr_mp3_c -#define dr_mp3_c -#include -#include -#include -DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision) -{ - if (pMajor) { - *pMajor = DRMP3_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = DRMP3_VERSION_MINOR; - } - if (pRevision) { - *pRevision = DRMP3_VERSION_REVISION; - } -} -DRMP3_API const char* drmp3_version_string(void) -{ - return DRMP3_VERSION_STRING; -} -#if defined(__TINYC__) -#define DR_MP3_NO_SIMD -#endif -#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) -#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 -#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES -#define DRMP3_MAX_FRAME_SYNC_MATCHES 10 -#endif -#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE -#define DRMP3_MAX_BITRESERVOIR_BYTES 511 -#define DRMP3_SHORT_BLOCK_TYPE 2 -#define DRMP3_STOP_BLOCK_TYPE 3 -#define DRMP3_MODE_MONO 3 -#define DRMP3_MODE_JOINT_STEREO 1 -#define DRMP3_HDR_SIZE 4 -#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) -#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) -#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) -#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) -#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) -#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) -#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) -#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) -#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) -#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) -#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) -#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) -#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) -#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) -#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) -#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) -#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) -#define DRMP3_BITS_DEQUANTIZER_OUT -1 -#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210) -#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) -#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) -#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) -#if !defined(DR_MP3_NO_SIMD) -#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) -#define DR_MP3_ONLY_SIMD -#endif -#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__)) -#if defined(_MSC_VER) -#include -#endif -#include -#define DRMP3_HAVE_SSE 1 -#define DRMP3_HAVE_SIMD 1 -#define DRMP3_VSTORE _mm_storeu_ps -#define DRMP3_VLD _mm_loadu_ps -#define DRMP3_VSET _mm_set1_ps -#define DRMP3_VADD _mm_add_ps -#define DRMP3_VSUB _mm_sub_ps -#define DRMP3_VMUL _mm_mul_ps -#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) -#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) -#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) -#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) -typedef __m128 drmp3_f4; -#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) -#define drmp3_cpuid __cpuid -#else -static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) -{ -#if defined(__PIC__) - __asm__ __volatile__( -#if defined(__x86_64__) - "push %%rbx\n" - "cpuid\n" - "xchgl %%ebx, %1\n" - "pop %%rbx\n" -#else - "xchgl %%ebx, %1\n" - "cpuid\n" - "xchgl %%ebx, %1\n" -#endif - : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) - : "a" (InfoType)); -#else - __asm__ __volatile__( - "cpuid" - : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) - : "a" (InfoType)); -#endif -} -#endif -static int drmp3_have_simd(void) -{ -#ifdef DR_MP3_ONLY_SIMD - return 1; -#else - static int g_have_simd; - int CPUInfo[4]; -#ifdef MINIMP3_TEST - static int g_counter; - if (g_counter++ > 100) - return 0; -#endif - if (g_have_simd) - goto end; - drmp3_cpuid(CPUInfo, 0); - if (CPUInfo[0] > 0) - { - drmp3_cpuid(CPUInfo, 1); - g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; - return g_have_simd - 1; - } -end: - return g_have_simd - 1; -#endif -} -#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) -#include -#define DRMP3_HAVE_SSE 0 -#define DRMP3_HAVE_SIMD 1 -#define DRMP3_VSTORE vst1q_f32 -#define DRMP3_VLD vld1q_f32 -#define DRMP3_VSET vmovq_n_f32 -#define DRMP3_VADD vaddq_f32 -#define DRMP3_VSUB vsubq_f32 -#define DRMP3_VMUL vmulq_f32 -#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) -#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) -#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) -#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) -typedef float32x4_t drmp3_f4; -static int drmp3_have_simd(void) -{ - return 1; -} -#else -#define DRMP3_HAVE_SSE 0 -#define DRMP3_HAVE_SIMD 0 -#ifdef DR_MP3_ONLY_SIMD -#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled -#endif -#endif -#else -#define DRMP3_HAVE_SIMD 0 -#endif -#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) -#define DRMP3_HAVE_ARMV6 1 -static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a) -{ - drmp3_int32 x = 0; - __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); - return x; -} -#else -#define DRMP3_HAVE_ARMV6 0 -#endif -#ifndef DRMP3_ASSERT -#include -#define DRMP3_ASSERT(expression) assert(expression) -#endif -#ifndef DRMP3_COPY_MEMORY -#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef DRMP3_MOVE_MEMORY -#define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif -#ifndef DRMP3_ZERO_MEMORY -#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) -#ifndef DRMP3_MALLOC -#define DRMP3_MALLOC(sz) malloc((sz)) -#endif -#ifndef DRMP3_REALLOC -#define DRMP3_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef DRMP3_FREE -#define DRMP3_FREE(p) free((p)) -#endif -typedef struct -{ - const drmp3_uint8 *buf; - int pos, limit; -} drmp3_bs; -typedef struct -{ - float scf[3*64]; - drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; -} drmp3_L12_scale_info; -typedef struct -{ - drmp3_uint8 tab_offset, code_tab_width, band_count; -} drmp3_L12_subband_alloc; -typedef struct -{ - const drmp3_uint8 *sfbtab; - drmp3_uint16 part_23_length, big_values, scalefac_compress; - drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; - drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; - drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; -} drmp3_L3_gr_info; -typedef struct -{ - drmp3_bs bs; - drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; - drmp3_L3_gr_info gr_info[4]; - float grbuf[2][576], scf[40], syn[18 + 15][2*32]; - drmp3_uint8 ist_pos[2][39]; -} drmp3dec_scratch; -static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) -{ - bs->buf = data; - bs->pos = 0; - bs->limit = bytes*8; -} -static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) -{ - drmp3_uint32 next, cache = 0, s = bs->pos & 7; - int shl = n + s; - const drmp3_uint8 *p = bs->buf + (bs->pos >> 3); - if ((bs->pos += n) > bs->limit) - return 0; - next = *p++ & (255 >> s); - while ((shl -= 8) > 0) - { - cache |= next << shl; - next = *p++; - } - return cache | (next >> -shl); -} -static int drmp3_hdr_valid(const drmp3_uint8 *h) -{ - return h[0] == 0xff && - ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && - (DRMP3_HDR_GET_LAYER(h) != 0) && - (DRMP3_HDR_GET_BITRATE(h) != 15) && - (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); -} -static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2) -{ - return drmp3_hdr_valid(h2) && - ((h1[1] ^ h2[1]) & 0xFE) == 0 && - ((h1[2] ^ h2[2]) & 0x0C) == 0 && - !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); -} -static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h) -{ - static const drmp3_uint8 halfrate[2][3][15] = { - { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, - { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, - }; - return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)]; -} -static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h) -{ - static const unsigned g_hz[3] = { 44100, 48000, 32000 }; - return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); -} -static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h) -{ - return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); -} -static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size) -{ - int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h); - if (DRMP3_HDR_IS_LAYER_1(h)) - { - frame_bytes &= ~3; - } - return frame_bytes ? frame_bytes : free_format_size; -} -static int drmp3_hdr_padding(const drmp3_uint8 *h) -{ - return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; -} -#ifndef DR_MP3_ONLY_MP3 -static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci) -{ - const drmp3_L12_subband_alloc *alloc; - int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); - int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; - if (DRMP3_HDR_IS_LAYER_1(hdr)) - { - static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; - alloc = g_alloc_L1; - nbands = 32; - } else if (!DRMP3_HDR_TEST_MPEG1(hdr)) - { - static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; - alloc = g_alloc_L2M2; - nbands = 30; - } else - { - static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; - int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); - unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); - if (!kbps) - { - kbps = 192; - } - alloc = g_alloc_L2M1; - nbands = 27; - if (kbps < 56) - { - static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; - alloc = g_alloc_L2M1_lowrate; - nbands = sample_rate_idx == 2 ? 12 : 8; - } else if (kbps >= 96 && sample_rate_idx != 1) - { - nbands = 30; - } - } - sci->total_bands = (drmp3_uint8)nbands; - sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); - return alloc; -} -static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf) -{ - static const float g_deq_L12[18*3] = { -#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x - DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9) - }; - int i, m; - for (i = 0; i < bands; i++) - { - float s = 0; - int ba = *pba++; - int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; - for (m = 4; m; m >>= 1) - { - if (mask & m) - { - int b = drmp3_bs_get_bits(bs, 6); - s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); - } - *scf++ = s; - } - } -} -static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci) -{ - static const drmp3_uint8 g_bitalloc_code_tab[] = { - 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, - 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, - 0,17,18, 3,19,4,5,16, - 0,17,18,16, - 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, - 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, - 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 - }; - const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); - int i, k = 0, ba_bits = 0; - const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab; - for (i = 0; i < sci->total_bands; i++) - { - drmp3_uint8 ba; - if (i == k) - { - k += subband_alloc->band_count; - ba_bits = subband_alloc->code_tab_width; - ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; - subband_alloc++; - } - ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; - sci->bitalloc[2*i] = ba; - if (i < sci->stereo_bands) - { - ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; - } - sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; - } - for (i = 0; i < 2*sci->total_bands; i++) - { - sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6); - } - drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); - for (i = sci->stereo_bands; i < sci->total_bands; i++) - { - sci->bitalloc[2*i + 1] = 0; - } -} -static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size) -{ - int i, j, k, choff = 576; - for (j = 0; j < 4; j++) - { - float *dst = grbuf + group_size*j; - for (i = 0; i < 2*sci->total_bands; i++) - { - int ba = sci->bitalloc[i]; - if (ba != 0) - { - if (ba < 17) - { - int half = (1 << (ba - 1)) - 1; - for (k = 0; k < group_size; k++) - { - dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); - } - } else - { - unsigned mod = (2 << (ba - 17)) + 1; - unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); - for (k = 0; k < group_size; k++, code /= mod) - { - dst[k] = (float)((int)(code % mod - mod/2)); - } - } - } - dst += choff; - choff = 18 - choff; - } - } - return group_size*4; -} -static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst) -{ - int i, k; - DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); - for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) - { - for (k = 0; k < 12; k++) - { - dst[k + 0] *= scf[0]; - dst[k + 576] *= scf[3]; - } - } -} -#endif -static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) -{ - static const drmp3_uint8 g_scf_long[8][23] = { - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, - { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, - { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } - }; - static const drmp3_uint8 g_scf_short[8][40] = { - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } - }; - static const drmp3_uint8 g_scf_mixed[8][40] = { - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, - { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } - }; - unsigned tables, scfsi = 0; - int main_data_begin, part_23_sum = 0; - int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; - int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); - if (DRMP3_HDR_TEST_MPEG1(hdr)) - { - gr_count *= 2; - main_data_begin = drmp3_bs_get_bits(bs, 9); - scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); - } else - { - main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; - } - do - { - if (DRMP3_HDR_IS_MONO(hdr)) - { - scfsi <<= 4; - } - gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); - part_23_sum += gr->part_23_length; - gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); - if (gr->big_values > 288) - { - return -1; - } - gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); - gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); - gr->sfbtab = g_scf_long[sr_idx]; - gr->n_long_sfb = 22; - gr->n_short_sfb = 0; - if (drmp3_bs_get_bits(bs, 1)) - { - gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); - if (!gr->block_type) - { - return -1; - } - gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); - gr->region_count[0] = 7; - gr->region_count[1] = 255; - if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) - { - scfsi &= 0x0F0F; - if (!gr->mixed_block_flag) - { - gr->region_count[0] = 8; - gr->sfbtab = g_scf_short[sr_idx]; - gr->n_long_sfb = 0; - gr->n_short_sfb = 39; - } else - { - gr->sfbtab = g_scf_mixed[sr_idx]; - gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; - gr->n_short_sfb = 30; - } - } - tables = drmp3_bs_get_bits(bs, 10); - tables <<= 5; - gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); - gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); - gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); - } else - { - gr->block_type = 0; - gr->mixed_block_flag = 0; - tables = drmp3_bs_get_bits(bs, 15); - gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); - gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); - gr->region_count[2] = 255; - } - gr->table_select[0] = (drmp3_uint8)(tables >> 10); - gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); - gr->table_select[2] = (drmp3_uint8)((tables) & 31); - gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); - gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); - gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); - gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); - scfsi <<= 4; - gr++; - } while(--gr_count); - if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) - { - return -1; - } - return main_data_begin; -} -static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi) -{ - int i, k; - for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) - { - int cnt = scf_count[i]; - if (scfsi & 8) - { - DRMP3_COPY_MEMORY(scf, ist_pos, cnt); - } else - { - int bits = scf_size[i]; - if (!bits) - { - DRMP3_ZERO_MEMORY(scf, cnt); - DRMP3_ZERO_MEMORY(ist_pos, cnt); - } else - { - int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; - for (k = 0; k < cnt; k++) - { - int s = drmp3_bs_get_bits(bitbuf, bits); - ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); - scf[k] = (drmp3_uint8)s; - } - } - } - ist_pos += cnt; - scf += cnt; - } - scf[0] = scf[1] = scf[2] = 0; -} -static float drmp3_L3_ldexp_q2(float y, int exp_q2) -{ - static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; - int e; - do - { - e = DRMP3_MIN(30*4, exp_q2); - y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); - } while ((exp_q2 -= e) > 0); - return y; -} -static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) -{ - static const drmp3_uint8 g_scf_partitions[3][28] = { - { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, - { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, - { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } - }; - const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; - drmp3_uint8 scf_size[4], iscf[40]; - int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; - float gain; - if (DRMP3_HDR_TEST_MPEG1(hdr)) - { - static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; - int part = g_scfc_decode[gr->scalefac_compress]; - scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); - scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); - } else - { - static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; - int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; - sfc = gr->scalefac_compress >> ist; - for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) - { - for (modprod = 1, i = 3; i >= 0; i--) - { - scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); - modprod *= g_mod[k + i]; - } - } - scf_partition += k; - scfsi = -16; - } - drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); - if (gr->n_short_sfb) - { - int sh = 3 - scf_shift; - for (i = 0; i < gr->n_short_sfb; i += 3) - { - iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); - iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); - iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); - } - } else if (gr->preflag) - { - static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; - for (i = 0; i < 10; i++) - { - iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]); - } - } - gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); - gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp); - for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) - { - scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); - } -} -static const float g_drmp3_pow43[129 + 16] = { - 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, - 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f -}; -static float drmp3_L3_pow_43(int x) -{ - float frac; - int sign, mult = 256; - if (x < 129) - { - return g_drmp3_pow43[16 + x]; - } - if (x < 1024) - { - mult = 16; - x <<= 3; - } - sign = 2*x & 64; - frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; -} -static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) -{ - static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, - -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, - -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, - -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, - -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, - -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, - -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, - -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, - -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, - -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, - -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, - -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, - -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, - -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, - -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; - static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; - static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; - static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; - static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n)) -#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } -#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } -#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) - float one = 0.0f; - int ireg = 0, big_val_cnt = gr_info->big_values; - const drmp3_uint8 *sfb = gr_info->sfbtab; - const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8; - drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); - int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; - bs_next_ptr += 4; - while (big_val_cnt > 0) - { - int tab_num = gr_info->table_select[ireg]; - int sfb_cnt = gr_info->region_count[ireg++]; - const drmp3_int16 *codebook = tabs + tabindex[tab_num]; - int linbits = g_linbits[tab_num]; - if (linbits) - { - do - { - np = *sfb++ / 2; - pairs_to_decode = DRMP3_MIN(big_val_cnt, np); - one = *scf++; - do - { - int j, w = 5; - int leaf = codebook[DRMP3_PEEK_BITS(w)]; - while (leaf < 0) - { - DRMP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; - } - DRMP3_FLUSH_BITS(leaf >> 8); - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - if (lsb == 15) - { - lsb += DRMP3_PEEK_BITS(linbits); - DRMP3_FLUSH_BITS(linbits); - DRMP3_CHECK_BITS; - *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1); - } else - { - *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - } - DRMP3_FLUSH_BITS(lsb ? 1 : 0); - } - DRMP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); - } else - { - do - { - np = *sfb++ / 2; - pairs_to_decode = DRMP3_MIN(big_val_cnt, np); - one = *scf++; - do - { - int j, w = 5; - int leaf = codebook[DRMP3_PEEK_BITS(w)]; - while (leaf < 0) - { - DRMP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; - } - DRMP3_FLUSH_BITS(leaf >> 8); - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - DRMP3_FLUSH_BITS(lsb ? 1 : 0); - } - DRMP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); - } - } - for (np = 1 - big_val_cnt;; dst += 4) - { - const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; - int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; - if (!(leaf & 8)) - { - leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; - } - DRMP3_FLUSH_BITS(leaf & 7); - if (DRMP3_BSPOS > layer3gr_limit) - { - break; - } -#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } -#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) } - DRMP3_RELOAD_SCALEFACTOR; - DRMP3_DEQ_COUNT1(0); - DRMP3_DEQ_COUNT1(1); - DRMP3_RELOAD_SCALEFACTOR; - DRMP3_DEQ_COUNT1(2); - DRMP3_DEQ_COUNT1(3); - DRMP3_CHECK_BITS; - } - bs->pos = layer3gr_limit; -} -static void drmp3_L3_midside_stereo(float *left, int n) -{ - int i = 0; - float *right = left + 576; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) - { - for (; i < n - 3; i += 4) - { - drmp3_f4 vl = DRMP3_VLD(left + i); - drmp3_f4 vr = DRMP3_VLD(right + i); - DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); - DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); - } -#ifdef __GNUC__ - if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) - return; -#endif - } -#endif - for (; i < n; i++) - { - float a = left[i]; - float b = right[i]; - left[i] = a + b; - right[i] = a - b; - } -} -static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) -{ - int i; - for (i = 0; i < n; i++) - { - left[i + 576] = left[i]*kr; - left[i] = left[i]*kl; - } -} -static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3]) -{ - int i, k; - max_band[0] = max_band[1] = max_band[2] = -1; - for (i = 0; i < nbands; i++) - { - for (k = 0; k < sfb[i]; k += 2) - { - if (right[k] != 0 || right[k + 1] != 0) - { - max_band[i % 3] = i; - break; - } - } - right += sfb[i]; - } -} -static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh) -{ - static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; - unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; - for (i = 0; sfb[i]; i++) - { - unsigned ipos = ist_pos[i]; - if ((int)i > max_band[i % 3] && ipos < max_pos) - { - float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; - if (DRMP3_HDR_TEST_MPEG1(hdr)) - { - kl = g_pan[2*ipos]; - kr = g_pan[2*ipos + 1]; - } else - { - kl = 1; - kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); - if (ipos & 1) - { - kl = kr; - kr = 1; - } - } - drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); - } else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) - { - drmp3_L3_midside_stereo(left, sfb[i]); - } - left += sfb[i]; - } -} -static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) -{ - int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; - int i, max_blocks = gr->n_short_sfb ? 3 : 1; - drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); - if (gr->n_long_sfb) - { - max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); - } - for (i = 0; i < max_blocks; i++) - { - int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; - int itop = n_sfb - max_blocks + i; - int prev = itop - max_blocks; - ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); - } - drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); -} -static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) -{ - int i, len; - float *src = grbuf, *dst = scratch; - for (;0 != (len = *sfb); sfb += 3, src += 2*len) - { - for (i = 0; i < len; i++, src++) - { - *dst++ = src[0*len]; - *dst++ = src[1*len]; - *dst++ = src[2*len]; - } - } - DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); -} -static void drmp3_L3_antialias(float *grbuf, int nbands) -{ - static const float g_aa[2][8] = { - {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, - {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} - }; - for (; nbands > 0; nbands--, grbuf += 18) - { - int i = 0; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (; i < 8; i += 4) - { - drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); - drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); - drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); - drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); - vd = DRMP3_VREV(vd); - DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); - vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); - DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); - } -#endif -#ifndef DR_MP3_ONLY_SIMD - for(; i < 8; i++) - { - float u = grbuf[18 + i]; - float d = grbuf[17 - i]; - grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; - grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; - } -#endif - } -} -static void drmp3_L3_dct3_9(float *y) -{ - float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; - s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; - t0 = s0 + s6*0.5f; - s0 -= s6; - t4 = (s4 + s2)*0.93969262f; - t2 = (s8 + s2)*0.76604444f; - s6 = (s4 - s8)*0.17364818f; - s4 += s8 - s2; - s2 = s0 - s4*0.5f; - y[4] = s4 + s0; - s8 = t0 - t2 + s6; - s0 = t0 - t4 + t2; - s4 = t0 + t4 - s6; - s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; - s3 *= 0.86602540f; - t0 = (s5 + s1)*0.98480775f; - t4 = (s5 - s7)*0.34202014f; - t2 = (s1 + s7)*0.64278761f; - s1 = (s1 - s5 - s7)*0.86602540f; - s5 = t0 - s3 - t2; - s7 = t4 - s3 - t0; - s3 = t4 + s3 - t2; - y[0] = s4 - s7; - y[1] = s2 + s1; - y[2] = s0 - s3; - y[3] = s8 + s5; - y[5] = s8 - s5; - y[6] = s0 + s3; - y[7] = s2 - s1; - y[8] = s4 + s7; -} -static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) -{ - int i, j; - static const float g_twid9[18] = { - 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f - }; - for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) - { - float co[9], si[9]; - co[0] = -grbuf[0]; - si[0] = grbuf[17]; - for (i = 0; i < 4; i++) - { - si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; - co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; - si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; - co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); - } - drmp3_L3_dct3_9(co); - drmp3_L3_dct3_9(si); - si[1] = -si[1]; - si[3] = -si[3]; - si[5] = -si[5]; - si[7] = -si[7]; - i = 0; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (; i < 8; i += 4) - { - drmp3_f4 vovl = DRMP3_VLD(overlap + i); - drmp3_f4 vc = DRMP3_VLD(co + i); - drmp3_f4 vs = DRMP3_VLD(si + i); - drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); - drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); - drmp3_f4 vw0 = DRMP3_VLD(window + i); - drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); - drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); - DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); - DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); - vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); - DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); - } -#endif - for (; i < 9; i++) - { - float ovl = overlap[i]; - float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; - overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; - grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; - grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; - } - } -} -static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) -{ - float m1 = x1*0.86602540f; - float a1 = x0 - x2*0.5f; - dst[1] = x0 + x2; - dst[0] = a1 + m1; - dst[2] = a1 - m1; -} -static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) -{ - static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; - float co[3], si[3]; - int i; - drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); - drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); - si[1] = -si[1]; - for (i = 0; i < 3; i++) - { - float ovl = overlap[i]; - float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; - overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; - dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; - dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; - } -} -static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) -{ - for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) - { - float tmp[18]; - DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); - DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); - drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); - drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); - drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); - } -} -static void drmp3_L3_change_sign(float *grbuf) -{ - int b, i; - for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) - for (i = 1; i < 18; i += 2) - grbuf[i] = -grbuf[i]; -} -static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) -{ - static const float g_mdct_window[2][18] = { - { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, - { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } - }; - if (n_long_bands) - { - drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); - grbuf += 18*n_long_bands; - overlap += 9*n_long_bands; - } - if (block_type == DRMP3_SHORT_BLOCK_TYPE) - drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); - else - drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands); -} -static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s) -{ - int pos = (s->bs.pos + 7)/8u; - int remains = s->bs.limit/8u - pos; - if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) - { - pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; - remains = DRMP3_MAX_BITRESERVOIR_BYTES; - } - if (remains > 0) - { - DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); - } - h->reserv = remains; -} -static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin) -{ - int frame_bytes = (bs->limit - bs->pos)/8; - int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); - DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin)); - DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); - drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); - return h->reserv >= main_data_begin; -} -static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch) -{ - int ch; - for (ch = 0; ch < nch; ch++) - { - int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; - drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); - drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); - } - if (DRMP3_HDR_TEST_I_STEREO(h->header)) - { - drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); - } else if (DRMP3_HDR_IS_MS_STEREO(h->header)) - { - drmp3_L3_midside_stereo(s->grbuf[0], 576); - } - for (ch = 0; ch < nch; ch++, gr_info++) - { - int aa_bands = 31; - int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); - if (gr_info->n_short_sfb) - { - aa_bands = n_long_bands - 1; - drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); - } - drmp3_L3_antialias(s->grbuf[ch], aa_bands); - drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); - drmp3_L3_change_sign(s->grbuf[ch]); - } -} -static void drmp3d_DCT_II(float *grbuf, int n) -{ - static const float g_sec[24] = { - 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f - }; - int i, k = 0; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (; k < n; k += 4) - { - drmp3_f4 t[4][8], *x; - float *y = grbuf + k; - for (x = t[0], i = 0; i < 8; i++, x++) - { - drmp3_f4 x0 = DRMP3_VLD(&y[i*18]); - drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]); - drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]); - drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]); - drmp3_f4 t0 = DRMP3_VADD(x0, x3); - drmp3_f4 t1 = DRMP3_VADD(x1, x2); - drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]); - drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]); - x[0] = DRMP3_VADD(t0, t1); - x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]); - x[16] = DRMP3_VADD(t3, t2); - x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]); - } - for (x = t[0], i = 0; i < 4; i++, x += 8) - { - drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7); - x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6); - x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5); - x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4); - x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3); - x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2); - x[0] = DRMP3_VADD(x0, x1); - x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); - x5 = DRMP3_VADD(x5, x6); - x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); - x7 = DRMP3_VADD(x7, xt); - x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); - x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); - x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); - x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); - x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6); - x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); - x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); - x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); - x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); - x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); - x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); - } - if (k > n - 3) - { -#if DRMP3_HAVE_SSE -#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) -#else -#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v)) -#endif - for (i = 0; i < 7; i++, y += 4*18) - { - drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); - DRMP3_VSAVE2(0, t[0][i]); - DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); - DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); - DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); - } - DRMP3_VSAVE2(0, t[0][7]); - DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); - DRMP3_VSAVE2(2, t[1][7]); - DRMP3_VSAVE2(3, t[3][7]); - } else - { -#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v) - for (i = 0; i < 7; i++, y += 4*18) - { - drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); - DRMP3_VSAVE4(0, t[0][i]); - DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); - DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); - DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); - } - DRMP3_VSAVE4(0, t[0][7]); - DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); - DRMP3_VSAVE4(2, t[1][7]); - DRMP3_VSAVE4(3, t[3][7]); - } - } else -#endif -#ifdef DR_MP3_ONLY_SIMD - {} -#else - for (; k < n; k++) - { - float t[4][8], *x, *y = grbuf + k; - for (x = t[0], i = 0; i < 8; i++, x++) - { - float x0 = y[i*18]; - float x1 = y[(15 - i)*18]; - float x2 = y[(16 + i)*18]; - float x3 = y[(31 - i)*18]; - float t0 = x0 + x3; - float t1 = x1 + x2; - float t2 = (x1 - x2)*g_sec[3*i + 0]; - float t3 = (x0 - x3)*g_sec[3*i + 1]; - x[0] = t0 + t1; - x[8] = (t0 - t1)*g_sec[3*i + 2]; - x[16] = t3 + t2; - x[24] = (t3 - t2)*g_sec[3*i + 2]; - } - for (x = t[0], i = 0; i < 4; i++, x += 8) - { - float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = x0 - x7; x0 += x7; - x7 = x1 - x6; x1 += x6; - x6 = x2 - x5; x2 += x5; - x5 = x3 - x4; x3 += x4; - x4 = x0 - x3; x0 += x3; - x3 = x1 - x2; x1 += x2; - x[0] = x0 + x1; - x[4] = (x0 - x1)*0.70710677f; - x5 = x5 + x6; - x6 = (x6 + x7)*0.70710677f; - x7 = x7 + xt; - x3 = (x3 + x4)*0.70710677f; - x5 -= x7*0.198912367f; - x7 += x5*0.382683432f; - x5 -= x7*0.198912367f; - x0 = xt - x6; xt += x6; - x[1] = (xt + x7)*0.50979561f; - x[2] = (x4 + x3)*0.54119611f; - x[3] = (x0 - x5)*0.60134488f; - x[5] = (x0 + x5)*0.89997619f; - x[6] = (x4 - x3)*1.30656302f; - x[7] = (xt - x7)*2.56291556f; - } - for (i = 0; i < 7; i++, y += 4*18) - { - y[0*18] = t[0][i]; - y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; - y[2*18] = t[1][i] + t[1][i + 1]; - y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; - } - y[0*18] = t[0][7]; - y[1*18] = t[2][7] + t[3][7]; - y[2*18] = t[1][7]; - y[3*18] = t[3][7]; - } -#endif -} -#ifndef DR_MP3_FLOAT_OUTPUT -typedef drmp3_int16 drmp3d_sample_t; -static drmp3_int16 drmp3d_scale_pcm(float sample) -{ - drmp3_int16 s; -#if DRMP3_HAVE_ARMV6 - drmp3_int32 s32 = (drmp3_int32)(sample + .5f); - s32 -= (s32 < 0); - s = (drmp3_int16)drmp3_clip_int16_arm(s32); -#else - if (sample >= 32766.5) return (drmp3_int16) 32767; - if (sample <= -32767.5) return (drmp3_int16)-32768; - s = (drmp3_int16)(sample + .5f); - s -= (s < 0); -#endif - return s; -} -#else -typedef float drmp3d_sample_t; -static float drmp3d_scale_pcm(float sample) -{ - return sample*(1.f/32768.f); -} -#endif -static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) -{ - float a; - a = (z[14*64] - z[ 0]) * 29; - a += (z[ 1*64] + z[13*64]) * 213; - a += (z[12*64] - z[ 2*64]) * 459; - a += (z[ 3*64] + z[11*64]) * 2037; - a += (z[10*64] - z[ 4*64]) * 5153; - a += (z[ 5*64] + z[ 9*64]) * 6574; - a += (z[ 8*64] - z[ 6*64]) * 37489; - a += z[ 7*64] * 75038; - pcm[0] = drmp3d_scale_pcm(a); - z += 2; - a = z[14*64] * 104; - a += z[12*64] * 1567; - a += z[10*64] * 9727; - a += z[ 8*64] * 64019; - a += z[ 6*64] * -9975; - a += z[ 4*64] * -45; - a += z[ 2*64] * 146; - a += z[ 0*64] * -5; - pcm[16*nch] = drmp3d_scale_pcm(a); -} -static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) -{ - int i; - float *xr = xl + 576*(nch - 1); - drmp3d_sample_t *dstr = dstl + (nch - 1); - static const float g_win[] = { - -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, - -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, - -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, - -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, - -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, - -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, - -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, - -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, - -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, - -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, - -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, - -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, - -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, - -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, - -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 - }; - float *zlin = lins + 15*64; - const float *w = g_win; - zlin[4*15] = xl[18*16]; - zlin[4*15 + 1] = xr[18*16]; - zlin[4*15 + 2] = xl[0]; - zlin[4*15 + 3] = xr[0]; - zlin[4*31] = xl[1 + 18*16]; - zlin[4*31 + 1] = xr[1 + 18*16]; - zlin[4*31 + 2] = xl[1]; - zlin[4*31 + 3] = xr[1]; - drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1); - drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); - drmp3d_synth_pair(dstl, nch, lins + 4*15); - drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (i = 14; i >= 0; i--) - { -#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]); -#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); } -#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); } -#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); } - drmp3_f4 a, b; - zlin[4*i] = xl[18*(31 - i)]; - zlin[4*i + 1] = xr[18*(31 - i)]; - zlin[4*i + 2] = xl[1 + 18*(31 - i)]; - zlin[4*i + 3] = xr[1 + 18*(31 - i)]; - zlin[4*i + 64] = xl[1 + 18*(1 + i)]; - zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; - zlin[4*i - 64 + 2] = xl[18*(1 + i)]; - zlin[4*i - 64 + 3] = xr[18*(1 + i)]; - DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) - { -#ifndef DR_MP3_FLOAT_OUTPUT -#if DRMP3_HAVE_SSE - static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; - static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); - dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); - dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); - dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); - dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); - dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); - dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); - dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); -#else - int16x4_t pcma, pcmb; - a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); - b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); - vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); - vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); - vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); - vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); - vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); - vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); - vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); - vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); -#endif -#else - #if DRMP3_HAVE_SSE - static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; - #else - const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); - #endif - a = DRMP3_VMUL(a, g_scale); - b = DRMP3_VMUL(b, g_scale); -#if DRMP3_HAVE_SSE - _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); - _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); - _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); - _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); -#else - vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); - vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); - vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); - vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); - vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); - vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); - vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); - vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); -#endif -#endif - } - } else -#endif -#ifdef DR_MP3_ONLY_SIMD - {} -#else - for (i = 14; i >= 0; i--) - { -#define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; -#define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } -#define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } -#define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } - float a[4], b[4]; - zlin[4*i] = xl[18*(31 - i)]; - zlin[4*i + 1] = xr[18*(31 - i)]; - zlin[4*i + 2] = xl[1 + 18*(31 - i)]; - zlin[4*i + 3] = xr[1 + 18*(31 - i)]; - zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; - zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; - zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; - zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; - DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) - dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]); - dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]); - dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]); - dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]); - dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]); - dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]); - dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]); - dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]); - } -#endif -} -static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins) -{ - int i; - for (i = 0; i < nch; i++) - { - drmp3d_DCT_II(grbuf + 576*i, nbands); - } - DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); - for (i = 0; i < nbands; i += 2) - { - drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); - } -#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL - if (nch == 1) - { - for (i = 0; i < 15*64; i += 2) - { - qmf_state[i] = lins[nbands*64 + i]; - } - } else -#endif - { - DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); - } -} -static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes) -{ - int i, nmatch; - for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) - { - i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); - if (i + DRMP3_HDR_SIZE > mp3_bytes) - return nmatch > 0; - if (!drmp3_hdr_compare(hdr, hdr + i)) - return 0; - } - return 1; -} -static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) -{ - int i, k; - for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) - { - if (drmp3_hdr_valid(mp3)) - { - int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); - int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); - for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++) - { - if (drmp3_hdr_compare(mp3, mp3 + k)) - { - int fb = k - drmp3_hdr_padding(mp3); - int nextfb = fb + drmp3_hdr_padding(mp3 + k); - if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) - continue; - frame_and_padding = k; - frame_bytes = fb; - *free_format_bytes = fb; - } - } - if ((frame_bytes && i + frame_and_padding <= mp3_bytes && - drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || - (!i && frame_and_padding == mp3_bytes)) - { - *ptr_frame_bytes = frame_and_padding; - return i; - } - *free_format_bytes = 0; - } - } - *ptr_frame_bytes = 0; - return mp3_bytes; -} -DRMP3_API void drmp3dec_init(drmp3dec *dec) -{ - dec->header[0] = 0; -} -DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) -{ - int i = 0, igr, frame_size = 0, success = 1; - const drmp3_uint8 *hdr; - drmp3_bs bs_frame[1]; - drmp3dec_scratch scratch; - if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) - { - frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); - if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) - { - frame_size = 0; - } - } - if (!frame_size) - { - DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec)); - i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); - if (!frame_size || i + frame_size > mp3_bytes) - { - info->frame_bytes = i; - return 0; - } - } - hdr = mp3 + i; - DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE); - info->frame_bytes = i + frame_size; - info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; - info->hz = drmp3_hdr_sample_rate_hz(hdr); - info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); - info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); - drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); - if (DRMP3_HDR_IS_CRC(hdr)) - { - drmp3_bs_get_bits(bs_frame, 16); - } - if (info->layer == 3) - { - int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); - if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) - { - drmp3dec_init(dec); - return 0; - } - success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); - if (success && pcm != NULL) - { - for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) - { - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); - } - } - drmp3_L3_save_reservoir(dec, &scratch); - } else - { -#ifdef DR_MP3_ONLY_MP3 - return 0; -#else - drmp3_L12_scale_info sci[1]; - if (pcm == NULL) { - return drmp3_hdr_frame_samples(hdr); - } - drmp3_L12_read_scale_info(hdr, bs_frame, sci); - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - for (i = 0, igr = 0; igr < 3; igr++) - { - if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) - { - i = 0; - drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); - } - if (bs_frame->pos > bs_frame->limit) - { - drmp3dec_init(dec); - return 0; - } - } -#endif - } - return success*drmp3_hdr_frame_samples(dec->header); -} -DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples) -{ - size_t i = 0; -#if DRMP3_HAVE_SIMD - size_t aligned_count = num_samples & ~7; - for(; i < aligned_count; i+=8) - { - drmp3_f4 scale = DRMP3_VSET(32768.0f); - drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale); - drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale); -#if DRMP3_HAVE_SSE - drmp3_f4 s16max = DRMP3_VSET( 32767.0f); - drmp3_f4 s16min = DRMP3_VSET(-32768.0f); - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); - out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); - out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); - out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); - out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); - out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); - out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); - out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); - out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); -#else - int16x4_t pcma, pcmb; - a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); - b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); - vst1_lane_s16(out+i , pcma, 0); - vst1_lane_s16(out+i+1, pcma, 1); - vst1_lane_s16(out+i+2, pcma, 2); - vst1_lane_s16(out+i+3, pcma, 3); - vst1_lane_s16(out+i+4, pcmb, 0); - vst1_lane_s16(out+i+5, pcmb, 1); - vst1_lane_s16(out+i+6, pcmb, 2); - vst1_lane_s16(out+i+7, pcmb, 3); -#endif - } -#endif - for(; i < num_samples; i++) - { - float sample = in[i] * 32768.0f; - if (sample >= 32766.5) - out[i] = (drmp3_int16) 32767; - else if (sample <= -32767.5) - out[i] = (drmp3_int16)-32768; - else - { - short s = (drmp3_int16)(sample + .5f); - s -= (s < 0); - out[i] = s; - } - } -} -#if defined(SIZE_MAX) - #define DRMP3_SIZE_MAX SIZE_MAX -#else - #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) - #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) - #else - #define DRMP3_SIZE_MAX 0xFFFFFFFF - #endif -#endif -#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES -#define DRMP3_SEEK_LEADING_MP3_FRAMES 2 -#endif -#define DRMP3_MIN_DATA_CHUNK_SIZE 16384 -#ifndef DRMP3_DATA_CHUNK_SIZE -#define DRMP3_DATA_CHUNK_SIZE DRMP3_MIN_DATA_CHUNK_SIZE*4 -#endif -#define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) -#define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) -#ifndef DRMP3_PI_D -#define DRMP3_PI_D 3.14159265358979323846264 -#endif -#define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2 -static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a) -{ - return x*(1-a) + y*a; -} -static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a) -{ - float r0 = (y - x); - float r1 = r0*a; - return x + r1; -} -static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) -{ - for (;;) { - if (b == 0) { - break; - } else { - drmp3_uint32 t = a; - a = b; - b = t % a; - } - } - return a; -} -static void* drmp3__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return DRMP3_MALLOC(sz); -} -static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return DRMP3_REALLOC(p, sz); -} -static void drmp3__free_default(void* p, void* pUserData) -{ - (void)pUserData; - DRMP3_FREE(p); -} -static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - DRMP3_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return *pAllocationCallbacks; - } else { - drmp3_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = drmp3__malloc_default; - allocationCallbacks.onRealloc = drmp3__realloc_default; - allocationCallbacks.onFree = drmp3__free_default; - return allocationCallbacks; - } -} -static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) -{ - size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); - pMP3->streamCursor += bytesRead; - return bytesRead; -} -static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) -{ - DRMP3_ASSERT(offset >= 0); - if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { - return DRMP3_FALSE; - } - if (origin == drmp3_seek_origin_start) { - pMP3->streamCursor = (drmp3_uint64)offset; - } else { - pMP3->streamCursor += offset; - } - return DRMP3_TRUE; -} -static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin) -{ - if (offset <= 0x7FFFFFFF) { - return drmp3__on_seek(pMP3, (int)offset, origin); - } - if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) { - return DRMP3_FALSE; - } - offset -= 0x7FFFFFFF; - while (offset > 0) { - if (offset <= 0x7FFFFFFF) { - if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) { - return DRMP3_FALSE; - } - offset = 0; - } else { - if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) { - return DRMP3_FALSE; - } - offset -= 0x7FFFFFFF; - } - } - return DRMP3_TRUE; -} -static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) -{ - drmp3_uint32 pcmFramesRead = 0; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onRead != NULL); - if (pMP3->atEnd) { - return 0; - } - for (;;) { - drmp3dec_frame_info info; - if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) { - size_t bytesRead; - if (pMP3->pData != NULL) { - DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); - } - pMP3->dataConsumed = 0; - if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { - drmp3_uint8* pNewData; - size_t newDataCap; - newDataCap = DRMP3_DATA_CHUNK_SIZE; - pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - pMP3->pData = pNewData; - pMP3->dataCapacity = newDataCap; - } - bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) { - if (pMP3->dataSize == 0) { - pMP3->atEnd = DRMP3_TRUE; - return 0; - } - } - pMP3->dataSize += bytesRead; - } - if (pMP3->dataSize > INT_MAX) { - pMP3->atEnd = DRMP3_TRUE; - return 0; - } - DRMP3_ASSERT(pMP3->pData != NULL); - DRMP3_ASSERT(pMP3->dataCapacity > 0); - pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); - if (info.frame_bytes > 0) { - pMP3->dataConsumed += (size_t)info.frame_bytes; - pMP3->dataSize -= (size_t)info.frame_bytes; - } - if (pcmFramesRead > 0) { - pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; - pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; - break; - } else if (info.frame_bytes == 0) { - size_t bytesRead; - DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); - pMP3->dataConsumed = 0; - if (pMP3->dataCapacity == pMP3->dataSize) { - drmp3_uint8* pNewData; - size_t newDataCap; - newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE; - pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - pMP3->pData = pNewData; - pMP3->dataCapacity = newDataCap; - } - bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) { - pMP3->atEnd = DRMP3_TRUE; - return 0; - } - pMP3->dataSize += bytesRead; - } - }; - return pcmFramesRead; -} -static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) -{ - drmp3_uint32 pcmFramesRead = 0; - drmp3dec_frame_info info; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->memory.pData != NULL); - if (pMP3->atEnd) { - return 0; - } - for (;;) { - pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); - if (pcmFramesRead > 0) { - pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; - pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; - break; - } else if (info.frame_bytes > 0) { - pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - } else { - break; - } - } - pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - return pcmFramesRead; -} -static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) -{ - if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); - } else { - return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); - } -} -static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3) -{ - DRMP3_ASSERT(pMP3 != NULL); - return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames); -} -#if 0 -static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) -{ - drmp3_uint32 pcmFrameCount; - DRMP3_ASSERT(pMP3 != NULL); - pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFrameCount == 0) { - return 0; - } - pMP3->currentPCMFrame += pcmFrameCount; - pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; - pMP3->pcmFramesRemainingInMP3Frame = 0; - return pcmFrameCount; -} -#endif -static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(onRead != NULL); - drmp3dec_init(&pMP3->decoder); - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->pUserData = pUserData; - pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { - return DRMP3_FALSE; - } - if (drmp3_decode_next_frame(pMP3) == 0) { - drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); - return DRMP3_FALSE; - } - pMP3->channels = pMP3->mp3FrameChannels; - pMP3->sampleRate = pMP3->mp3FrameSampleRate; - return DRMP3_TRUE; -} -DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL || onRead == NULL) { - return DRMP3_FALSE; - } - DRMP3_ZERO_OBJECT(pMP3); - return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); -} -static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - drmp3* pMP3 = (drmp3*)pUserData; - size_t bytesRemaining; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); - bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); - pMP3->memory.currentReadPos += bytesToRead; - } - return bytesToRead; -} -static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) -{ - drmp3* pMP3 = (drmp3*)pUserData; - DRMP3_ASSERT(pMP3 != NULL); - if (origin == drmp3_seek_origin_current) { - if (byteOffset > 0) { - if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { - byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); - } - } else { - if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(int)pMP3->memory.currentReadPos; - } - } - pMP3->memory.currentReadPos += byteOffset; - } else { - if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) { - pMP3->memory.currentReadPos = byteOffset; - } else { - pMP3->memory.currentReadPos = pMP3->memory.dataSize; - } - } - return DRMP3_TRUE; -} -DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return DRMP3_FALSE; - } - DRMP3_ZERO_OBJECT(pMP3); - if (pData == NULL || dataSize == 0) { - return DRMP3_FALSE; - } - pMP3->memory.pData = (const drmp3_uint8*)pData; - pMP3->memory.dataSize = dataSize; - pMP3->memory.currentReadPos = 0; - return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks); -} -#ifndef DR_MP3_NO_STDIO -#include -#include -#include -static drmp3_result drmp3_result_from_errno(int e) -{ - switch (e) - { - case 0: return DRMP3_SUCCESS; - #ifdef EPERM - case EPERM: return DRMP3_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return DRMP3_INTERRUPT; - #endif - #ifdef EIO - case EIO: return DRMP3_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return DRMP3_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return DRMP3_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return DRMP3_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return DRMP3_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return DRMP3_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return DRMP3_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return DRMP3_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return DRMP3_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return DRMP3_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return DRMP3_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return DRMP3_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return DRMP3_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return DRMP3_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return DRMP3_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return DRMP3_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return DRMP3_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return DRMP3_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return DRMP3_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return DRMP3_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return DRMP3_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return DRMP3_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return DRMP3_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return DRMP3_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return DRMP3_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return DRMP3_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return DRMP3_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return DRMP3_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return DRMP3_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return DRMP3_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return DRMP3_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return DRMP3_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return DRMP3_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return DRMP3_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return DRMP3_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return DRMP3_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return DRMP3_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return DRMP3_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return DRMP3_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return DRMP3_ERROR; - #endif - #ifdef EBADE - case EBADE: return DRMP3_ERROR; - #endif - #ifdef EBADR - case EBADR: return DRMP3_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return DRMP3_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return DRMP3_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return DRMP3_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return DRMP3_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return DRMP3_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return DRMP3_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return DRMP3_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return DRMP3_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return DRMP3_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return DRMP3_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return DRMP3_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return DRMP3_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return DRMP3_ERROR; - #endif - #ifdef EADV - case EADV: return DRMP3_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return DRMP3_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return DRMP3_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return DRMP3_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return DRMP3_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return DRMP3_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return DRMP3_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return DRMP3_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return DRMP3_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return DRMP3_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return DRMP3_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return DRMP3_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return DRMP3_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return DRMP3_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return DRMP3_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return DRMP3_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return DRMP3_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return DRMP3_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return DRMP3_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return DRMP3_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return DRMP3_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return DRMP3_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return DRMP3_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return DRMP3_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return DRMP3_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return DRMP3_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return DRMP3_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return DRMP3_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return DRMP3_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return DRMP3_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return DRMP3_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return DRMP3_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return DRMP3_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return DRMP3_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return DRMP3_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return DRMP3_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return DRMP3_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return DRMP3_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return DRMP3_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return DRMP3_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return DRMP3_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return DRMP3_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return DRMP3_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return DRMP3_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return DRMP3_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return DRMP3_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return DRMP3_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return DRMP3_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return DRMP3_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return DRMP3_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return DRMP3_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return DRMP3_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return DRMP3_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return DRMP3_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return DRMP3_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return DRMP3_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return DRMP3_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return DRMP3_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return DRMP3_ERROR; - #endif - default: return DRMP3_ERROR; - } -} -static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRMP3_INVALID_ARGS; - } -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drmp3_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - drmp3_result result = drmp3_result_from_errno(errno); - if (result == DRMP3_SUCCESS) { - result = DRMP3_ERROR; - } - return result; - } -#endif - return DRMP3_SUCCESS; -} -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define DRMP3_HAS_WFOPEN - #endif -#endif -static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRMP3_INVALID_ARGS; - } -#if defined(DRMP3_HAS_WFOPEN) - { - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drmp3_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return drmp3_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - DRMP3_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return drmp3_result_from_errno(errno); - } - pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return DRMP3_OUT_OF_MEMORY; - } - pFilePathTemp = pFilePath; - DRMP3_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - *ppFile = fopen(pFilePathMB, pOpenModeMB); - drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); - } - if (*ppFile == NULL) { - return DRMP3_ERROR; - } -#endif - return DRMP3_SUCCESS; -} -static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} -static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - drmp3_bool32 result; - FILE* pFile; - if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { - return DRMP3_FALSE; - } - result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRMP3_TRUE) { - fclose(pFile); - return result; - } - return DRMP3_TRUE; -} -DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - drmp3_bool32 result; - FILE* pFile; - if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { - return DRMP3_FALSE; - } - result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRMP3_TRUE) { - fclose(pFile); - return result; - } - return DRMP3_TRUE; -} -#endif -DRMP3_API void drmp3_uninit(drmp3* pMP3) -{ - if (pMP3 == NULL) { - return; - } -#ifndef DR_MP3_NO_STDIO - if (pMP3->onRead == drmp3__on_read_stdio) { - FILE* pFile = (FILE*)pMP3->pUserData; - if (pFile != NULL) { - fclose(pFile); - pMP3->pUserData = NULL; - } - } -#endif - drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); -} -#if defined(DR_MP3_FLOAT_OUTPUT) -static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount) -{ - drmp3_uint64 i; - drmp3_uint64 i4; - drmp3_uint64 sampleCount4; - i = 0; - sampleCount4 = sampleCount >> 2; - for (i4 = 0; i4 < sampleCount4; i4 += 1) { - float x0 = src[i+0]; - float x1 = src[i+1]; - float x2 = src[i+2]; - float x3 = src[i+3]; - x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); - x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); - x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); - x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); - x0 = x0 * 32767.0f; - x1 = x1 * 32767.0f; - x2 = x2 * 32767.0f; - x3 = x3 * 32767.0f; - dst[i+0] = (drmp3_int16)x0; - dst[i+1] = (drmp3_int16)x1; - dst[i+2] = (drmp3_int16)x2; - dst[i+3] = (drmp3_int16)x3; - i += 4; - } - for (; i < sampleCount; i += 1) { - float x = src[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - x = x * 32767.0f; - dst[i] = (drmp3_int16)x; - } -} -#endif -#if !defined(DR_MP3_FLOAT_OUTPUT) -static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount) -{ - drmp3_uint64 i; - for (i = 0; i < sampleCount; i += 1) { - float x = (float)src[i]; - x = x * 0.000030517578125f; - dst[i] = x; - } -} -#endif -static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut) -{ - drmp3_uint64 totalFramesRead = 0; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onRead != NULL); - while (framesToRead > 0) { - drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); - if (pBufferOut != NULL) { - #if defined(DR_MP3_FLOAT_OUTPUT) - float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); - float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); - #else - drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels); - drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels); - #endif - } - pMP3->currentPCMFrame += framesToConsume; - pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; - pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; - totalFramesRead += framesToConsume; - framesToRead -= framesToConsume; - if (framesToRead == 0) { - break; - } - DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); - if (drmp3_decode_next_frame(pMP3) == 0) { - break; - } - } - return totalFramesRead; -} -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) -{ - if (pMP3 == NULL || pMP3->onRead == NULL) { - return 0; - } -#if defined(DR_MP3_FLOAT_OUTPUT) - return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); -#else - { - drmp3_int16 pTempS16[8192]; - drmp3_uint64 totalPCMFramesRead = 0; - while (totalPCMFramesRead < framesToRead) { - drmp3_uint64 framesJustRead; - drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels; - if (framesToReadNow > framesRemaining) { - framesToReadNow = framesRemaining; - } - framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); - if (framesJustRead == 0) { - break; - } - drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); - totalPCMFramesRead += framesJustRead; - } - return totalPCMFramesRead; - } -#endif -} -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut) -{ - if (pMP3 == NULL || pMP3->onRead == NULL) { - return 0; - } -#if !defined(DR_MP3_FLOAT_OUTPUT) - return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); -#else - { - float pTempF32[4096]; - drmp3_uint64 totalPCMFramesRead = 0; - while (totalPCMFramesRead < framesToRead) { - drmp3_uint64 framesJustRead; - drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels; - if (framesToReadNow > framesRemaining) { - framesToReadNow = framesRemaining; - } - framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); - if (framesJustRead == 0) { - break; - } - drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); - totalPCMFramesRead += framesJustRead; - } - return totalPCMFramesRead; - } -#endif -} -static void drmp3_reset(drmp3* pMP3) -{ - DRMP3_ASSERT(pMP3 != NULL); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = 0; - pMP3->currentPCMFrame = 0; - pMP3->dataSize = 0; - pMP3->atEnd = DRMP3_FALSE; - drmp3dec_init(&pMP3->decoder); -} -static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) -{ - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onSeek != NULL); - if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) { - return DRMP3_FALSE; - } - drmp3_reset(pMP3); - return DRMP3_TRUE; -} -static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset) -{ - drmp3_uint64 framesRead; -#if defined(DR_MP3_FLOAT_OUTPUT) - framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); -#else - framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); -#endif - if (framesRead != frameOffset) { - return DRMP3_FALSE; - } - return DRMP3_TRUE; -} -static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) -{ - DRMP3_ASSERT(pMP3 != NULL); - if (frameIndex == pMP3->currentPCMFrame) { - return DRMP3_TRUE; - } - if (frameIndex < pMP3->currentPCMFrame) { - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; - } - } - DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); - return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); -} -static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex) -{ - drmp3_uint32 iSeekPoint; - DRMP3_ASSERT(pSeekPointIndex != NULL); - *pSeekPointIndex = 0; - if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { - return DRMP3_FALSE; - } - for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { - if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { - break; - } - *pSeekPointIndex = iSeekPoint; - } - return DRMP3_TRUE; -} -static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) -{ - drmp3_seek_point seekPoint; - drmp3_uint32 priorSeekPointIndex; - drmp3_uint16 iMP3Frame; - drmp3_uint64 leftoverFrames; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->pSeekPoints != NULL); - DRMP3_ASSERT(pMP3->seekPointCount > 0); - if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { - seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; - } else { - seekPoint.seekPosInBytes = 0; - seekPoint.pcmFrameIndex = 0; - seekPoint.mp3FramesToDiscard = 0; - seekPoint.pcmFramesToDiscard = 0; - } - if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) { - return DRMP3_FALSE; - } - drmp3_reset(pMP3); - for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { - drmp3_uint32 pcmFramesRead; - drmp3d_sample_t* pPCMFrames; - pPCMFrames = NULL; - if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { - pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames; - } - pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames); - if (pcmFramesRead == 0) { - return DRMP3_FALSE; - } - } - pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; - leftoverFrames = frameIndex - pMP3->currentPCMFrame; - return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); -} -DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) -{ - if (pMP3 == NULL || pMP3->onSeek == NULL) { - return DRMP3_FALSE; - } - if (frameIndex == 0) { - return drmp3_seek_to_start_of_stream(pMP3); - } - if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { - return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); - } else { - return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); - } -} -DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount) -{ - drmp3_uint64 currentPCMFrame; - drmp3_uint64 totalPCMFrameCount; - drmp3_uint64 totalMP3FrameCount; - if (pMP3 == NULL) { - return DRMP3_FALSE; - } - if (pMP3->onSeek == NULL) { - return DRMP3_FALSE; - } - currentPCMFrame = pMP3->currentPCMFrame; - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; - } - totalPCMFrameCount = 0; - totalMP3FrameCount = 0; - for (;;) { - drmp3_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3Frame == 0) { - break; - } - totalPCMFrameCount += pcmFramesInCurrentMP3Frame; - totalMP3FrameCount += 1; - } - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; - } - if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return DRMP3_FALSE; - } - if (pMP3FrameCount != NULL) { - *pMP3FrameCount = totalMP3FrameCount; - } - if (pPCMFrameCount != NULL) { - *pPCMFrameCount = totalPCMFrameCount; - } - return DRMP3_TRUE; -} -DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) -{ - drmp3_uint64 totalPCMFrameCount; - if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { - return 0; - } - return totalPCMFrameCount; -} -DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) -{ - drmp3_uint64 totalMP3FrameCount; - if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { - return 0; - } - return totalMP3FrameCount; -} -static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) -{ - float srcRatio; - float pcmFrameCountOutF; - drmp3_uint32 pcmFrameCountOut; - srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; - DRMP3_ASSERT(srcRatio > 0); - pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); - pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF; - *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; - *pRunningPCMFrameCount += pcmFrameCountOut; -} -typedef struct -{ - drmp3_uint64 bytePos; - drmp3_uint64 pcmFrameIndex; -} drmp3__seeking_mp3_frame_info; -DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints) -{ - drmp3_uint32 seekPointCount; - drmp3_uint64 currentPCMFrame; - drmp3_uint64 totalMP3FrameCount; - drmp3_uint64 totalPCMFrameCount; - if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { - return DRMP3_FALSE; - } - seekPointCount = *pSeekPointCount; - if (seekPointCount == 0) { - return DRMP3_FALSE; - } - currentPCMFrame = pMP3->currentPCMFrame; - if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { - return DRMP3_FALSE; - } - if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) { - seekPointCount = 1; - pSeekPoints[0].seekPosInBytes = 0; - pSeekPoints[0].pcmFrameIndex = 0; - pSeekPoints[0].mp3FramesToDiscard = 0; - pSeekPoints[0].pcmFramesToDiscard = 0; - } else { - drmp3_uint64 pcmFramesBetweenSeekPoints; - drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1]; - drmp3_uint64 runningPCMFrameCount = 0; - float runningPCMFrameCountFractionalPart = 0; - drmp3_uint64 nextTargetPCMFrame; - drmp3_uint32 iMP3Frame; - drmp3_uint32 iSeekPoint; - if (seekPointCount > totalMP3FrameCount-1) { - seekPointCount = (drmp3_uint32)totalMP3FrameCount-1; - } - pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; - } - for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { - drmp3_uint32 pcmFramesInCurrentMP3FrameIn; - DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); - mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3FrameIn == 0) { - return DRMP3_FALSE; - } - drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); - } - nextTargetPCMFrame = 0; - for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { - nextTargetPCMFrame += pcmFramesBetweenSeekPoints; - for (;;) { - if (nextTargetPCMFrame < runningPCMFrameCount) { - pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; - pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); - break; - } else { - size_t i; - drmp3_uint32 pcmFramesInCurrentMP3FrameIn; - for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) { - mp3FrameInfo[i] = mp3FrameInfo[i+1]; - } - mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3FrameIn == 0) { - pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; - pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); - break; - } - drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); - } - } - } - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; - } - if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return DRMP3_FALSE; - } - } - *pSeekPointCount = seekPointCount; - return DRMP3_TRUE; -} -DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints) -{ - if (pMP3 == NULL) { - return DRMP3_FALSE; - } - if (seekPointCount == 0 || pSeekPoints == NULL) { - pMP3->seekPointCount = 0; - pMP3->pSeekPoints = NULL; - } else { - pMP3->seekPointCount = seekPointCount; - pMP3->pSeekPoints = pSeekPoints; - } - return DRMP3_TRUE; -} -static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) -{ - drmp3_uint64 totalFramesRead = 0; - drmp3_uint64 framesCapacity = 0; - float* pFrames = NULL; - float temp[4096]; - DRMP3_ASSERT(pMP3 != NULL); - for (;;) { - drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; - drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); - if (framesJustRead == 0) { - break; - } - if (framesCapacity < totalFramesRead + framesJustRead) { - drmp3_uint64 oldFramesBufferSize; - drmp3_uint64 newFramesBufferSize; - drmp3_uint64 newFramesCap; - float* pNewFrames; - newFramesCap = framesCapacity * 2; - if (newFramesCap < totalFramesRead + framesJustRead) { - newFramesCap = totalFramesRead + framesJustRead; - } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); - if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { - break; - } - pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); - if (pNewFrames == NULL) { - drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); - break; - } - pFrames = pNewFrames; - framesCapacity = newFramesCap; - } - DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); - totalFramesRead += framesJustRead; - if (framesJustRead != framesToReadRightNow) { - break; - } - } - if (pConfig != NULL) { - pConfig->channels = pMP3->channels; - pConfig->sampleRate = pMP3->sampleRate; - } - drmp3_uninit(pMP3); - if (pTotalFrameCount) { - *pTotalFrameCount = totalFramesRead; - } - return pFrames; -} -static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) -{ - drmp3_uint64 totalFramesRead = 0; - drmp3_uint64 framesCapacity = 0; - drmp3_int16* pFrames = NULL; - drmp3_int16 temp[4096]; - DRMP3_ASSERT(pMP3 != NULL); - for (;;) { - drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; - drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); - if (framesJustRead == 0) { - break; - } - if (framesCapacity < totalFramesRead + framesJustRead) { - drmp3_uint64 newFramesBufferSize; - drmp3_uint64 oldFramesBufferSize; - drmp3_uint64 newFramesCap; - drmp3_int16* pNewFrames; - newFramesCap = framesCapacity * 2; - if (newFramesCap < totalFramesRead + framesJustRead) { - newFramesCap = totalFramesRead + framesJustRead; - } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16); - if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { - break; - } - pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); - if (pNewFrames == NULL) { - drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); - break; - } - pFrames = pNewFrames; - framesCapacity = newFramesCap; - } - DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16))); - totalFramesRead += framesJustRead; - if (framesJustRead != framesToReadRightNow) { - break; - } - } - if (pConfig != NULL) { - pConfig->channels = pMP3->channels; - pConfig->sampleRate = pMP3->sampleRate; - } - drmp3_uninit(pMP3); - if (pTotalFrameCount) { - *pTotalFrameCount = totalFramesRead; - } - return pFrames; -} -DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - drmp3 mp3; - if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - drmp3 mp3; - if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - drmp3 mp3; - if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { - return NULL; - } - return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - drmp3 mp3; - if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { - return NULL; - } - return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -#ifndef DR_MP3_NO_STDIO -DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - drmp3 mp3; - if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { - return NULL; - } - return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - drmp3 mp3; - if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { - return NULL; - } - return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -#endif -DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks); - } else { - return drmp3__malloc_default(sz, NULL); - } -} -DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - drmp3__free_from_callbacks(p, pAllocationCallbacks); - } else { - drmp3__free_default(p, NULL); - } -} -#endif -/* dr_mp3_c end */ -#endif /* DRMP3_IMPLEMENTATION */ -#endif /* MA_NO_MP3 */ - - -/* End globally disabled warnings. */ -#if defined(_MSC_VER) - #pragma warning(pop) -#endif - -#endif /* miniaudio_c */ -#endif /* MINIAUDIO_IMPLEMENTATION */ - - -/* -This software is available as a choice of the following licenses. Choose -whichever you prefer. - -=============================================================================== -ALTERNATIVE 1 - Public Domain (www.unlicense.org) -=============================================================================== -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. - -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to - -=============================================================================== -ALTERNATIVE 2 - MIT No Attribution -=============================================================================== -Copyright 2020 David Reid - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ diff --git a/libs/zaudio/libs/miniaudio/zaudio.c b/libs/zaudio/libs/miniaudio/zaudio.c deleted file mode 100644 index 539270c06..000000000 --- a/libs/zaudio/libs/miniaudio/zaudio.c +++ /dev/null @@ -1,661 +0,0 @@ -#include "miniaudio.h" -#include -#include -//-------------------------------------------------------------------------------------------------- -static void* defaultAlloc(size_t size, void* user_data) { - return malloc(size); -} -static void* defaulRealloc(void* ptr, size_t size, void* user_data) { - return realloc(ptr, size); -} -static void defaultFree(void* ptr, void* user_data) { - return free(ptr); -} - -static ma_allocation_callbacks s_mem = { - .pUserData = NULL, - .onMalloc = defaultAlloc, - .onRealloc = defaulRealloc, - .onFree = defaultFree, -}; -//-------------------------------------------------------------------------------------------------- -void zaudioNoiseConfigInit( - ma_format format, - ma_uint32 channels, - ma_noise_type type, - ma_int32 seed, - double amplitude, - ma_noise_config* out_config -) { - assert(out_config != NULL); - *out_config = ma_noise_config_init(format, channels, type, seed, amplitude); -} - -ma_result zaudioNoiseCreate(const ma_noise_config* config, ma_noise** out_handle) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_noise), s_mem.pUserData); - ma_result res = ma_noise_init(config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioNoiseDestroy(ma_noise* handle) { - assert(handle != NULL); - ma_noise_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioNodeConfigInit(ma_node_config* out_config) { - assert(out_config != NULL); - *out_config = ma_node_config_init(); -} -//-------------------------------------------------------------------------------------------------- -void zaudioDataSourceConfigInit(ma_data_source_config* out_config) { - assert(out_config != NULL); - *out_config = ma_data_source_config_init(); -} - -ma_result zaudioDataSourceCreate(const ma_data_source_config* config, ma_data_source** out_handle) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_data_source), s_mem.pUserData); - ma_result res = ma_data_source_init(config, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioDataSourceDestroy(ma_data_source* handle) { - assert(handle != NULL); - ma_data_source_uninit(handle); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioWaveformConfigInit( - ma_format format, - ma_uint32 channels, - ma_uint32 sampleRate, - ma_waveform_type type, - double amplitude, - double frequency, - ma_waveform_config* out_config -) { - assert(out_config != NULL); - *out_config = ma_waveform_config_init(format, channels, sampleRate, type, amplitude, frequency); -} - -ma_result zaudioWaveformCreate(const ma_waveform_config* config, ma_waveform** out_handle) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_waveform), s_mem.pUserData); - ma_result res = ma_waveform_init(config, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioWaveformDestroy(ma_waveform* handle) { - assert(handle != NULL); - ma_waveform_uninit(handle); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioDataSourceNodeConfigInit(ma_data_source* ds, ma_data_source_node_config* out_config) { - assert(out_config != NULL); - *out_config = ma_data_source_node_config_init(ds); -} - -ma_result zaudioDataSourceNodeCreate( - ma_node_graph* node_graph, - const ma_data_source_node_config* config, - ma_data_source** out_handle -) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_data_source_node), s_mem.pUserData); - ma_result res = ma_data_source_node_init(node_graph, config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioDataSourceNodeDestroy(ma_data_source_node* handle) { - assert(handle != NULL); - ma_data_source_node_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioSplitterNodeConfigInit(ma_uint32 channels, ma_splitter_node_config* out_config) { - assert(out_config != NULL); - *out_config = ma_splitter_node_config_init(channels); -} - -ma_result zaudioSplitterNodeCreate( - ma_node_graph* node_graph, - const ma_splitter_node_config* config, - ma_splitter_node** out_handle -) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_splitter_node), s_mem.pUserData); - ma_result res = ma_splitter_node_init(node_graph, config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioSplitterNodeDestroy(ma_splitter_node* handle) { - assert(handle != NULL); - ma_splitter_node_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioBiquadNodeConfigInit( - ma_uint32 channels, - float b0, - float b1, - float b2, - float a0, - float a1, - float a2, - ma_biquad_node_config* out_config -) { - assert(out_config != NULL); - *out_config = ma_biquad_node_config_init(channels, b0, b1, b2, a0, a1, a2); -} - -ma_result zaudioBiquadNodeCreate( - ma_node_graph* node_graph, - const ma_biquad_node_config* config, - ma_biquad_node** out_handle -) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_biquad_node), s_mem.pUserData); - ma_result res = ma_biquad_node_init(node_graph, config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioBiquadNodeDestroy(ma_biquad_node* handle) { - assert(handle != NULL); - ma_biquad_node_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioLpfNodeConfigInit( - ma_uint32 channels, - ma_uint32 sample_rate, - double cutoff_frequency, - ma_uint32 order, - ma_lpf_node_config* out_config -) { - assert(out_config != NULL); - *out_config = ma_lpf_node_config_init(channels, sample_rate, cutoff_frequency, order); -} - -ma_result zaudioLpfNodeCreate( - ma_node_graph* node_graph, - const ma_lpf_node_config* config, - ma_lpf_node** out_handle -) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_lpf_node), s_mem.pUserData); - ma_result res = ma_lpf_node_init(node_graph, config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioLpfNodeDestroy(ma_lpf_node* handle) { - assert(handle != NULL); - ma_lpf_node_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioHpfNodeConfigInit( - ma_uint32 channels, - ma_uint32 sample_rate, - double cutoff_frequency, - ma_uint32 order, - ma_hpf_node_config* out_config -) { - assert(out_config != NULL); - *out_config = ma_hpf_node_config_init(channels, sample_rate, cutoff_frequency, order); -} - -ma_result zaudioHpfNodeCreate( - ma_node_graph* node_graph, - const ma_hpf_node_config* config, - ma_hpf_node** out_handle -) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_hpf_node), s_mem.pUserData); - ma_result res = ma_hpf_node_init(node_graph, config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioHpfNodeDestroy(ma_hpf_node* handle) { - assert(handle != NULL); - ma_hpf_node_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioNotchNodeConfigInit( - ma_uint32 channels, - ma_uint32 sample_rate, - double q, - double frequency, - ma_notch_node_config* out_config -) { - assert(out_config != NULL); - *out_config = ma_notch_node_config_init(channels, sample_rate, q, frequency); -} - -ma_result zaudioNotchNodeCreate( - ma_node_graph* node_graph, - const ma_notch_node_config* config, - ma_notch_node** out_handle -) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_notch_node), s_mem.pUserData); - ma_result res = ma_notch_node_init(node_graph, config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioNotchNodeDestroy(ma_notch_node* handle) { - assert(handle != NULL); - ma_notch_node_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioPeakNodeConfigInit( - ma_uint32 channels, - ma_uint32 sample_rate, - double gain_db, - double q, - double frequency, - ma_peak_node_config* out_config -) { - assert(out_config != NULL); - *out_config = ma_peak_node_config_init(channels, sample_rate, gain_db, q, frequency); -} - -ma_result zaudioPeakNodeCreate( - ma_node_graph* node_graph, - const ma_peak_node_config* config, - ma_peak_node** out_handle -) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_peak_node), s_mem.pUserData); - ma_result res = ma_peak_node_init(node_graph, config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioPeakNodeDestroy(ma_peak_node* handle) { - assert(handle != NULL); - ma_peak_node_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioLoshelfNodeConfigInit( - ma_uint32 channels, - ma_uint32 sample_rate, - double gain_db, - double shelf_slope, - double frequency, - ma_loshelf_node_config* out_config -) { - assert(out_config != NULL); - *out_config = ma_loshelf_node_config_init(channels, sample_rate, gain_db, shelf_slope, frequency); -} - -ma_result zaudioLoshelfNodeCreate( - ma_node_graph* node_graph, - const ma_loshelf_node_config* config, - ma_loshelf_node** out_handle -) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_loshelf_node), s_mem.pUserData); - ma_result res = ma_loshelf_node_init(node_graph, config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioLoshelfNodeDestroy(ma_loshelf_node* handle) { - assert(handle != NULL); - ma_loshelf_node_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioHishelfNodeConfigInit( - ma_uint32 channels, - ma_uint32 sample_rate, - double gain_db, - double shelf_slope, - double frequency, - ma_hishelf_node_config* out_config -) { - assert(out_config != NULL); - *out_config = ma_hishelf_node_config_init(channels, sample_rate, gain_db, shelf_slope, frequency); -} - -ma_result zaudioHishelfNodeCreate( - ma_node_graph* node_graph, - const ma_hishelf_node_config* config, - ma_hishelf_node** out_handle -) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_hishelf_node), s_mem.pUserData); - ma_result res = ma_hishelf_node_init(node_graph, config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioHishelfNodeDestroy(ma_hishelf_node* handle) { - assert(handle != NULL); - ma_hishelf_node_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioDelayNodeConfigInit( - ma_uint32 channels, - ma_uint32 sample_rate, - ma_uint32 delay_in_frames, - float decay, - ma_delay_node_config* out_config -) { - assert(out_config != NULL); - *out_config = ma_delay_node_config_init(channels, sample_rate, delay_in_frames, decay); -} - -ma_result zaudioDelayNodeCreate( - ma_node_graph* node_graph, - const ma_delay_node_config* config, - ma_delay_node** out_handle -) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_delay_node), s_mem.pUserData); - ma_result res = ma_delay_node_init(node_graph, config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioDelayNodeDestroy(ma_delay_node* handle) { - assert(handle != NULL); - ma_delay_node_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioNodeGraphConfigInit(ma_uint32 channels, ma_node_graph_config* out_config) { - assert(out_config != NULL); - *out_config = ma_node_graph_config_init(channels); -} - -ma_result zaudioNodeGraphCreate(const ma_node_graph_config* config, ma_node_graph** out_handle) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_node_graph), s_mem.pUserData); - ma_result res = ma_node_graph_init(config, &s_mem, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioNodeGraphDestroy(ma_node_graph* handle) { - assert(handle != NULL); - ma_node_graph_uninit(handle, &s_mem); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioDeviceConfigInit(ma_device_type device_type, ma_device_config* out_config) { - assert(out_config != NULL); - *out_config = ma_device_config_init(device_type); -} - -ma_result zaudioDeviceCreate(ma_context* context, const ma_device_config* config, ma_device** out_handle) { - assert(config != NULL && out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_device), s_mem.pUserData); - ma_result res = ma_device_init(context, config, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioDeviceDestroy(ma_device* handle) { - assert(handle != NULL); - ma_device_uninit(handle); - s_mem.onFree(handle, s_mem.pUserData); -} - -void* zaudioDeviceGetUserData(ma_device* handle) { - assert(handle != NULL); - return handle->pUserData; -} -//-------------------------------------------------------------------------------------------------- -void zaudioEngineConfigInit(ma_engine_config* out_config) { - assert(out_config != NULL); - *out_config = ma_engine_config_init(); - out_config->allocationCallbacks = s_mem; -} - -ma_result zaudioEngineCreate(const ma_engine_config* config, ma_engine** out_handle) { - assert(out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_engine), s_mem.pUserData); - ma_result res = ma_engine_init(config, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioEngineDestroy(ma_engine* handle) { - assert(handle != NULL); - ma_engine_uninit(handle); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -void zaudioSoundConfigInit(ma_sound_config* out_config) { - assert(out_config != NULL); - *out_config = ma_sound_config_init(); -} - -ma_result zaudioSoundCreate(ma_engine* engine, const ma_sound_config* config, ma_sound** out_handle) { - assert(out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_sound), s_mem.pUserData); - ma_result res = ma_sound_init_ex(engine, config, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -ma_result zaudioSoundCreateFromFile( - ma_engine* engine, - const char* file_path, - ma_uint32 flags, - ma_sound_group* sgroup, - ma_fence* done_fence, - ma_sound** out_handle -) { - assert(out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_sound), s_mem.pUserData); - ma_result res = ma_sound_init_from_file(engine, file_path, flags, sgroup, done_fence, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -ma_result zaudioSoundCreateFromDataSource( - ma_engine* engine, - ma_data_source* data_source, - ma_uint32 flags, - ma_sound_group* sgroup, - ma_sound** out_handle -) { - assert(out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_sound), s_mem.pUserData); - ma_result res = ma_sound_init_from_data_source(engine, data_source, flags, sgroup, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -ma_result zaudioSoundCreateCopy( - ma_engine* engine, - ma_sound* existing_sound, - ma_uint32 flags, - ma_sound_group* sgroup, - ma_sound** out_handle -) { - assert(out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_sound), s_mem.pUserData); - ma_result res = ma_sound_init_copy(engine, existing_sound, flags, sgroup, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioSoundDestroy(ma_sound* handle) { - assert(handle != NULL); - ma_sound_uninit(handle); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -ma_result zaudioSoundGroupCreate( - ma_engine* engine, - ma_uint32 flags, - ma_sound_group* parent, - ma_sound_group** out_handle -) { - assert(out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_sound_group), s_mem.pUserData); - ma_result res = ma_sound_group_init(engine, flags, parent, *out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioSoundGroupDestroy(ma_sound_group* handle) { - assert(handle != NULL); - ma_sound_group_uninit(handle); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -ma_result zaudioFenceCreate(ma_sound_group** out_handle) { - assert(out_handle != NULL); - *out_handle = s_mem.onMalloc(sizeof(ma_fence), s_mem.pUserData); - ma_result res = ma_fence_init(*out_handle); - if (res != MA_SUCCESS) { - s_mem.onFree(*out_handle, s_mem.pUserData); - *out_handle = NULL; - } - return res; -} - -void zaudioFenceDestroy(ma_fence* handle) { - assert(handle != NULL); - ma_fence_uninit(handle); - s_mem.onFree(handle, s_mem.pUserData); -} -//-------------------------------------------------------------------------------------------------- -// -// C ABI workarounds -// -//-------------------------------------------------------------------------------------------------- -// ma_engine -void WA_ma_engine_listener_get_position(const ma_engine* engine, ma_uint32 index, ma_vec3f* vout) { - *vout = ma_engine_listener_get_position(engine, index); -} - -void WA_ma_engine_listener_get_direction(const ma_engine* engine, ma_uint32 index, ma_vec3f* vout) { - *vout = ma_engine_listener_get_direction(engine, index); -} - -void WA_ma_engine_listener_get_velocity(const ma_engine* engine, ma_uint32 index, ma_vec3f* vout) { - *vout = ma_engine_listener_get_velocity(engine, index); -} - -void WA_ma_engine_listener_get_world_up(const ma_engine* engine, ma_uint32 index, ma_vec3f* vout) { - *vout = ma_engine_listener_get_world_up(engine, index); -} - -// ma_sound_group -void WA_ma_sound_group_get_direction_to_listener(const ma_sound_group* sgroup, ma_vec3f* vout) { - *vout = ma_sound_group_get_direction_to_listener(sgroup); -} - -void WA_ma_sound_group_get_position(const ma_sound_group* sgroup, ma_vec3f* vout) { - *vout = ma_sound_group_get_position(sgroup); -} - -void WA_ma_sound_group_get_direction(const ma_sound_group* sgroup, ma_vec3f* vout) { - *vout = ma_sound_group_get_direction(sgroup); -} - -void WA_ma_sound_group_get_velocity(const ma_sound_group* sgroup, ma_vec3f* vout) { - *vout = ma_sound_group_get_velocity(sgroup); -} - -// ma_sound -void WA_ma_sound_get_direction_to_listener(const ma_sound* sound, ma_vec3f* vout) { - *vout = ma_sound_get_direction_to_listener(sound); -} - -void WA_ma_sound_get_position(const ma_sound* sound, ma_vec3f* vout) { - *vout = ma_sound_get_position(sound); -} - -void WA_ma_sound_get_direction(const ma_sound* sound, ma_vec3f* vout) { - *vout = ma_sound_get_direction(sound); -} - -void WA_ma_sound_get_velocity(const ma_sound* sound, ma_vec3f* vout) { - *vout = ma_sound_get_velocity(sound); -} -//-------------------------------------------------------------------------------------------------- diff --git a/libs/zaudio/src/zaudio.zig b/libs/zaudio/src/zaudio.zig deleted file mode 100644 index 16371b7a6..000000000 --- a/libs/zaudio/src/zaudio.zig +++ /dev/null @@ -1,2410 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const math = std.math; - -pub const SoundFlags = packed struct(u32) { - stream: bool = false, - decode: bool = false, - async_load: bool = false, - wait_init: bool = false, - no_default_attachment: bool = false, - no_pitch: bool = false, - no_spatialization: bool = false, - _padding: u25 = 0, -}; - -// TODO: Add all errors. -pub const Error = error{ - GenericError, - InvalidArgs, - InvalidOperation, - OutOfMemory, -}; - -const Result = enum(i32) { - success = 0, - generic_error = -1, - invalid_args = -2, - invalid_operation = -3, - out_of_memory = -4, -}; - -pub fn maybeError(result: Result) Error!void { - return switch (result) { - .success => {}, - .generic_error => Error.GenericError, - .invalid_args => Error.InvalidArgs, - .invalid_operation => Error.InvalidOperation, - .out_of_memory => Error.OutOfMemory, - }; -} - -pub const PanMode = enum(u32) { - balance, - pan, -}; - -pub const AttenuationModel = enum(u32) { - none, - inverse, - linear, - exponential, -}; - -pub const Positioning = enum(u32) { - absolute, - relative, -}; - -pub const Format = enum(u32) { - unknown, - unsigned8, - signed16, - signed24, - signed32, - float32, -}; - -pub const DeviceState = enum(u32) { - uninitialized = 0, - stopped = 1, - started = 2, - starting = 3, - stopping = 4, -}; - -pub const Bool32 = u32; -pub const Channel = u8; - -pub const ResourceManager = *align(@sizeOf(usize)) ResourceManagerImpl; - -const ResourceManagerImpl = opaque { - // TODO: Add methods. -}; - -pub const Vfs = *align(@sizeOf(usize)) VfsImpl; - -const VfsImpl = opaque { - // TODO: Add methods. -}; - -pub const Context = *align(@sizeOf(usize)) ContextImpl; - -const ContextImpl = opaque { - // TODO: Add methods. -}; - -pub const Log = *align(@sizeOf(usize)) LogImpl; - -const LogImpl = opaque { - // TODO: Add methods. -}; -//-------------------------------------------------------------------------------------------------- -// -// Data Source -// -//-------------------------------------------------------------------------------------------------- -pub const DataSource = *align(@sizeOf(usize)) DataSourceImpl; - -pub fn createDataSource(config: DataSourceConfig) Error!DataSource { - var handle: ?DataSource = null; - try maybeError(zaudioDataSourceCreate(&config, &handle)); - return handle.?; -} -extern fn zaudioDataSourceCreate(config: *const DataSourceConfig, out_handle: ?*?*DataSourceImpl) Result; - -pub const DataSourceFlags = packed struct(u32) { - self_managed_range_and_loop_point: bool = false, - _padding: u31 = 0, -}; - -pub const DataSourceVTable = extern struct { - onRead: ?*const fn ( - ds: DataSource, - frames_out: ?*anyopaque, - frame_count: u64, - frames_read: *u64, - ) callconv(.C) Result, - onSeek: ?*const fn (ds: DataSource, frame_index: u64) callconv(.C) Result, - onGetDataFormat: ?*const fn ( - ds: DataSource, - format: ?*Format, - channels: ?*u32, - sample_rate: ?*u32, - channel_map: ?[*]Channel, - channel_map_cap: usize, - ) callconv(.C) Result, - onGetCursor: ?*const fn (ds: DataSource, cursor: ?*u64) callconv(.C) Result, - onGetLength: ?*const fn (ds: DataSource, length: ?*u64) callconv(.C) Result, - onSetLooping: ?*const fn (ds: DataSource, is_looping: Bool32) callconv(.C) Result, - flags: DataSourceFlags, -}; - -pub const DataSourceConfig = extern struct { - vtable: *const DataSourceVTable, - - pub fn init() DataSourceConfig { - var config: DataSourceConfig = undefined; - zaudioDataSourceConfigInit(&config); - return config; - } - extern fn zaudioDataSourceConfigInit(out_config: *DataSourceConfig) void; -}; - -const DataSourceImpl = opaque { - pub const destroy = zaudioDataSourceDestroy; - extern fn zaudioDataSourceDestroy(handle: DataSource) void; - - fn Methods(comptime T: type) type { - return struct { - pub fn asDataSource(handle: T) DataSource { - return @ptrCast(DataSource, handle); - } - // TODO: Add missing methods. - }; - } -}; -//-------------------------------------------------------------------------------------------------- -// -// Waveform Data Source -// -//-------------------------------------------------------------------------------------------------- -pub const WaveformDataSource = *align(@sizeOf(usize)) WaveformDataSourceImpl; - -pub fn createWaveformDataSource(config: WaveformConfig) Error!WaveformDataSource { - var handle: ?WaveformDataSource = null; - try maybeError(zaudioWaveformCreate(&config, &handle)); - return handle.?; -} -extern fn zaudioWaveformCreate(config: *const WaveformConfig, handle: ?*?*WaveformDataSourceImpl) Result; - -pub const WaveformType = enum(u32) { - sine, - square, - triangle, - sawtooth, -}; - -pub const WaveformConfig = extern struct { - format: Format, - channels: u32, - sampleRate: u32, - waveform_type: WaveformType, - amplitude: f64, - frequency: f64, - - pub fn init( - format: Format, - num_channels: u32, - sample_rate: u32, - waveform_type: WaveformType, - amplitude: f64, - frequency: f64, - ) WaveformConfig { - var config: WaveformConfig = undefined; - zaudioWaveformConfigInit( - format, - num_channels, - sample_rate, - waveform_type, - amplitude, - frequency, - &config, - ); - return config; - } - extern fn zaudioWaveformConfigInit( - format: Format, - channels: u32, - sampleRate: u32, - waveform_type: WaveformType, - amplitude: f64, - frequency: f64, - out_config: *WaveformConfig, - ) void; -}; - -const WaveformDataSourceImpl = opaque { - pub usingnamespace DataSourceImpl.Methods(WaveformDataSource); - - pub const destroy = zaudioWaveformDestroy; - extern fn zaudioWaveformDestroy(waveform: WaveformDataSource) void; - - pub fn setAmplitude(waveform: WaveformDataSource, amplitude: f64) Error!void { - try maybeError(ma_waveform_set_amplitude(waveform, amplitude)); - } - extern fn ma_waveform_set_amplitude(waveform: WaveformDataSource, amplitude: f64) Result; - - pub fn setFrequency(waveform: WaveformDataSource, frequency: f64) Error!void { - try maybeError(ma_waveform_set_frequency(waveform, frequency)); - } - extern fn ma_waveform_set_frequency(waveform: WaveformDataSource, frequency: f64) Result; - - pub fn setType(waveform: WaveformDataSource, waveform_type: WaveformType) Error!void { - try maybeError(ma_waveform_set_type(waveform, waveform_type)); - } - extern fn ma_waveform_set_type(waveform: WaveformDataSource, waveform_type: WaveformType) Result; - - pub fn setSampleRate(waveform: WaveformDataSource, sample_rate: u32) Error!void { - try maybeError(ma_waveform_set_sample_rate(waveform, sample_rate)); - } - extern fn ma_waveform_set_sample_rate(waveform: WaveformDataSource, sample_rate: u32) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Noise Data Source -// -//-------------------------------------------------------------------------------------------------- -pub const NoiseDataSource = *align(@sizeOf(usize)) NoiseDataSourceImpl; - -pub fn createNoiseDataSource(config: NoiseConfig) Error!NoiseDataSource { - var handle: ?NoiseDataSource = null; - try maybeError(zaudioNoiseCreate(&config, &handle)); - return handle.?; -} -extern fn zaudioNoiseCreate(config: *const NoiseConfig, out_handle: ?*?*NoiseDataSourceImpl) Result; - -pub const NoiseType = enum(u32) { - white, - pink, - brownian, -}; - -pub const NoiseConfig = extern struct { - format: Format, - num_channels: u32, - noise_type: NoiseType, - seed: i32, - amplitude: f64, - duplicate_channels: Bool32, - - pub fn init( - format: Format, - num_channels: u32, - noise_type: NoiseType, - seed: i32, - amplitude: f64, - ) NoiseConfig { - var config: NoiseConfig = undefined; - zaudioNoiseConfigInit( - format, - num_channels, - noise_type, - seed, - amplitude, - &config, - ); - return config; - } - extern fn zaudioNoiseConfigInit( - format: Format, - num_channels: u32, - noise_type: NoiseType, - seed: i32, - amplitude: f64, - out_config: *NoiseConfig, - ) void; -}; - -const NoiseDataSourceImpl = opaque { - pub usingnamespace DataSourceImpl.Methods(NoiseDataSource); - - pub const destroy = zaudioNoiseDestroy; - extern fn zaudioNoiseDestroy(handle: NoiseDataSource) void; - - pub fn setAmplitude(noise: NoiseDataSource, amplitude: f64) Error!void { - try maybeError(ma_noise_set_amplitude(noise, amplitude)); - } - extern fn ma_noise_set_amplitude(noise: NoiseDataSource, amplitude: f64) Result; - - pub fn setType(noise: NoiseDataSource, noise_type: NoiseType) Error!void { - try maybeError(ma_noise_set_type(noise, noise_type)); - } - extern fn ma_noise_set_type(noise: NoiseDataSource, noise_type: NoiseType) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Node -// -//-------------------------------------------------------------------------------------------------- -pub const Node = *align(@sizeOf(usize)) NodeImpl; - -pub const NodeState = enum(u32) { - started, - stopped, -}; - -pub const NodeFlags = packed struct(u32) { - passthrough: bool = false, - continuous_processing: bool = false, - allow_null_input: bool = false, - different_processing_rates: bool = false, - silent_output: bool = false, - _padding: u27 = 0, -}; - -pub const NodeVTable = extern struct { - onProcess: ?*const fn ( - node: Node, - frames_in: ?*[*]const f32, - frame_count_in: ?*u32, - frames_out: *[*]f32, - frame_count_out: *u32, - ) callconv(.C) void, - onGetRequiredInputFrameCount: ?*const fn ( - node: Node, - output_frame_count: u32, - input_frame_count: *u32, - ) callconv(.C) Result, - input_bus_count: u8, - output_bus_count: u8, - flags: NodeFlags, -}; - -pub const NodeConfig = extern struct { - vtable: *const NodeVTable, - initial_state: NodeState, - input_bus_count: u32, - output_bus_count: u32, - input_channels: [*]const u32, - output_channels: [*]const u32, - - pub fn init() NodeConfig { - var config: NodeConfig = undefined; - zaudioNodeConfigInit(&config); - return config; - } - extern fn zaudioNodeConfigInit(out_config: *NodeConfig) void; -}; - -const NodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(Node); - - fn Methods(comptime T: type) type { - return struct { - pub fn asNode(node: T) Node { - return @ptrCast(Node, node); - } - - pub fn getNodeGraph(node: T) NodeGraph { - return ma_node_get_node_graph(node.asNode()); - } - extern fn ma_node_get_node_graph(node: Node) NodeGraph; - - pub fn getNumInputBuses(node: T) u32 { - return ma_node_get_input_bus_count(node.asNode()); - } - extern fn ma_node_get_input_bus_count(node: Node) u32; - - pub fn getNumOutputBuses(node: T) u32 { - return ma_node_get_output_bus_count(node.asNode()); - } - extern fn ma_node_get_output_bus_count(node: Node) u32; - - pub fn getNumInputChannels(node: T, bus_index: u32) u32 { - return ma_node_get_input_channels(node.asNode(), bus_index); - } - extern fn ma_node_get_input_channels(node: Node, bus_index: u32) u32; - - pub fn getNumOutputChannels(node: T, bus_index: u32) u32 { - return ma_node_get_output_channels(node.asNode(), bus_index); - } - extern fn ma_node_get_output_channels(node: Node, bus_index: u32) u32; - - pub fn attachOutputBus( - node: T, - output_bus_index: u32, - other_node: Node, - other_node_input_bus_index: u32, - ) Error!void { - try maybeError(ma_node_attach_output_bus( - node.asNode(), - output_bus_index, - other_node.asNode(), - other_node_input_bus_index, - )); - } - extern fn ma_node_attach_output_bus( - node: Node, - output_bus_index: u32, - other_node: Node, - other_node_input_bus_index: u32, - ) Result; - - pub fn dettachOutputBus(node: T, output_bus_index: u32) Error!void { - try maybeError(ma_node_detach_output_bus(node.asNode(), output_bus_index)); - } - extern fn ma_node_detach_output_bus(node: Node, output_bus_index: u32) Result; - - pub fn dettachAllOutputBuses(node: T) Error!void { - try maybeError(ma_node_detach_all_output_buses(node.asNode())); - } - extern fn ma_node_detach_all_output_buses(node: Node) Result; - - pub fn setOutputBusVolume(node: T, output_bus_index: u32, volume: f32) Error!void { - try maybeError(ma_node_set_output_bus_volume(node.asNode(), output_bus_index, volume)); - } - extern fn ma_node_set_output_bus_volume(node: Node, output_bus_index: u32, volume: f32) Result; - - pub fn getOutputBusVolume(node: T, output_bus_index: u32) f32 { - return ma_node_get_output_bus_volume(node.asNode(), output_bus_index); - } - extern fn ma_node_get_output_bus_volume(node: Node, output_bus_index: u32) f32; - - pub fn setState(node: T, state: NodeState) Error!void { - try maybeError(ma_node_set_state(node.asNode(), state)); - } - extern fn ma_node_set_state(node: Node, NodeState) Result; - - pub fn getState(node: T) NodeState { - return ma_node_get_state(node.asNode()); - } - extern fn ma_node_get_state(node: Node) NodeState; - - pub fn setTime(node: T, local_time: u64) Error!void { - try maybeError(ma_node_set_time(node.asNode(), local_time)); - } - extern fn ma_node_set_time(node: Node, local_time: u64) Result; - - pub fn getTime(node: T) u64 { - return ma_node_get_time(node.asNode()); - } - extern fn ma_node_get_time(node: Node) u64; - }; - } -}; -//-------------------------------------------------------------------------------------------------- -// -// Data Source Node -// -//-------------------------------------------------------------------------------------------------- -pub const DataSourceNode = *align(@sizeOf(usize)) DataSourceNodeImpl; - -pub const DataSourceNodeConfig = extern struct { - node_config: NodeConfig, - data_source: DataSource, - - pub fn init(ds: DataSource) DataSourceNodeConfig { - var config: DataSourceNodeConfig = undefined; - zaudioDataSourceNodeConfigInit(ds, &config); - return config; - } - extern fn zaudioDataSourceNodeConfigInit(ds: DataSource, out_config: *DataSourceNodeConfig) void; -}; - -const DataSourceNodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(DataSourceNode); - - pub const destroy = zaudioDataSourceNodeDestroy; - extern fn zaudioDataSourceNodeDestroy(handle: DataSourceNode) void; - - pub fn setLooping(handle: DataSourceNode, is_looping: bool) void { - try maybeError(ma_data_source_node_set_looping(handle, @boolToInt(is_looping))); - } - extern fn ma_data_source_node_set_looping(handle: DataSourceNode, is_looping: Bool32) Result; - - pub fn isLooping(handle: DataSourceNode) bool { - return if (ma_data_source_node_is_looping(handle) == 0) false else true; - } - extern fn ma_data_source_node_is_looping(handle: DataSourceNode) Bool32; -}; -//-------------------------------------------------------------------------------------------------- -// -// Splitter Node -// -//-------------------------------------------------------------------------------------------------- -pub const SplitterNode = *align(@sizeOf(usize)) SplitterNodeImpl; - -pub const SplitterNodeConfig = struct { - node_config: NodeConfig, - channels: u32, - - pub fn init(channels: u32) SplitterNodeConfig { - var config: SplitterNodeConfig = undefined; - zaudioSplitterNodeConfigInit(channels, &config); - return config; - } - extern fn zaudioSplitterNodeConfigInit(channels: u32, out_config: *SplitterNodeConfig) void; -}; - -const SplitterNodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(SplitterNode); - - pub const destroy = zaudioSplitterNodeDestroy; - extern fn zaudioSplitterNodeDestroy(handle: SplitterNode) void; -}; -//-------------------------------------------------------------------------------------------------- -// -// Biquad Filter Node -// -//-------------------------------------------------------------------------------------------------- -pub const BiquadNode = *align(@sizeOf(usize)) BiquadNodeImpl; - -pub const BiquadConfig = extern struct { - format: Format, - channels: u32, - b0: f64, - b1: f64, - b2: f64, - a0: f64, - a1: f64, - a2: f64, -}; - -pub const BiquadNodeConfig = extern struct { - node_config: NodeConfig, - biquad: BiquadConfig, - - pub fn init(channels: u32, b0: f32, b1: f32, b2: f32, a0: f32, a1: f32, a2: f32) BiquadNodeConfig { - var config: BiquadNodeConfig = undefined; - zaudioBiquadNodeConfigInit(channels, b0, b1, b2, a0, a1, a2, &config); - return config; - } -}; -extern fn zaudioBiquadNodeConfigInit( - channels: u32, - b0: f32, - b1: f32, - b2: f32, - a0: f32, - a1: f32, - a2: f32, - out_config: *BiquadNodeConfig, -) void; - -const BiquadNodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(BiquadNode); - - pub const destroy = zaudioBiquadNodeDestroy; - extern fn zaudioBiquadNodeDestroy(handle: BiquadNode) void; - - pub fn reconfigure(handle: BiquadNode, config: BiquadNodeConfig) Error!void { - try maybeError(ma_biquad_node_reinit(&config, handle)); - } - extern fn ma_biquad_node_reinit(config: *const BiquadConfig, handle: BiquadNode) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Low-Pass Filter Node -// -//-------------------------------------------------------------------------------------------------- -pub const LpfNode = *align(@sizeOf(usize)) LpfNodeImpl; - -pub const LpfConfig = extern struct { - format: Format, - channels: u32, - sampleRate: u32, - cutoff_frequency: f64, - order: u32, -}; - -pub const LpfNodeConfig = extern struct { - node_config: NodeConfig, - lpf: LpfConfig, - - pub fn init(channels: u32, sample_rate: u32, cutoff_frequency: f64, order: u32) LpfNodeConfig { - var config: LpfNodeConfig = undefined; - zaudioLpfNodeConfigInit(channels, sample_rate, cutoff_frequency, order, &config); - return config; - } - extern fn zaudioLpfNodeConfigInit( - channels: u32, - sample_rate: u32, - cutoff_frequency: f64, - order: u32, - out_config: *LpfNodeConfig, - ) void; -}; - -const LpfNodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(LpfNode); - - pub const destroy = zaudioLpfNodeDestroy; - extern fn zaudioLpfNodeDestroy(handle: LpfNode) void; - - pub fn reconfigure(handle: LpfNode, config: LpfConfig) Error!void { - try maybeError(ma_lpf_node_reinit(&config, handle)); - } - extern fn ma_lpf_node_reinit(config: *const LpfConfig, handle: LpfNode) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// High-Pass Filter Node -// -//-------------------------------------------------------------------------------------------------- -pub const HpfNode = *align(@sizeOf(usize)) HpfNodeImpl; - -pub const HpfConfig = extern struct { - format: Format, - channels: u32, - sample_rate: u32, - cutoff_frequency: f64, - order: u32, -}; - -pub const HpfNodeConfig = extern struct { - node_config: NodeConfig, - hpf: HpfConfig, - - pub fn init(channels: u32, sample_rate: u32, cutoff_frequency: f64, order: u32) HpfNodeConfig { - var config: HpfNodeConfig = undefined; - zaudioHpfNodeConfigInit(channels, sample_rate, cutoff_frequency, order, &config); - return config; - } - extern fn zaudioHpfNodeConfigInit( - channels: u32, - sample_rate: u32, - cutoff_frequency: f64, - order: u32, - out_config: *HpfNodeConfig, - ) void; -}; - -const HpfNodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(HpfNode); - - pub const destroy = zaudioHpfNodeDestroy; - extern fn zaudioHpfNodeDestroy(handle: HpfNode) void; - - pub fn reconfigure(handle: HpfNode, config: HpfConfig) Error!void { - try maybeError(ma_hpf_node_reinit(&config, handle)); - } - extern fn ma_hpf_node_reinit(config: *const HpfConfig, handle: HpfNode) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Notch Filter Node -// -//-------------------------------------------------------------------------------------------------- -pub const NotchNode = *align(@sizeOf(usize)) NotchNodeImpl; - -pub const NotchConfig = extern struct { - format: Format, - channels: u32, - sample_rate: u32, - q: f64, - frequency: f64, -}; - -pub const NotchNodeConfig = extern struct { - node_config: NodeConfig, - notch: NotchConfig, - - pub fn init(channels: u32, sample_rate: u32, q: f64, frequency: f64) NotchNodeConfig { - var config: NotchNodeConfig = undefined; - zaudioNotchNodeConfigInit(channels, sample_rate, q, frequency, &config); - return config; - } - extern fn zaudioNotchNodeConfigInit( - channels: u32, - sample_rate: u32, - q: f64, - frequency: f64, - out_config: *NotchNodeConfig, - ) void; -}; - -const NotchNodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(NotchNode); - - pub const destroy = zaudioNotchNodeDestroy; - extern fn zaudioNotchNodeDestroy(handle: NotchNode) void; - - pub fn reconfigure(handle: NotchNode, config: NotchConfig) Error!void { - try maybeError(ma_notch_node_reinit(&config, handle)); - } - extern fn ma_notch_node_reinit(config: *const NotchConfig, handle: NotchNode) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Peak Filter Node -// -//-------------------------------------------------------------------------------------------------- -pub const PeakNode = *align(@sizeOf(usize)) PeakNodeImpl; - -pub const PeakConfig = extern struct { - format: Format, - channels: u32, - sample_rate: u32, - gain_db: f64, - q: f64, - frequency: f64, -}; - -pub const PeakNodeConfig = extern struct { - node_config: NodeConfig, - peak: PeakConfig, - - pub fn init(channels: u32, sample_rate: u32, gain_db: f64, q: f64, frequency: f64) PeakNodeConfig { - var config: PeakNodeConfig = undefined; - zaudioPeakNodeConfigInit(channels, sample_rate, gain_db, q, frequency, &config); - return config; - } - extern fn zaudioPeakNodeConfigInit( - channels: u32, - sample_rate: u32, - gain_db: f64, - q: f64, - frequency: f64, - out_config: *PeakNodeConfig, - ) void; -}; - -const PeakNodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(PeakNode); - - pub const destroy = zaudioPeakNodeDestroy; - extern fn zaudioPeakNodeDestroy(handle: PeakNode) void; - - pub fn reconfigure(handle: PeakNode, config: PeakConfig) Error!void { - try maybeError(ma_peak_node_reinit(&config, handle)); - } - extern fn ma_peak_node_reinit(config: *const PeakConfig, handle: PeakNode) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Low Shelf Filter Node -// -//-------------------------------------------------------------------------------------------------- -pub const LoshelfNode = *align(@sizeOf(usize)) LoshelfNodeImpl; - -pub const LoshelfConfig = extern struct { - format: Format, - channels: u32, - sample_rate: u32, - gain_db: f64, - shelf_slope: f64, - frequency: f64, -}; - -pub const LoshelfNodeConfig = extern struct { - node_config: NodeConfig, - loshelf: LoshelfConfig, - - pub fn init( - channels: u32, - sample_rate: u32, - gain_db: f64, - shelf_slope: f64, - frequency: f64, - ) LoshelfNodeConfig { - var config: LoshelfNodeConfig = undefined; - zaudioLoshelfNodeConfigInit(channels, sample_rate, gain_db, shelf_slope, frequency, &config); - return config; - } - extern fn zaudioLoshelfNodeConfigInit( - channels: u32, - sample_rate: u32, - gain_db: f64, - shelf_slope: f64, - frequency: f64, - out_config: *LoshelfNodeConfig, - ) void; -}; - -const LoshelfNodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(LoshelfNode); - - pub const destroy = zaudioLoshelfNodeDestroy; - extern fn zaudioLoshelfNodeDestroy(handle: LoshelfNode) void; - - pub fn reconfigure(handle: LoshelfNode, config: LoshelfConfig) Error!void { - try maybeError(ma_loshelf_node_reinit(&config, handle)); - } - extern fn ma_loshelf_node_reinit(config: *const LoshelfConfig, handle: LoshelfNode) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// High Shelf Filter Node -// -//-------------------------------------------------------------------------------------------------- -pub const HishelfNode = *align(@sizeOf(usize)) HishelfNodeImpl; - -pub const HishelfConfig = extern struct { - format: Format, - channels: u32, - sample_rate: u32, - gain_db: f64, - shelf_slope: f64, - frequency: f64, -}; - -pub const HishelfNodeConfig = extern struct { - node_config: NodeConfig, - hishelf: HishelfConfig, - - pub fn init( - channels: u32, - sample_rate: u32, - gain_db: f64, - shelf_slope: f64, - frequency: f64, - ) HishelfNodeConfig { - var config: HishelfNodeConfig = undefined; - zaudioHishelfNodeConfigInit(channels, sample_rate, gain_db, shelf_slope, frequency, &config); - return config; - } - extern fn zaudioHishelfNodeConfigInit( - channels: u32, - sample_rate: u32, - gain_db: f64, - shelf_slope: f64, - frequency: f64, - out_config: *HishelfNodeConfig, - ) void; -}; - -const HishelfNodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(HishelfNode); - - pub const destroy = zaudioHishelfNodeDestroy; - extern fn zaudioHishelfNodeDestroy(handle: HishelfNode) void; - - pub fn reconfigure(handle: HishelfNode, config: HishelfConfig) Error!void { - try maybeError(ma_hishelf_node_reinit(&config, handle)); - } - extern fn ma_hishelf_node_reinit(config: *const HishelfConfig, handle: HishelfNode) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Delay Filter Node -// -//-------------------------------------------------------------------------------------------------- -pub const DelayNode = *align(@sizeOf(usize)) DelayNodeImpl; - -pub const DelayConfig = extern struct { - channels: u32, - sample_rate: u32, - delay_in_frames: u32, - delay_start: Bool32, - wet: f32, - dry: f32, - decay: f32, -}; - -pub const DelayNodeConfig = extern struct { - node_config: NodeConfig, - delay: DelayConfig, - - pub fn init(channels: u32, sample_rate: u32, delay_in_frames: u32, decay: f32) DelayNodeConfig { - var config: DelayNodeConfig = undefined; - zaudioDelayNodeConfigInit(channels, sample_rate, delay_in_frames, decay, &config); - return config; - } - extern fn zaudioDelayNodeConfigInit( - channels: u32, - sample_rate: u32, - delay_in_frames: u32, - decay: f32, - out_config: *DelayNodeConfig, - ) void; -}; - -const DelayNodeImpl = opaque { - pub usingnamespace NodeImpl.Methods(DelayNode); - - pub const destroy = zaudioDelayNodeDestroy; - extern fn zaudioDelayNodeDestroy(handle: DelayNode) void; - - pub const setWet = ma_delay_node_set_wet; - extern fn ma_delay_node_set_wet(handle: DelayNode, value: f32) void; - - pub const getWet = ma_delay_node_get_wet; - extern fn ma_delay_node_get_wet(handle: DelayNode) f32; - - pub const setDry = ma_delay_node_set_dry; - extern fn ma_delay_node_set_dry(handle: DelayNode, value: f32) void; - - pub const getDry = ma_delay_node_get_dry; - extern fn ma_delay_node_get_dry(handle: DelayNode) f32; - - pub const setDecay = ma_delay_node_set_decay; - extern fn ma_delay_node_set_decay(handle: DelayNode, value: f32) void; - - pub const getDecay = ma_delay_node_get_decay; - extern fn ma_delay_node_get_decay(handle: DelayNode) f32; -}; -//-------------------------------------------------------------------------------------------------- -// -// Node Graph -// -//-------------------------------------------------------------------------------------------------- -pub const NodeGraph = *align(@sizeOf(usize)) NodeGraphImpl; - -pub fn createNodeGraph(config: NodeGraphConfig) Error!NodeGraph { - var handle: ?NodeGraph = null; - try maybeError(zaudioNodeGraphCreate(&config, &handle)); - return handle.?; -} -extern fn zaudioNodeGraphCreate(config: *const NodeGraphConfig, out_handle: ?*?*NodeGraphImpl) Result; - -pub const NodeGraphConfig = extern struct { - channels: u32, - node_cache_cap_in_frames: u16, - - pub fn init(channels: u32) NodeGraphConfig { - var config: NodeGraphConfig = undefined; - zaudioNodeGraphConfigInit(channels, &config); - return config; - } - extern fn zaudioNodeGraphConfigInit(channels: u32, out_config: *NodeGraphConfig) void; -}; - -const NodeGraphImpl = opaque { - pub usingnamespace NodeImpl.Methods(NodeGraph); - pub usingnamespace NodeGraphImpl.Methods(NodeGraph); - - pub const destroy = zaudioNodeGraphDestroy; - extern fn zaudioNodeGraphDestroy(handle: NodeGraph) void; - - fn Methods(comptime T: type) type { - return struct { - pub fn asNodeGraph(handle: T) NodeGraph { - return @ptrCast(NodeGraph, handle); - } - - pub fn createDataSourceNode(node_graph: T, config: DataSourceNodeConfig) Error!DataSourceNode { - var handle: ?DataSourceNode = null; - try maybeError(zaudioDataSourceNodeCreate(node_graph.asNodeGraph(), &config, &handle)); - return handle.?; - } - extern fn zaudioDataSourceNodeCreate( - node_graph: NodeGraph, - config: *const DataSourceNodeConfig, - out_handle: ?*?*DataSourceNodeImpl, - ) Result; - - pub fn createBiquadNode(node_graph: T, config: BiquadNodeConfig) Error!BiquadNode { - var handle: ?BiquadNode = null; - try maybeError(zaudioBiquadNodeCreate(node_graph.asNodeGraph(), &config, &handle)); - return handle.?; - } - extern fn zaudioBiquadNodeCreate( - node_graph: NodeGraph, - config: *const BiquadNodeConfig, - out_handle: ?*?*BiquadNodeImpl, - ) Result; - - pub fn createLpfNode(node_graph: T, config: LpfNodeConfig) Error!LpfNode { - var handle: ?LpfNode = null; - try maybeError(zaudioLpfNodeCreate(node_graph.asNodeGraph(), &config, &handle)); - return handle.?; - } - extern fn zaudioLpfNodeCreate( - node_graph: NodeGraph, - config: *const LpfNodeConfig, - out_handle: ?*?*LpfNodeImpl, - ) Result; - - pub fn createHpfNode(node_graph: T, config: HpfNodeConfig) Error!HpfNode { - var handle: ?HpfNode = null; - try maybeError(zaudioHpfNodeCreate(node_graph.asNodeGraph(), &config, &handle)); - return handle.?; - } - extern fn zaudioHpfNodeCreate( - node_graph: NodeGraph, - config: *const HpfNodeConfig, - out_handle: ?*?*HpfNodeImpl, - ) Result; - - pub fn createSplitterNode(node_graph: T, config: SplitterNodeConfig) Error!SplitterNode { - var handle: ?SplitterNode = null; - try maybeError(zaudioSplitterNodeCreate(node_graph.asNodeGraph(), &config, &handle)); - return handle.?; - } - extern fn zaudioSplitterNodeCreate( - node_graph: NodeGraph, - config: *const SplitterNodeConfig, - out_handle: ?*?*SplitterNodeImpl, - ) Result; - - pub fn createNotchNode(node_graph: T, config: NotchNodeConfig) Error!NotchNode { - var handle: ?NotchNode = null; - try maybeError(zaudioNotchNodeCreate(node_graph.asNodeGraph(), &config, &handle)); - return handle.?; - } - extern fn zaudioNotchNodeCreate( - node_graph: NodeGraph, - config: *const NotchNodeConfig, - out_handle: ?*?*NotchNodeImpl, - ) Result; - - pub fn createPeakNode(node_graph: T, config: PeakNodeConfig) Error!PeakNode { - var handle: ?PeakNode = null; - try maybeError(zaudioPeakNodeCreate(node_graph.asNodeGraph(), &config, &handle)); - return handle.?; - } - extern fn zaudioPeakNodeCreate( - node_graph: NodeGraph, - config: *const PeakNodeConfig, - out_handle: ?*?*PeakNodeImpl, - ) Result; - - pub fn createLoshelfNode(node_graph: T, config: LoshelfNodeConfig) Error!LoshelfNode { - var handle: ?LoshelfNode = null; - try maybeError(zaudioLoshelfNodeCreate(node_graph.asNodeGraph(), &config, &handle)); - return handle.?; - } - extern fn zaudioLoshelfNodeCreate( - node_graph: NodeGraph, - config: *const LoshelfNodeConfig, - out_handle: ?*?*LoshelfNodeImpl, - ) Result; - - pub fn createHishelfNode(node_graph: T, config: HishelfNodeConfig) Error!HishelfNode { - var handle: ?HishelfNode = null; - try maybeError(zaudioHishelfNodeCreate(node_graph.asNodeGraph(), &config, &handle)); - return handle.?; - } - extern fn zaudioHishelfNodeCreate( - node_graph: NodeGraph, - config: *const HishelfNodeConfig, - out_handle: ?*?*HishelfNodeImpl, - ) Result; - - pub fn createDelayNode(node_graph: T, config: DelayNodeConfig) Error!DelayNode { - var handle: ?DelayNode = null; - try maybeError(zaudioDelayNodeCreate(node_graph.asNodeGraph(), &config, &handle)); - return handle.?; - } - extern fn zaudioDelayNodeCreate( - node_graph: NodeGraph, - config: *const DelayNodeConfig, - out_handle: ?*?*DelayNodeImpl, - ) Result; - - pub fn getEndpoint(handle: T) Node { - return ma_node_graph_get_endpoint(handle.asNodeGraph()); - } - extern fn ma_node_graph_get_endpoint(handle: NodeGraph) Node; - - pub fn getChannels(handle: T) u32 { - return ma_node_graph_get_channels(handle.asNodeGraph()); - } - extern fn ma_node_graph_get_channels(handle: NodeGraph) u32; - - pub fn readPcmFrames( - node_graph: T, - frames_out: *anyopaque, - frame_count: u64, - frames_read: ?*u64, - ) Error!void { - try maybeError(ma_node_graph_read_pcm_frames( - node_graph.asNodeGraph(), - frames_out, - frame_count, - frames_read, - )); - } - extern fn ma_node_graph_read_pcm_frames( - handle: NodeGraph, - frames_out: *anyopaque, - frame_count: u64, - frames_read: ?*u64, - ) Result; - }; - } -}; -//-------------------------------------------------------------------------------------------------- -// -// Device -// -//-------------------------------------------------------------------------------------------------- -pub const Device = *align(@sizeOf(usize)) DeviceImpl; - -pub const DeviceType = enum(u32) { - playback = 1, - capture = 2, - duplex = 3, - loopback = 4, -}; - -pub const PerformanceProfile = enum(u32) { - low_latency, - conservative, -}; - -pub const DeviceDataProc = *const fn ( - device: Device, - output: ?*anyopaque, - input: ?*const anyopaque, - frame_count: u32, -) callconv(.C) void; - -pub const DeviceNotificationProc = *const fn ( - *const anyopaque, // TODO: Should be `*const ma_device_notification`. -) callconv(.C) void; - -pub const StopProc = *const fn (device: Device) callconv(.C) void; - -pub const ResampleAlgorithm = enum(u32) { - linear, - custom, -}; - -pub const DeviceId = extern union { - wasapi: [64]i32, - dsound: [16]u8, - winmm: u32, - alsa: [256]u8, - pulse: [256]u8, - jack: i32, - coreaudio: [256]u8, - sndio: [256]u8, - audio4: [256]u8, - oss: [64]u8, - aaudio: i32, - opensl: u32, - webaudio: [32]u8, - custom: extern union { - i: i32, - s: [256]u8, - p: ?*anyopaque, - }, - nullbackend: i32, -}; - -pub const ChannelMixMode = enum(u32) { - rectangular, - simple, - custom_weights, - - pub const default: ChannelMixMode = .rectangular; -}; - -pub const ShareMode = enum(u32) { - shared, - exclusive, -}; - -pub const OpenslStreamType = enum(u32) { - default, - voice, - system, - ring, - media, - alarm, - notification, -}; - -pub const OpenslRecordingPreset = enum(u32) { - default, - generic, - camcorder, - recognition, - voice_communication, - voice_unprocessed, -}; - -pub const AaudioUsage = enum(u32) { - default, - announcement, - emergency, - safety, - vehicle_status, - alarm, - assistance_accessibility, - assistance_navigation_guidance, - assistance_sonification, - assitant, - game, - media, - notification, - notification_event, - notification_ringtone, - voice_communication, - voice_communication_signalling, -}; - -pub const AaudioContentType = enum(u32) { - default, - movie, - music, - sonification, - speech, -}; - -pub const AaudioInputPreset = enum(u32) { - default, - generic, - camcorder, - unprocessed, - voice_recognition, - voice_communication, - voice_performance, -}; - -pub const DeviceConfig = extern struct { - device_type: DeviceType, - sample_rate: u32, - period_size_in_frames: u32, - period_size_in_milliseconds: u32, - periods: u32, - performance_profile: PerformanceProfile, - no_pre_silenced_output_buffer: bool, - no_clip: bool, - no_disable_denormals: bool, - no_fixed_sized_callback: bool, - data_callback: ?DeviceDataProc, - notification_callback: ?DeviceNotificationProc, - stop_callback: ?StopProc, - user_data: ?*anyopaque, - resampling: extern struct { - format: Format, - channels: u32, - sample_rate_in: u32, - sample_rate_out: u32, - algorithm: ResampleAlgorithm, - backend_vtable: ?*anyopaque, // TODO: Should be `*ma_resampling_backend_vtable` (custom resamplers). - backend_user_data: ?*anyopaque, - linear: extern struct { - lpf_order: u32, - }, - }, - playback: extern struct { - device_id: ?*const DeviceId, - format: Format, - channels: u32, - channel_map: [*]Channel, - channel_mix_mode: ChannelMixMode, - share_mode: ShareMode, - }, - capture: extern struct { - device_id: ?*const DeviceId, - format: Format, - channels: u32, - channel_map: [*]Channel, - channel_mix_mode: ChannelMixMode, - share_mode: ShareMode, - }, - wasapi: extern struct { - no_auto_convert_src: bool, - no_default_quality_src: bool, - no_auto_stream_routing: bool, - no_hardware_offloading: bool, - }, - alsa: extern struct { - no_mmap: Bool32, - no_auto_format: Bool32, - no_auto_channels: Bool32, - no_auto_resample: Bool32, - }, - pulse: extern struct { - stream_name_playback: [*:0]const u8, - stream_name_capture: [*:0]const u8, - }, - coreaudio: extern struct { - allow_nominal_sample_rate_change: Bool32, - }, - opensl: extern struct { - stream_type: OpenslStreamType, - recording_preset: OpenslRecordingPreset, - }, - aaudio: extern struct { - usage: AaudioUsage, - content_type: AaudioContentType, - input_preset: AaudioInputPreset, - no_auto_start_after_reroute: Bool32, - }, - - pub fn init(device_type: DeviceType) DeviceConfig { - var config: DeviceConfig = undefined; - zaudioDeviceConfigInit(device_type, &config); - return config; - } - extern fn zaudioDeviceConfigInit(device_type: DeviceType, out_config: *DeviceConfig) void; -}; - -pub fn createDevice(context: ?Context, config: DeviceConfig) Error!Device { - var handle: ?Device = null; - try maybeError(zaudioDeviceCreate(context, &config, &handle)); - return handle.?; -} -extern fn zaudioDeviceCreate( - context: ?Context, - config: *const DeviceConfig, - out_handle: ?*?*DeviceImpl, -) Result; - -const DeviceImpl = opaque { - pub const destroy = zaudioDeviceDestroy; - extern fn zaudioDeviceDestroy(device: Device) void; - - pub const getUserData = zaudioDeviceGetUserData; - extern fn zaudioDeviceGetUserData(device: Device) ?*anyopaque; - - pub const getContext = ma_device_get_context; - extern fn ma_device_get_context(device: Device) Context; - - pub const getLog = ma_device_get_log; - extern fn ma_device_get_log(device: Device) ?Log; - - pub fn start(device: Device) Error!void { - try maybeError(ma_device_start(device)); - } - extern fn ma_device_start(device: Device) Result; - - pub fn stop(device: Device) Error!void { - try maybeError(ma_device_stop(device)); - } - extern fn ma_device_stop(device: Device) Result; - - pub fn isStarted(device: Device) bool { - return ma_device_is_started(device) != 0; - } - extern fn ma_device_is_started(device: Device) Bool32; - - pub const getState = ma_device_get_state; - extern fn ma_device_get_state(device: Device) DeviceState; - - pub fn setMasterVolume(device: Device, volume: f32) Error!void { - try maybeError(ma_device_set_master_volume(device, volume)); - } - extern fn ma_device_set_master_volume(device: Device, volume: f32) Result; - - pub fn getMasterVolume(device: Device) Error!f32 { - var volume: f32 = 0.0; - try maybeError(ma_device_get_master_volume(device, &volume)); - return volume; - } - extern fn ma_device_get_master_volume(device: Device, volume: *f32) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Engine -// -//-------------------------------------------------------------------------------------------------- -pub const Engine = *align(@sizeOf(usize)) EngineImpl; - -pub const MonoExpansionMode = enum(u32) { - duplicate, - average, - stereo_only, - - pub const default: MonoExpansionMode = .duplicate; -}; - -pub const AllocationCallbacks = extern struct { - user_data: ?*anyopaque, - on_malloc: ?*const fn (len: usize, user_data: ?*anyopaque) callconv(.C) ?*anyopaque, - on_realloc: ?*const fn ( - ptr: ?*anyopaque, - len: usize, - user_data: ?*anyopaque, - ) callconv(.C) ?*anyopaque, - on_free: ?*const fn (ptr: ?*anyopaque, user_data: ?*anyopaque) callconv(.C) void, -}; - -pub const EngineConfig = extern struct { - resource_manager: ?ResourceManager, - context: ?Context, - device: ?Device, - playback_device_id: ?*DeviceId, - log: ?Log, - listener_count: u32, - channels: u32, - sample_rate: u32, - period_size_in_frames: u32, - period_size_in_milliseconds: u32, - gain_smooth_time_in_frames: u32, - gain_smooth_time_in_milliseconds: u32, - allocation_callbacks: AllocationCallbacks, - no_auto_start: Bool32, - no_device: Bool32, - mono_expansion_mode: MonoExpansionMode, - resource_manager_vfs: ?*Vfs, - - pub fn init() EngineConfig { - var config: EngineConfig = undefined; - zaudioEngineConfigInit(&config); - return config; - } - extern fn zaudioEngineConfigInit(out_config: *EngineConfig) void; -}; - -pub fn createEngine(config: ?EngineConfig) Error!Engine { - var handle: ?Engine = null; - try maybeError(zaudioEngineCreate(if (config) |conf| &conf else null, &handle)); - return handle.?; -} -extern fn zaudioEngineCreate(config: ?*const EngineConfig, out_handle: ?*?*EngineImpl) Result; - -const EngineImpl = opaque { - pub usingnamespace NodeImpl.Methods(Engine); - pub usingnamespace NodeGraphImpl.Methods(Engine); - - pub const destroy = zaudioEngineDestroy; - extern fn zaudioEngineDestroy(handle: Engine) void; - - pub fn createSoundFromFile( - engine: Engine, - file_path: [:0]const u8, - args: struct { - flags: SoundFlags = .{}, - sgroup: ?SoundGroup = null, - done_fence: ?Fence = null, - }, - ) Error!Sound { - return SoundImpl.createFromFile(engine, file_path, args.flags, args.sgroup, args.done_fence); - } - - pub fn createSoundFromDataSource( - engine: Engine, - allocator: std.mem.Allocator, - data_source: DataSource, - flags: SoundFlags, - sgroup: ?SoundGroup, - ) Error!Sound { - return SoundImpl.createFromDataSource(allocator, engine, data_source, flags, sgroup); - } - - pub fn createSound(engine: Engine, config: SoundConfig) Error!Sound { - return SoundImpl.create(engine, config); - } - - pub fn createSoundCopy( - engine: Engine, - allocator: std.mem.Allocator, - existing_sound: Sound, - flags: SoundFlags, - sgroup: ?SoundGroup, - ) Error!Sound { - return SoundImpl.createCopy(allocator, engine, existing_sound, flags, sgroup); - } - - pub fn createSoundGroup(engine: Engine, flags: SoundFlags, parent: ?SoundGroup) Error!SoundGroup { - return SoundGroupImpl.create(engine, flags, parent); - } - - pub const getResourceManager = ma_engine_get_resource_manager; - extern fn ma_engine_get_resource_manager(engine: Engine) ResourceManager; - - pub const getDevice = ma_engine_get_device; - extern fn ma_engine_get_device(engine: Engine) ?Device; - - pub const getLog = ma_engine_get_log; - extern fn ma_engine_get_log(engine: Engine) ?Log; - - pub const getSampleRate = ma_engine_get_sample_rate; - extern fn ma_engine_get_sample_rate(engine: Engine) u32; - - pub fn start(engine: Engine) Error!void { - try maybeError(ma_engine_start(engine)); - } - extern fn ma_engine_start(engine: Engine) Result; - - pub fn stop(engine: Engine) Error!void { - try maybeError(ma_engine_stop(engine)); - } - extern fn ma_engine_stop(engine: Engine) Result; - - pub fn setVolume(engine: Engine, volume: f32) Error!void { - try maybeError(ma_engine_set_volume(engine, volume)); - } - extern fn ma_engine_set_volume(engine: Engine, volume: f32) Result; - - pub fn setGainDb(engine: Engine, gain_db: f32) Error!void { - try maybeError(ma_engine_set_gain_db(engine, gain_db)); - } - extern fn ma_engine_set_gain_db(engine: Engine, gain_db: f32) Result; - - pub const getListenerCount = ma_engine_get_listener_count; - extern fn ma_engine_get_listener_count(engine: Engine) u32; - - pub fn findClosestListener(engine: Engine, absolute_pos_xyz: [3]f32) u32 { - return ma_engine_find_closest_listener( - engine, - absolute_pos_xyz[0], - absolute_pos_xyz[1], - absolute_pos_xyz[2], - ); - } - extern fn ma_engine_find_closest_listener(engine: Engine, x: f32, y: f32, z: f32) u32; - - pub fn setListenerPosition(engine: Engine, index: u32, v: [3]f32) void { - ma_engine_listener_set_position(engine, index, v[0], v[1], v[2]); - } - extern fn ma_engine_listener_set_position(engine: Engine, index: u32, x: f32, y: f32, z: f32) void; - - pub fn getListenerPosition(engine: Engine, index: u32) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_engine_listener_get_position(engine, index, &v); - return v; - } - extern fn WA_ma_engine_listener_get_position(engine: Engine, index: u32, vout: *[3]f32) void; - - pub fn setListenerDirection(engine: Engine, index: u32, v: [3]f32) void { - ma_engine_listener_set_direction(engine, index, v[0], v[1], v[2]); - } - extern fn ma_engine_listener_set_direction(engine: Engine, index: u32, x: f32, y: f32, z: f32) void; - - pub fn getListenerDirection(engine: Engine, index: u32) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_engine_listener_get_direction(engine, index, &v); - return v; - } - extern fn WA_ma_engine_listener_get_direction(engine: Engine, index: u32, vout: *[3]f32) void; - - pub fn setListenerVelocity(engine: Engine, index: u32, v: [3]f32) void { - ma_engine_listener_set_velocity(engine, index, v[0], v[1], v[2]); - } - extern fn ma_engine_listener_set_velocity(engine: Engine, index: u32, x: f32, y: f32, z: f32) void; - - pub fn getListenerVelocity(engine: Engine, index: u32) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_engine_listener_get_velocity(engine, index, &v); - return v; - } - extern fn WA_ma_engine_listener_get_velocity(engine: *Engine, index: u32, vout: *[3]f32) void; - - pub fn setListenerWorldUp(engine: Engine, index: u32, v: [3]f32) void { - ma_engine_listener_set_world_up(engine, index, v[0], v[1], v[2]); - } - extern fn ma_engine_listener_set_world_up(engine: Engine, index: u32, x: f32, y: f32, z: f32) void; - - pub fn getListenerWorldUp(engine: Engine, index: u32) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_engine_listener_get_world_up(engine, index, &v); - return v; - } - extern fn WA_ma_engine_listener_get_world_up(engine: Engine, index: u32, vout: *[3]f32) void; - - pub fn setListenerEnabled(engine: Engine, index: u32, enabled: bool) void { - ma_engine_listener_set_enabled(engine, index, if (enabled) 1 else 0); - } - extern fn ma_engine_listener_set_enabled(engine: Engine, index: u32, is_enabled: Bool32) void; - - pub fn isListenerEnabled(engine: Engine, index: u32) bool { - return ma_engine_listener_is_enabled(engine, index) != 0; - } - extern fn ma_engine_listener_is_enabled(engine: Engine, index: u32) Bool32; - - pub const setListenerCone = ma_engine_listener_set_cone; - extern fn ma_engine_listener_set_cone( - engine: Engine, - index: u32, - inner_radians: f32, - outer_radians: f32, - outer_gain: f32, - ) void; - - pub const getListenerCone = ma_engine_listener_get_cone; - extern fn ma_engine_listener_get_cone( - engine: Engine, - index: u32, - inner_radians: ?*f32, - outer_radians: ?*f32, - outer_gain: ?*f32, - ) void; - - pub fn playSound(engine: Engine, filepath: [:0]const u8, sgroup: ?SoundGroup) Error!void { - try maybeError(ma_engine_play_sound(engine, filepath.ptr, sgroup)); - } - extern fn ma_engine_play_sound(engine: Engine, filepath: [*:0]const u8, sgroup: ?SoundGroup) Result; - - pub fn playSoundEx( - engine: Engine, - filepath: [:0]const u8, - node: ?Node, - node_input_bus_index: u32, - ) Error!void { - try maybeError(ma_engine_play_sound_ex(engine, filepath.ptr, node, node_input_bus_index)); - } - extern fn ma_engine_play_sound_ex( - engine: Engine, - filepath: [*:0]const u8, - node: ?Node, - node_input_bus_index: u32, - ) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Sound -// -//-------------------------------------------------------------------------------------------------- -pub const Sound = *align(@sizeOf(usize)) SoundImpl; - -pub const SoundConfig = extern struct { - file_path: [*:0]const u8, - file_path_w: [*:0]const i32, - data_source: ?DataSource, - initial_attachment: ?Node, - initial_attachment_input_bus_index: u32, - channels_in: u32, - channels_out: u32, - flags: SoundFlags, - initial_seek_point_in_pcm_frames: u64, - range_beg_in_pcm_frames: u64, - range_end_in_pcm_Frames: u64, - loop_point_beg_in_pcm_frames: u64, - loop_point_end_in_pcm_frames: u64, - is_looping: Bool32, - done_fence: ?Fence, - - pub fn init() SoundConfig { - var config: SoundConfig = undefined; - zaudioSoundConfigInit(&config); - return config; - } - extern fn zaudioSoundConfigInit(out_config: *SoundConfig) void; -}; - -const SoundImpl = opaque { - pub usingnamespace NodeImpl.Methods(Sound); - - fn createFromFile( - engine: Engine, - file_path: [:0]const u8, - flags: SoundFlags, - sgroup: ?SoundGroup, - done_fence: ?Fence, - ) Error!Sound { - var handle: ?Sound = null; - try maybeError(zaudioSoundCreateFromFile(engine, file_path.ptr, flags, sgroup, done_fence, &handle)); - return handle.?; - } - extern fn zaudioSoundCreateFromFile( - engine: Engine, - file_path: [*:0]const u8, - flags: SoundFlags, - sgroup: ?SoundGroup, - done_fence: ?Fence, - out_handle: ?*?*SoundImpl, - ) Result; - - fn createFromDataSource( - engine: Engine, - data_source: DataSource, - flags: SoundFlags, - sgroup: ?SoundGroup, - ) Error!Sound { - var handle: ?Sound = null; - try maybeError(zaudioSoundCreateFromDataSource(engine, data_source, flags, sgroup, &handle)); - return handle.?; - } - extern fn zaudioSoundCreateFromDataSource( - engine: Engine, - data_source: DataSource, - flags: SoundFlags, - sgroup: ?SoundGroup, - out_handle: ?*?*SoundImpl, - ) Result; - - fn createCopy(engine: Engine, existing_sound: Sound, flags: SoundFlags, sgroup: ?SoundGroup) Error!Sound { - var handle: ?Sound = null; - try maybeError(zaudioSoundCreateCopy(engine, existing_sound, flags, sgroup, &handle)); - return handle.?; - } - extern fn zaudioSoundCreateCopy( - engine: Engine, - existing_sound: Sound, - flags: SoundFlags, - sgroup: ?SoundGroup, - out_handle: ?*?*SoundImpl, - ) Result; - - fn create(engine: Engine, config: SoundConfig) Error!Sound { - var handle: ?Sound = null; - try maybeError(zaudioSoundCreate(engine, &config, &handle)); - return handle.?; - } - extern fn zaudioSoundCreate(engine: Engine, config: *const SoundConfig, out_handle: ?*?*SoundImpl) Result; - - pub const destroy = zaudioSoundDestroy; - extern fn zaudioSoundDestroy(sound: Sound) void; - - pub const getDataSource = ma_sound_get_data_source; - extern fn ma_sound_get_data_source(sound: Sound) ?DataSource; - - pub const getEngine = ma_sound_get_engine; - extern fn ma_sound_get_engine(sound: Sound) Engine; - - pub fn start(sound: Sound) Error!void { - try maybeError(ma_sound_start(sound)); - } - extern fn ma_sound_start(sound: Sound) Result; - - pub fn stop(sound: Sound) Error!void { - try maybeError(ma_sound_stop(sound)); - } - extern fn ma_sound_stop(sound: Sound) Result; - - pub const setVolume = ma_sound_set_volume; - extern fn ma_sound_set_volume(sound: Sound, volume: f32) void; - - pub const getVolume = ma_sound_get_volume; - extern fn ma_sound_get_volume(sound: Sound) f32; - - pub const setPan = ma_sound_set_pan; - extern fn ma_sound_set_pan(sound: Sound, pan: f32) void; - - pub const getPan = ma_sound_get_pan; - extern fn ma_sound_get_pan(sound: Sound) f32; - - pub const setPanMode = ma_sound_set_pan_mode; - extern fn ma_sound_set_pan_mode(sound: Sound, pan_mode: PanMode) void; - - pub const getPanMode = ma_sound_get_pan_mode; - extern fn ma_sound_get_pan_mode(sound: Sound) PanMode; - - pub const setPitch = ma_sound_set_pitch; - extern fn ma_sound_set_pitch(sound: Sound, pitch: f32) void; - - pub const getPitch = ma_sound_get_pitch; - extern fn ma_sound_get_pitch(sound: Sound) f32; - - pub fn setSpatializationEnabled(sound: Sound, enabled: bool) void { - ma_sound_set_spatialization_enabled(sound, @boolToInt(enabled)); - } - extern fn ma_sound_set_spatialization_enabled(sound: Sound, enabled: Bool32) void; - - pub fn isSpatializationEnabled(sound: Sound) bool { - return ma_sound_is_spatialization_enabled(sound) != 0; - } - extern fn ma_sound_is_spatialization_enabled(sound: Sound) Bool32; - - pub const setPinnedListenerIndex = ma_sound_set_pinned_listener_index; - extern fn ma_sound_set_pinned_listener_index(sound: Sound, index: u32) void; - - pub const getPinnedListenerIndex = ma_sound_get_pinned_listener_index; - extern fn ma_sound_get_pinned_listener_index(sound: Sound) u32; - - pub const getListenerIndex = ma_sound_get_listener_index; - extern fn ma_sound_get_listener_index(sound: Sound) u32; - - pub fn getDirectionToListener(sound: Sound) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_sound_get_direction_to_listener(sound, &v); - return v; - } - extern fn WA_ma_sound_get_direction_to_listener(sound: Sound, vout: *[3]f32) void; - - pub fn setPosition(sound: Sound, v: [3]f32) void { - ma_sound_set_position(sound, v[0], v[1], v[2]); - } - extern fn ma_sound_set_position(sound: Sound, x: f32, y: f32, z: f32) void; - - pub fn getPosition(sound: Sound) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_sound_get_position(sound, &v); - return v; - } - extern fn WA_ma_sound_get_position(sound: Sound, vout: *[3]f32) void; - - pub fn setDirection(sound: Sound, v: [3]f32) void { - ma_sound_set_direction(sound, v[0], v[1], v[2]); - } - extern fn ma_sound_set_direction(sound: Sound, x: f32, y: f32, z: f32) void; - - pub fn getDirection(sound: Sound) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_sound_get_direction(sound, &v); - return v; - } - extern fn WA_ma_sound_get_direction(sound: Sound, vout: *[3]f32) void; - - pub fn setVelocity(sound: Sound, v: [3]f32) void { - ma_sound_set_velocity(sound, v[0], v[1], v[2]); - } - extern fn ma_sound_set_velocity(sound: Sound, x: f32, y: f32, z: f32) void; - - pub fn getVelocity(sound: Sound) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_sound_get_velocity(sound, &v); - return v; - } - extern fn WA_ma_sound_get_velocity(sound: Sound, vout: *[3]f32) void; - - pub const setAttenuationModel = ma_sound_set_attenuation_model; - extern fn ma_sound_set_attenuation_model(sound: Sound, model: AttenuationModel) void; - - pub const getAttenuationModel = ma_sound_get_attenuation_model; - extern fn ma_sound_get_attenuation_model(sound: Sound) AttenuationModel; - - pub const setPositioning = ma_sound_set_positioning; - extern fn ma_sound_set_positioning(sound: Sound, pos: Positioning) void; - - pub const getPositioning = ma_sound_get_positioning; - extern fn ma_sound_get_positioning(sound: Sound) Positioning; - - pub const setRolloff = ma_sound_set_rolloff; - extern fn ma_sound_set_rolloff(sound: Sound, rolloff: f32) void; - - pub const getRolloff = ma_sound_get_rolloff; - extern fn ma_sound_get_rolloff(sound: Sound) f32; - - pub const setMinGain = ma_sound_set_min_gain; - extern fn ma_sound_set_min_gain(sound: Sound, min_gain: f32) void; - - pub const getMinGain = ma_sound_get_min_gain; - extern fn ma_sound_get_min_gain(sound: Sound) f32; - - pub const setMaxGain = ma_sound_set_max_gain; - extern fn ma_sound_set_max_gain(sound: Sound, max_gain: f32) void; - - pub const getMaxGain = ma_sound_get_max_gain; - extern fn ma_sound_get_max_gain(sound: Sound) f32; - - pub const setMinDistance = ma_sound_set_min_distance; - extern fn ma_sound_set_min_distance(sound: Sound, min_distance: f32) void; - - pub const getMinDistance = ma_sound_get_min_distance; - extern fn ma_sound_get_min_distance(sound: Sound) f32; - - pub const setMaxDistance = ma_sound_set_max_distance; - extern fn ma_sound_set_max_distance(sound: Sound, max_distance: f32) void; - - pub const getMaxDistance = ma_sound_get_max_distance; - extern fn ma_sound_get_max_distance(sound: Sound) f32; - - pub const setCone = ma_sound_set_cone; - extern fn ma_sound_set_cone(sound: Sound, inner_radians: f32, outer_radians: f32, outer_gain: f32) void; - - pub const getCone = ma_sound_get_cone; - extern fn ma_sound_get_cone(sound: Sound, inner_radians: ?*f32, outer_radians: ?*f32, outer_gain: ?*f32) void; - - pub const setDopplerFactor = ma_sound_set_doppler_factor; - extern fn ma_sound_set_doppler_factor(sound: Sound, factor: f32) void; - - pub const getDopplerFactor = ma_sound_get_doppler_factor; - extern fn ma_sound_get_doppler_factor(sound: Sound) f32; - - pub const setDirectionalAttenuationFactor = ma_sound_set_directional_attenuation_factor; - extern fn ma_sound_set_directional_attenuation_factor(sound: Sound, factor: f32) void; - - pub const getDirectionalAttenuationFactor = ma_sound_get_directional_attenuation_factor; - extern fn ma_sound_get_directional_attenuation_factor(sound: Sound) f32; - - pub const setFadeInPcmFrames = ma_sound_set_fade_in_pcm_frames; - extern fn ma_sound_set_fade_in_pcm_frames( - sound: Sound, - volume_begin: f32, - volume_end: f32, - len_in_frames: u64, - ) void; - - pub const setFadeInMilliseconds = ma_sound_set_fade_in_milliseconds; - extern fn ma_sound_set_fade_in_milliseconds( - sound: Sound, - volume_begin: f32, - volume_end: f32, - len_in_ms: u64, - ) void; - - pub const getCurrentFadeVolume = ma_sound_get_current_fade_volume; - extern fn ma_sound_get_current_fade_volume(sound: Sound) f32; - - pub const setStartTimeInPcmFrames = ma_sound_set_start_time_in_pcm_frames; - extern fn ma_sound_set_start_time_in_pcm_frames(sound: Sound, abs_global_time_in_frames: u64) void; - - pub const setStartTimeInMilliseconds = ma_sound_set_start_time_in_milliseconds; - extern fn ma_sound_set_start_time_in_milliseconds(sound: Sound, abs_global_time_in_ms: u64) void; - - pub const setStopTimeInPcmFrames = ma_sound_set_stop_time_in_pcm_frames; - extern fn ma_sound_set_stop_time_in_pcm_frames(sound: Sound, abs_global_time_in_frames: u64) void; - - pub const setStopTimeInMilliseconds = ma_sound_set_stop_time_in_milliseconds; - extern fn ma_sound_set_stop_time_in_milliseconds(sound: Sound, abs_global_time_in_ms: u64) void; - - pub fn isPlaying(sound: Sound) bool { - return ma_sound_is_playing(sound) != 0; - } - extern fn ma_sound_is_playing(sound: Sound) Bool32; - - pub const getTimeInPcmFrames = ma_sound_get_time_in_pcm_frames; - extern fn ma_sound_get_time_in_pcm_frames(sound: Sound) u64; - - pub fn setLooping(sound: Sound, looping: bool) void { - ma_sound_set_looping(sound, @boolToInt(looping)); - } - extern fn ma_sound_set_looping(sound: Sound, looping: Bool32) void; - - pub fn isLooping(sound: Sound) bool { - return ma_sound_is_looping(sound) != 0; - } - extern fn ma_sound_is_looping(sound: Sound) Bool32; - - pub fn isAtEnd(sound: Sound) bool { - return ma_sound_at_end(sound) != 0; - } - extern fn ma_sound_at_end(sound: Sound) Bool32; - - pub fn seekToPcmFrame(sound: Sound, frame_index: u64) Error!void { - try maybeError(ma_sound_seek_to_pcm_frame(sound, frame_index)); - } - extern fn ma_sound_seek_to_pcm_frame(sound: Sound, frame_index: u64) Result; - - pub fn getDataFormat( - sound: Sound, - format: ?*Format, - channels: ?*u32, - sample_rate: ?*u32, - channel_map: ?[]Channel, - ) Error!void { - try maybeError(ma_sound_get_data_format( - sound, - format, - channels, - sample_rate, - if (channel_map) |chm| chm.ptr else null, - if (channel_map) |chm| chm.len else 0, - )); - } - extern fn ma_sound_get_data_format( - sound: Sound, - format: ?*Format, - channels: ?*u32, - sample_rate: ?*u32, - channel_map: ?[*]Channel, - channel_map_cap: usize, - ) Result; - - pub fn getCursorInPcmFrames(sound: Sound) Error!u64 { - var cursor: u64 = 0; - try maybeError(ma_sound_get_cursor_in_pcm_frames(sound, &cursor)); - return cursor; - } - extern fn ma_sound_get_cursor_in_pcm_frames(sound: Sound, cursor: *u64) Result; - - pub fn getLengthInPcmFrames(sound: Sound) Error!u64 { - var length: u64 = 0; - try maybeError(ma_sound_get_length_in_pcm_frames(sound, &length)); - return length; - } - extern fn ma_sound_get_length_in_pcm_frames(sound: Sound, length: *u64) Result; - - pub fn getCursorInSeconds(sound: Sound) Error!f32 { - var cursor: f32 = 0.0; - try maybeError(ma_sound_get_cursor_in_seconds(sound, &cursor)); - return cursor; - } - extern fn ma_sound_get_cursor_in_seconds(sound: Sound, cursor: *f32) Result; - - pub fn getLengthInSeconds(sound: Sound) Error!f32 { - var length: f32 = 0.0; - try maybeError(ma_sound_get_length_in_seconds(sound, &length)); - return length; - } - extern fn ma_sound_get_length_in_seconds(sound: Sound, length: *f32) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Sound Group -// -//-------------------------------------------------------------------------------------------------- -pub const SoundGroup = *align(@sizeOf(usize)) SoundGroupImpl; - -const SoundGroupImpl = opaque { - pub usingnamespace NodeImpl.Methods(SoundGroup); - - fn create(engine: Engine, flags: SoundFlags, parent: ?SoundGroup) Error!SoundGroup { - var handle: ?SoundGroup = null; - try maybeError(zaudioSoundGroupCreate(engine, flags, parent, &handle)); - return handle.?; - } - extern fn zaudioSoundGroupCreate( - engine: Engine, - flags: SoundFlags, - parent: ?SoundGroup, - out_handle: ?*?*SoundGroupImpl, - ) Result; - - pub const destroy = zaudioSoundGroupDestroy; - extern fn zaudioSoundGroupDestroy(handle: SoundGroup) void; - - pub const getEngine = ma_sound_group_get_engine; - extern fn ma_sound_group_get_engine(sound: SoundGroup) Engine; - - pub fn start(sound: SoundGroup) Error!void { - try maybeError(ma_sound_group_start(sound)); - } - extern fn ma_sound_group_start(sound: SoundGroup) Result; - - pub fn stop(sound: SoundGroup) Error!void { - try maybeError(ma_sound_group_stop(sound)); - } - extern fn ma_sound_group_stop(sound: SoundGroup) Result; - - pub const setVolume = ma_sound_group_set_volume; - extern fn ma_sound_group_set_volume(sound: SoundGroup, volume: f32) void; - - pub const getVolume = ma_sound_group_get_volume; - extern fn ma_sound_group_get_volume(sound: SoundGroup) f32; - - pub const setPan = ma_sound_group_set_pan; - extern fn ma_sound_group_set_pan(sound: SoundGroup, pan: f32) void; - - pub const getPan = ma_sound_group_get_pan; - extern fn ma_sound_group_get_pan(sound: SoundGroup) f32; - - pub const setPanMode = ma_sound_group_set_pan_mode; - extern fn ma_sound_group_set_pan_mode(sound: SoundGroup, pan_mode: PanMode) void; - - pub const getPanMode = ma_sound_group_get_pan_mode; - extern fn ma_sound_group_get_pan_mode(sound: SoundGroup) PanMode; - - pub const setPitch = ma_sound_group_set_pitch; - extern fn ma_sound_group_set_pitch(sound: SoundGroup, pitch: f32) void; - - pub const getPitch = ma_sound_group_get_pitch; - extern fn ma_sound_group_get_pitch(sound: SoundGroup) f32; - - pub fn setSpatializationEnabled(sound: SoundGroup, enabled: bool) void { - ma_sound_group_set_spatialization_enabled(sound, @boolToInt(enabled)); - } - extern fn ma_sound_group_set_spatialization_enabled(sound: SoundGroup, enabled: Bool32) void; - - pub fn isSpatializationEnabled(sound: SoundGroup) bool { - return ma_sound_group_is_spatialization_enabled(sound) != 0; - } - extern fn ma_sound_group_is_spatialization_enabled(sound: SoundGroup) Bool32; - - pub const setPinnedListenerIndex = ma_sound_group_set_pinned_listener_index; - extern fn ma_sound_group_set_pinned_listener_index(sound: SoundGroup, index: u32) void; - - pub const getPinnedListenerIndex = ma_sound_group_get_pinned_listener_index; - extern fn ma_sound_group_get_pinned_listener_index(sound: SoundGroup) u32; - - pub const getListenerIndex = ma_sound_group_get_listener_index; - extern fn ma_sound_group_get_listener_index(sound: SoundGroup) u32; - - pub fn getDirectionToListener(sound: SoundGroup) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_sound_group_get_direction_to_listener(sound, &v); - return v; - } - extern fn WA_ma_sound_group_get_direction_to_listener(sound: SoundGroup, vout: *[3]f32) void; - - pub fn setPosition(sound: SoundGroup, v: [3]f32) void { - ma_sound_group_set_position(sound, v[0], v[1], v[2]); - } - extern fn ma_sound_group_set_position(sound: SoundGroup, x: f32, y: f32, z: f32) void; - - pub fn getPosition(sound: SoundGroup) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_sound_group_get_position(sound, &v); - return v; - } - extern fn WA_ma_sound_group_get_position(sound: SoundGroup, vout: *[3]f32) void; - - pub fn setDirection(sound: SoundGroup, v: [3]f32) void { - ma_sound_group_set_direction(sound, v[0], v[1], v[2]); - } - extern fn ma_sound_group_set_direction(sound: SoundGroup, x: f32, y: f32, z: f32) void; - - pub fn getDirection(sound: SoundGroup) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_sound_group_get_direction(sound, &v); - return v; - } - extern fn WA_ma_sound_group_get_direction(sound: SoundGroup, vout: *[3]f32) void; - - pub fn setVelocity(sound: SoundGroup, v: [3]f32) void { - ma_sound_group_set_velocity(sound, v[0], v[1], v[2]); - } - extern fn ma_sound_group_set_velocity(sound: SoundGroup, x: f32, y: f32, z: f32) void; - - pub fn getVelocity(sound: SoundGroup) [3]f32 { - var v: [3]f32 = undefined; - WA_ma_sound_group_get_velocity(sound, &v); - return v; - } - extern fn WA_ma_sound_group_get_velocity(sound: SoundGroup, vout: *[3]f32) void; - - pub const setAttenuationModel = ma_sound_group_set_attenuation_model; - extern fn ma_sound_group_set_attenuation_model(sound: SoundGroup, model: AttenuationModel) void; - - pub const getAttenuationModel = ma_sound_group_get_attenuation_model; - extern fn ma_sound_group_get_attenuation_model(sound: SoundGroup) AttenuationModel; - - pub const setPositioning = ma_sound_group_set_positioning; - extern fn ma_sound_group_set_positioning(sound: SoundGroup, pos: Positioning) void; - - pub const getPositioning = ma_sound_group_get_positioning; - extern fn ma_sound_group_get_positioning(sound: SoundGroup) Positioning; - - pub const setRolloff = ma_sound_group_set_rolloff; - extern fn ma_sound_group_set_rolloff(sound: SoundGroup, rolloff: f32) void; - - pub const getRolloff = ma_sound_group_get_rolloff; - extern fn ma_sound_group_get_rolloff(sound: SoundGroup) f32; - - pub const setMinGain = ma_sound_group_set_min_gain; - extern fn ma_sound_group_set_min_gain(sound: SoundGroup, min_gain: f32) void; - - pub const getMinGain = ma_sound_group_get_min_gain; - extern fn ma_sound_group_get_min_gain(sound: SoundGroup) f32; - - pub const setMaxGain = ma_sound_group_set_max_gain; - extern fn ma_sound_group_set_max_gain(sound: SoundGroup, max_gain: f32) void; - - pub const getMaxGain = ma_sound_group_get_max_gain; - extern fn ma_sound_group_get_max_gain(sound: SoundGroup) f32; - - pub const setMinDistance = ma_sound_group_set_min_distance; - extern fn ma_sound_group_set_min_distance(sound: SoundGroup, min_distance: f32) void; - - pub const getMinDistance = ma_sound_group_get_min_distance; - extern fn ma_sound_group_get_min_distance(sound: SoundGroup) f32; - - pub const setMaxDistance = ma_sound_group_set_max_distance; - extern fn ma_sound_group_set_max_distance(sound: SoundGroup, max_distance: f32) void; - - pub const getMaxDistance = ma_sound_group_get_max_distance; - extern fn ma_sound_group_get_max_distance(sound: SoundGroup) f32; - - pub const setCone = ma_sound_group_set_cone; - extern fn ma_sound_group_set_cone( - sound: SoundGroup, - inner_radians: f32, - outer_radians: f32, - outer_gain: f32, - ) void; - - pub const getCone = ma_sound_group_get_cone; - extern fn ma_sound_group_get_cone( - sound: SoundGroup, - inner_radians: ?*f32, - outer_radians: ?*f32, - outer_gain: ?*f32, - ) void; - - pub const setDopplerFactor = ma_sound_group_set_doppler_factor; - extern fn ma_sound_group_set_doppler_factor(sound: SoundGroup, factor: f32) void; - - pub const getDopplerFactor = ma_sound_group_get_doppler_factor; - extern fn ma_sound_group_get_doppler_factor(sound: SoundGroup) f32; - - pub const setDirectionalAttenuationFactor = ma_sound_group_set_directional_attenuation_factor; - extern fn ma_sound_group_set_directional_attenuation_factor(sound: SoundGroup, factor: f32) void; - - pub const getDirectionalAttenuationFactor = ma_sound_group_get_directional_attenuation_factor; - extern fn ma_sound_group_get_directional_attenuation_factor(sound: SoundGroup) f32; - - pub const setFadeInPcmFrames = ma_sound_group_set_fade_in_pcm_frames; - extern fn ma_sound_group_set_fade_in_pcm_frames( - sound: SoundGroup, - volume_begin: f32, - volume_end: f32, - len_in_frames: u64, - ) void; - - pub const setFadeInMilliseconds = ma_sound_group_set_fade_in_milliseconds; - extern fn ma_sound_group_set_fade_in_milliseconds( - sound: SoundGroup, - volume_begin: f32, - volume_end: f32, - len_in_ms: u64, - ) void; - - pub const getCurrentFadeVolume = ma_sound_group_get_current_fade_volume; - extern fn ma_sound_group_get_current_fade_volume(sound: SoundGroup) f32; - - pub const setStartTimeInPcmFrames = ma_sound_group_set_start_time_in_pcm_frames; - extern fn ma_sound_group_set_start_time_in_pcm_frames(sound: SoundGroup, abs_global_time_in_frames: u64) void; - - pub const setStartTimeInMilliseconds = ma_sound_group_set_start_time_in_milliseconds; - extern fn ma_sound_group_set_start_time_in_milliseconds(sound: SoundGroup, abs_global_time_in_ms: u64) void; - - pub const setStopTimeInPcmFrames = ma_sound_group_set_stop_time_in_pcm_frames; - extern fn ma_sound_group_set_stop_time_in_pcm_frames(sound: SoundGroup, abs_global_time_in_frames: u64) void; - - pub const setStopTimeInMilliseconds = ma_sound_group_set_stop_time_in_milliseconds; - extern fn ma_sound_group_set_stop_time_in_milliseconds(sound: SoundGroup, abs_global_time_in_ms: u64) void; - - pub fn isPlaying(sound: SoundGroup) bool { - return ma_sound_group_is_playing(sound) != 0; - } - extern fn ma_sound_group_is_playing(sound: SoundGroup) Bool32; - - pub const getTimeInPcmFrames = ma_sound_group_get_time_in_pcm_frames; - extern fn ma_sound_group_get_time_in_pcm_frames(sound: SoundGroup) u64; -}; -//-------------------------------------------------------------------------------------------------- -// -// Fence -// -//-------------------------------------------------------------------------------------------------- -pub const Fence = *align(@sizeOf(usize)) FenceImpl; - -pub fn createFence() Error!Fence { - var handle: ?Fence = null; - try maybeError(zaudioFenceCreate(&handle)); - return handle.?; -} -extern fn zaudioFenceCreate(out_handle: ?*?*FenceImpl) Result; - -const FenceImpl = opaque { - pub const destroy = zaudioFenceDestroy; - extern fn zaudioFenceDestroy(handle: Fence) void; - - pub fn acquire(fence: Fence) Error!void { - try maybeError(ma_fence_acquire(fence)); - } - extern fn ma_fence_acquire(fence: Fence) Result; - - pub fn release(fence: Fence) Error!void { - try maybeError(ma_fence_release(fence)); - } - extern fn ma_fence_release(fence: Fence) Result; - - pub fn wait(fence: Fence) Error!void { - try maybeError(ma_fence_wait(fence)); - } - extern fn ma_fence_wait(fence: Fence) Result; -}; -//-------------------------------------------------------------------------------------------------- -// -// Tests -// -//-------------------------------------------------------------------------------------------------- -const expect = std.testing.expect; - -test "zaudio.engine.basic" { - const engine = try createEngine(null); - defer engine.destroy(); - - try engine.setTime(engine.getTime()); - - _ = engine.getChannels(); - _ = engine.getSampleRate(); - _ = engine.getListenerCount(); - _ = engine.findClosestListener(.{ 0.0, 0.0, 0.0 }); - - try engine.start(); - try engine.stop(); - try engine.start(); - try engine.setVolume(1.0); - try engine.setGainDb(1.0); - - engine.setListenerEnabled(0, true); - try expect(engine.isListenerEnabled(0) == true); - - engine.setListenerPosition(0, .{ 1.0, 2.0, 3.0 }); - { - const pos = engine.getListenerPosition(0); - try expect(pos[0] == 1.0 and pos[1] == 2.0 and pos[2] == 3.0); - } - - try expect(engine.getDevice() != null); - _ = engine.getResourceManager(); - _ = engine.getLog(); - _ = engine.getEndpoint(); - - const hpf_node = try engine.createHpfNode(HpfNodeConfig.init( - engine.getChannels(), - engine.getSampleRate(), - 1000.0, - 2, - )); - defer hpf_node.destroy(); - try hpf_node.attachOutputBus(0, engine.getEndpoint(), 0); -} - -test "zaudio.soundgroup.basic" { - const engine = try createEngine(null); - defer engine.destroy(); - - const sgroup = try engine.createSoundGroup(.{}, null); - defer sgroup.destroy(); - - try expect(sgroup.getEngine() == engine); - - try sgroup.start(); - try sgroup.stop(); - try sgroup.start(); - - _ = sgroup.getNumInputChannels(0); - _ = sgroup.getNumOutputChannels(0); - _ = sgroup.getNumInputBuses(); - _ = sgroup.getNumOutputBuses(); - - sgroup.setVolume(0.5); - try expect(sgroup.getVolume() == 0.5); - - sgroup.setPan(0.25); - try expect(sgroup.getPan() == 0.25); - - const sdir = [3]f32{ 1.0, 2.0, 3.0 }; - sgroup.setDirection(sdir); - { - const gdir = sgroup.getDirection(); - expect(sdir[0] == gdir[0] and sdir[1] == gdir[1] and sdir[2] == gdir[2]) catch |err| { - std.debug.print("Direction is: {any} should be: {any}\n", .{ gdir, sdir }); - return err; - }; - } -} - -test "zaudio.fence.basic" { - const fence = try createFence(); - defer fence.destroy(); - - try fence.acquire(); - try fence.release(); - try fence.wait(); -} - -test "zaudio.sound.basic" { - const engine = try createEngine(null); - defer engine.destroy(); - - var config = SoundConfig.init(); - config.channels_in = 1; - const sound = try engine.createSound(config); - defer sound.destroy(); - - _ = sound.getNumInputChannels(0); - _ = sound.getNumOutputChannels(0); - _ = sound.getNumInputBuses(); - _ = sound.getNumOutputBuses(); - - sound.setVolume(0.25); - try expect(sound.getVolume() == 0.25); - - sound.setPanMode(.pan); - try expect(sound.getPanMode() == .pan); - - sound.setLooping(false); - try expect(sound.isLooping() == false); - - sound.setPitch(0.5); - try expect(sound.getPitch() == 0.5); - - try expect(sound.getNodeGraph() == engine.asNodeGraph()); - - var format: Format = .unknown; - var num_channels: u32 = 0; - var sample_rate: u32 = 0; - try sound.getDataFormat(&format, &num_channels, &sample_rate, null); - try expect(num_channels == 1); - try expect(sample_rate > 0); - try expect(format != .unknown); -} - -test "zaudio.device.basic" { - var config = DeviceConfig.init(.playback); - config.playback.format = .float32; - config.playback.channels = 2; - config.sample_rate = 48_000; - const device = try createDevice(null, config); - defer device.destroy(); - try device.start(); - try expect(device.getState() == .started or device.getState() == .starting); - try device.stop(); - try expect(device.getState() == .stopped or device.getState() == .stopping); - const context = device.getContext(); - _ = context; - const log = device.getLog(); - _ = log; - try device.setMasterVolume(0.1); - try expect((try device.getMasterVolume()) == 0.1); -} - -test "zaudio.node_graph.basic" { - const config = NodeGraphConfig.init(2); - const node_graph = try createNodeGraph(config); - defer node_graph.destroy(); - _ = node_graph.getTime(); -} -//-------------------------------------------------------------------------------------------------- diff --git a/libs/zaudio/system_sdk.zig b/libs/zaudio/system_sdk.zig deleted file mode 100644 index 68e85977e..000000000 --- a/libs/zaudio/system_sdk.zig +++ /dev/null @@ -1,300 +0,0 @@ -//! Mach system SDK inclusion -//! -//! This file contains all that you need to include the Mach system SDKs in your own build.zig, -//! allowing you to cross-compile most OpenGL/Vulkan applications with ease. -//! -//! The SDKs used by this script by default are: -//! -//! * Windows: https://github.com/hexops/sdk-windows-x86_64 (~7MB, updated DirectX headers for Zig/MinGW) -//! * Linux: https://github.com/hexops/sdk-linux-x86_64 (~40MB, X11, Wayland, etc. development libraries) -//! * MacOS (most frameworks you'd find in the XCode SDK): -//! * https://github.com/hexops/sdk-macos-11.3 (~160MB, default) -//! * https://github.com/hexops/sdk-macos-12.0 (~112MB, only if you specify a macOS 12 target triple.) -//! -//! You may supply your own SDKs via the Options struct if needed, although the Mach versions above -//! will generally work for most OpenGL/Vulkan applications. -//! -//! How it works: When `include` is called, the compilation target is detected. If it does not -//! already exist, the SDK repository for the target platform is cloned via `git clone`. If the -//! target is MacOS, an interactive license agreement prompt (agreeing to the XCode SDK terms) -//! will appear. You can also set the environment variable `AGREE=true` to dismiss this. -//! -//! Once downloaded, `include` will add the SDK library, header, etc. directions to the build step -//! so that you can just include and link against libraries/frameworks as if they were there, and -//! you may then cross-compile your code with ease. See https://github.com/hexops/mach-glfw for an -//! example. -//! -//! Best way to get this file in your own repository? We suggest just copying it, or importing it -//! from a project that includes it if you're using one (e.g. mach-glfw) -//! -//! version: Mar 4, 2022 - -const std = @import("std"); -const Builder = std.build.Builder; - -pub const Options = struct { - /// The github org to find repositories in. - github_org: []const u8 = "hexops", - - /// The MacOS 12 SDK repository name. - macos_sdk_12: []const u8 = "sdk-macos-12.0", - macos_sdk_12_revision: []const u8 = "14613b4917c7059dad8f3789f55bb13a2548f83d", - - /// The MacOS 11 SDK repository name. - macos_sdk_11: []const u8 = "sdk-macos-11.3", - macos_sdk_11_revision: []const u8 = "ccbaae84cc39469a6792108b24480a4806e09d59", - - /// The Linux x86-64 SDK repository name. - linux_x86_64: []const u8 = "sdk-linux-x86_64", - linux_x86_64_revision: []const u8 = "1c2ea7f968bff3fbe4ddb0b605eef6b626e181ea", - - /// The Linux aarch64 SDK repository name. - linux_aarch64: []const u8 = "sdk-linux-aarch64", - linux_aarch64_revision: []const u8 = "dbb05673a01050a937cbc8ad47a407111cac146c", - - /// The Windows x86-64 SDK repository name. - windows_x86_64: []const u8 = "sdk-windows-x86_64", - windows_x86_64_revision: []const u8 = "13dcda7fe3f1aec0fc6130527226ad7ae0f4b792", - - /// If true, the Builder.sysroot will set to the SDK path. This has the drawback of preventing - /// you from including headers, libraries, etc. from outside the SDK generally. However, it can - /// be useful in order to identify which libraries, headers, frameworks, etc. may be missing in - /// your SDK for cross compilation. - set_sysroot: bool = false, -}; - -pub fn include(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void { - const target = (std.zig.system.NativeTargetInfo.detect(step.target) catch unreachable).target; - switch (target.os.tag) { - .windows => includeSdkWindowsX8664(b, step, options), - .macos => includeSdkMacOS(b, step, options), - else => switch (target.cpu.arch) { // Assume Linux-like for now - .aarch64 => includeSdkLinuxAarch64(b, step, options), - else => includeSdkLinuxX8664(b, step, options), // Assume x86-64 for now - }, - } -} - -fn includeSdkMacOS(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void { - const target = (std.zig.system.NativeTargetInfo.detect(step.target) catch unreachable).target; - const mac_12 = target.os.version_range.semver.isAtLeast(.{ .major = 12, .minor = 0 }) orelse false; - const sdk_name = if (mac_12) options.macos_sdk_12 else options.macos_sdk_11; - const sdk_revision = if (mac_12) options.macos_sdk_12_revision else options.macos_sdk_11_revision; - const sdk_root_dir = getSdkRoot(b.allocator, step, options.github_org, sdk_name, sdk_revision) catch unreachable; - - if (options.set_sysroot) { - step.addFrameworkPath("/System/Library/Frameworks"); - step.addSystemIncludePath("/usr/include"); - step.addLibraryPath("/usr/lib"); - - var sdk_sysroot = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/" }) catch unreachable; - b.sysroot = sdk_sysroot; - return; - } - - var sdk_framework_dir = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/System/Library/Frameworks" }) catch unreachable; - step.addFrameworkPath(sdk_framework_dir); - - var sdk_include_dir = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/include" }) catch unreachable; - step.addSystemIncludePath(sdk_include_dir); - - var sdk_lib_dir = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/lib" }) catch unreachable; - step.addLibraryPath(sdk_lib_dir); -} - -fn includeSdkLinuxX8664(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void { - const sdk_root_dir = getSdkRoot(b.allocator, step, options.github_org, options.linux_x86_64, options.linux_x86_64_revision) catch unreachable; - - if (options.set_sysroot) { - var sdk_sysroot = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/" }) catch unreachable; - b.sysroot = sdk_sysroot; - return; - } - - var sdk_root_includes = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/include" }) catch unreachable; - var wayland_protocols_include = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/share/wayland-generated" }) catch unreachable; - var sdk_root_libs = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/lib/x86_64-linux-gnu" }) catch unreachable; - defer { - b.allocator.free(sdk_root_includes); - b.allocator.free(wayland_protocols_include); - b.allocator.free(sdk_root_libs); - } - step.addSystemIncludePath(sdk_root_includes); - step.addSystemIncludePath(wayland_protocols_include); - step.addLibraryPath(sdk_root_libs); -} - -fn includeSdkLinuxAarch64(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void { - const sdk_root_dir = getSdkRoot(b.allocator, step, options.github_org, options.linux_aarch64, options.linux_aarch64_revision) catch unreachable; - - if (options.set_sysroot) { - var sdk_sysroot = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/" }) catch unreachable; - b.sysroot = sdk_sysroot; - return; - } - - var sdk_root_includes = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/include" }) catch unreachable; - var wayland_protocols_include = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/share/wayland-generated" }) catch unreachable; - var sdk_root_libs = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/lib/aarch64-linux-gnu" }) catch unreachable; - defer { - b.allocator.free(sdk_root_includes); - b.allocator.free(wayland_protocols_include); - b.allocator.free(sdk_root_libs); - } - step.addSystemIncludePath(sdk_root_includes); - step.addSystemIncludePath(wayland_protocols_include); - step.addLibraryPath(sdk_root_libs); -} - -fn includeSdkWindowsX8664(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void { - const sdk_root_dir = getSdkRoot(b.allocator, step, options.github_org, options.windows_x86_64, options.windows_x86_64_revision) catch unreachable; - - if (options.set_sysroot) { - // We have no sysroot for Windows, but we still set one to prevent inclusion of other system - // libs (if set_sysroot is set, don't want to accidentally depend on system libs.) - var sdk_sysroot = std.fs.path.join(b.allocator, &.{sdk_root_dir}) catch unreachable; - b.sysroot = sdk_sysroot; - } - - var sdk_includes = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "include" }) catch unreachable; - var sdk_libs = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "lib" }) catch unreachable; - defer { - b.allocator.free(sdk_includes); - b.allocator.free(sdk_libs); - } - - step.addIncludePath(sdk_includes); - step.addLibraryPath(sdk_libs); -} - -var cached_sdk_roots: ?std.AutoHashMap(*std.build.LibExeObjStep, []const u8) = null; - -/// returns the SDK root path, determining it iff necessary. In a real application, this may be -/// tens or hundreds of times and so the result is cached in-memory (this also means the result -/// cannot be freed until the result will never be used again, which is fine as the Zig build system -/// Builder.allocator is an arena, you don't need to free.) -fn getSdkRoot(allocator: std.mem.Allocator, step: *std.build.LibExeObjStep, org: []const u8, name: []const u8, revision: []const u8) ![]const u8 { - if (cached_sdk_roots == null) { - cached_sdk_roots = std.AutoHashMap(*std.build.LibExeObjStep, []const u8).init(allocator); - } - - var entry = try cached_sdk_roots.?.getOrPut(step); - if (entry.found_existing) return entry.value_ptr.*; - const sdk_root = try determineSdkRoot(allocator, org, name, revision); - entry.value_ptr.* = sdk_root; - return sdk_root; -} - -fn determineSdkRoot(allocator: std.mem.Allocator, org: []const u8, name: []const u8, revision: []const u8) ![]const u8 { - // Find the directory where the SDK should be located. We'll consider two locations: - // - // 1. $SDK_PATH/ (if set, e.g. for testing changes to SDKs easily) - // 2. / (default) - // - // Where `` is the name of the SDK, e.g. `sdk-macos-12.0`. - var sdk_root_dir: []const u8 = undefined; - var sdk_path_dir: []const u8 = undefined; - var custom_sdk_path = false; - if (std.process.getEnvVarOwned(allocator, "SDK_PATH")) |sdk_path| { - custom_sdk_path = true; - sdk_path_dir = sdk_path; - sdk_root_dir = try std.fs.path.join(allocator, &.{ sdk_path, name }); - } else |err| switch (err) { - error.EnvironmentVariableNotFound => { - sdk_path_dir = try std.fs.getAppDataDir(allocator, org); - sdk_root_dir = try std.fs.path.join(allocator, &.{ sdk_path_dir, name }); - }, - else => |e| return e, - } - - ensureGit(allocator); - - // If the SDK exists, return it. Otherwise, clone it. - if (std.fs.openDirAbsolute(sdk_root_dir, .{})) { - const current_revision = try getCurrentGitRevision(allocator, sdk_root_dir); - if (!std.mem.eql(u8, current_revision, revision)) { - // Update the SDK to the target revision. This may be either forward or backwards in - // history (e.g. if building an old project) and so we use a hard reset. - // - // No reset is performed if specifying a custom SDK_PATH, as that is a development/debug - // option and could wipe out dev history. - exec(allocator, &[_][]const u8{ "git", "fetch" }, sdk_root_dir) catch |err| std.debug.print("warning: failed to check for updates to {s}/{s}: {s}\n", .{ org, name, @errorName(err) }); - if (!custom_sdk_path) try exec(allocator, &[_][]const u8{ "git", "reset", "--quiet", "--hard", revision }, sdk_root_dir); - } - return sdk_root_dir; - } else |err| return switch (err) { - error.FileNotFound => { - std.log.info("cloning required sdk..\ngit clone https://github.com/{s}/{s} '{s}'..\n", .{ org, name, sdk_root_dir }); - if (std.mem.startsWith(u8, name, "sdk-macos-")) { - if (!try confirmAppleSDKAgreement(allocator)) @panic("cannot continue"); - } - try std.fs.cwd().makePath(sdk_path_dir); - - var buf: [1000]u8 = undefined; - var repo_url_fbs = std.io.fixedBufferStream(&buf); - try std.fmt.format(repo_url_fbs.writer(), "https://github.com/{s}/{s}", .{ org, name }); - - try exec(allocator, &[_][]const u8{ "git", "clone", "-c", "core.longpaths=true", repo_url_fbs.getWritten() }, sdk_path_dir); - return sdk_root_dir; - }, - else => err, - }; -} - -fn exec(allocator: std.mem.Allocator, argv: []const []const u8, cwd: []const u8) !void { - var child = std.ChildProcess.init(argv, allocator); - child.cwd = cwd; - _ = try child.spawnAndWait(); -} - -fn getCurrentGitRevision(allocator: std.mem.Allocator, cwd: []const u8) ![]const u8 { - const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = &.{ "git", "rev-parse", "HEAD" }, .cwd = cwd }); - allocator.free(result.stderr); - if (result.stdout.len > 0) return result.stdout[0 .. result.stdout.len - 1]; // trim newline - return result.stdout; -} - -fn confirmAppleSDKAgreement(allocator: std.mem.Allocator) !bool { - if (std.process.getEnvVarOwned(allocator, "AGREE")) |agree| { - return std.mem.eql(u8, agree, "true"); - } else |err| switch (err) { - error.EnvironmentVariableNotFound => {}, - else => |e| return e, - } - - const stdin = std.io.getStdIn().reader(); - const stdout = std.io.getStdOut().writer(); - var buf: [10]u8 = undefined; - try stdout.print("This SDK is distributed under the terms of the Xcode and Apple SDKs agreement:\n", .{}); - try stdout.print(" https://www.apple.com/legal/sla/docs/xcode.pdf\n", .{}); - try stdout.print("\n", .{}); - try stdout.print("Do you agree to those terms? [Y/n] ", .{}); - if (try stdin.readUntilDelimiterOrEof(buf[0..], '\n')) |user_input| { - try stdout.print("\n", .{}); - var in = user_input; - if (in.len > 0 and in[in.len - 1] == '\r') in = in[0 .. in.len - 1]; - return std.mem.eql(u8, in, "y") or std.mem.eql(u8, in, "Y") or std.mem.eql(u8, in, "yes") or std.mem.eql(u8, in, ""); - } else { - return false; - } -} - -fn ensureGit(allocator: std.mem.Allocator) void { - const argv = &[_][]const u8{ "git", "--version" }; - const result = std.ChildProcess.exec(.{ - .allocator = allocator, - .argv = argv, - .cwd = ".", - }) catch { // e.g. FileNotFound - std.log.err("mach: error: 'git --version' failed. Is git not installed?", .{}); - std.process.exit(1); - }; - defer { - allocator.free(result.stderr); - allocator.free(result.stdout); - } - if (result.term.Exited != 0) { - std.log.err("mach: error: 'git --version' failed. Is git not installed?", .{}); - std.process.exit(1); - } -} diff --git a/libs/zbullet/src/zbullet.zig b/libs/zbullet/src/zbullet.zig index 6d2e4070f..9fb0af307 100644 --- a/libs/zbullet/src/zbullet.zig +++ b/libs/zbullet/src/zbullet.zig @@ -18,8 +18,8 @@ pub const Body = *align(@sizeOf(usize)) BodyImpl; pub const Constraint = *align(@sizeOf(usize)) ConstraintImpl; pub const Point2PointConstraint = *align(@sizeOf(usize)) Point2PointConstraintImpl; -pub const AllocFn = *const fn (size: usize, alignment: i32) callconv(.C) ?*anyopaque; -pub const FreeFn = *const fn (ptr: ?*anyopaque) callconv(.C) void; +pub const AllocFn = fn (size: usize, alignment: i32) callconv(.C) ?*anyopaque; +pub const FreeFn = fn (ptr: ?*anyopaque) callconv(.C) void; extern fn cbtAlignedAllocSetCustomAligned( alloc: ?AllocFn, @@ -923,14 +923,14 @@ pub const DebugMode = packed struct { }; pub const DebugDraw = extern struct { - const DrawLine1Fn = *const fn ( + const DrawLine1Fn = fn ( ?*anyopaque, *const [3]f32, *const [3]f32, *const [3]f32, ) callconv(.C) void; - const DrawLine2Fn = *const fn ( + const DrawLine2Fn = fn ( ?*anyopaque, *const [3]f32, *const [3]f32, @@ -938,7 +938,7 @@ pub const DebugDraw = extern struct { *const [3]f32, ) callconv(.C) void; - const DrawContactPointFn = *const fn ( + const DrawContactPointFn = fn ( ?*anyopaque, *const [3]f32, *const [3]f32, diff --git a/libs/zglfw/README.md b/libs/zglfw/README.md deleted file mode 100644 index 20b6a2d18..000000000 --- a/libs/zglfw/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# zglfw - GLFW bindings - -## Getting started - -Copy `zglfw` folder to a `libs` subdirectory of the root of your project. - -Then in your `build.zig` add: -```zig -const zglfw = @import("libs/zglfw/build.zig"); - -pub fn build(b: *std.build.Builder) void { - ... - exe.addPackage(zglfw.pkg); - - zglfw.link(exe); -} -``` -Now in your code you may import and use `zglfw`: -```zig -const zglfw = @import("zglfw"); - -pub fn main() !void { - ... -} -``` diff --git a/libs/zglfw/build.zig b/libs/zglfw/build.zig deleted file mode 100644 index 1e71cea29..000000000 --- a/libs/zglfw/build.zig +++ /dev/null @@ -1,112 +0,0 @@ -const std = @import("std"); -const system_sdk = @import("system_sdk.zig"); - -pub fn build(_: *std.build.Builder) void {} - -pub fn buildTests( - b: *std.build.Builder, - build_mode: std.builtin.Mode, - target: std.zig.CrossTarget, -) *std.build.LibExeObjStep { - const tests = b.addTest(pkg.source.path); - tests.setBuildMode(build_mode); - tests.setTarget(target); - link(tests); - return tests; -} - -pub const pkg = std.build.Pkg{ - .name = "zglfw", - .source = .{ .path = thisDir() ++ "/src/zglfw.zig" }, -}; - -pub fn link(exe: *std.build.LibExeObjStep) void { - exe.addIncludePath(thisDir() ++ "/libs/glfw/include"); - exe.linkSystemLibraryName("c"); - system_sdk.include(exe.builder, exe, .{}); - - const target = (std.zig.system.NativeTargetInfo.detect(exe.target) catch unreachable).target; - - const src_dir = thisDir() ++ "/libs/glfw/src/"; - - switch (target.os.tag) { - .windows => { - exe.linkSystemLibraryName("gdi32"); - exe.linkSystemLibraryName("user32"); - exe.linkSystemLibraryName("shell32"); - exe.addCSourceFiles(&.{ - src_dir ++ "monitor.c", - src_dir ++ "init.c", - src_dir ++ "vulkan.c", - src_dir ++ "input.c", - src_dir ++ "context.c", - src_dir ++ "window.c", - src_dir ++ "osmesa_context.c", - src_dir ++ "egl_context.c", - src_dir ++ "wgl_context.c", - src_dir ++ "win32_thread.c", - src_dir ++ "win32_init.c", - src_dir ++ "win32_monitor.c", - src_dir ++ "win32_time.c", - src_dir ++ "win32_joystick.c", - src_dir ++ "win32_window.c", - }, &.{"-D_GLFW_WIN32"}); - }, - .macos => { - exe.linkFramework("IOKit"); - exe.linkFramework("CoreFoundation"); - exe.linkFramework("Metal"); - exe.linkFramework("AppKit"); - exe.linkFramework("CoreServices"); - exe.linkFramework("CoreGraphics"); - exe.linkFramework("Foundation"); - exe.linkSystemLibraryName("objc"); - exe.addCSourceFiles(&.{ - src_dir ++ "monitor.c", - src_dir ++ "init.c", - src_dir ++ "vulkan.c", - src_dir ++ "input.c", - src_dir ++ "context.c", - src_dir ++ "window.c", - src_dir ++ "osmesa_context.c", - src_dir ++ "egl_context.c", - src_dir ++ "nsgl_context.m", - src_dir ++ "posix_thread.c", - src_dir ++ "cocoa_time.c", - src_dir ++ "cocoa_joystick.m", - src_dir ++ "cocoa_init.m", - src_dir ++ "cocoa_window.m", - src_dir ++ "cocoa_monitor.m", - }, &.{"-D_GLFW_COCOA"}); - }, - else => { - // We assume Linux (X11) - exe.linkSystemLibraryName("X11"); - exe.linkSystemLibraryName("xcb"); - exe.linkSystemLibraryName("Xau"); - exe.linkSystemLibraryName("Xdmcp"); - exe.addCSourceFiles(&.{ - src_dir ++ "monitor.c", - src_dir ++ "init.c", - src_dir ++ "vulkan.c", - src_dir ++ "input.c", - src_dir ++ "context.c", - src_dir ++ "window.c", - src_dir ++ "osmesa_context.c", - src_dir ++ "egl_context.c", - src_dir ++ "glx_context.c", - src_dir ++ "posix_time.c", - src_dir ++ "posix_thread.c", - src_dir ++ "linux_joystick.c", - src_dir ++ "xkb_unicode.c", - src_dir ++ "x11_init.c", - src_dir ++ "x11_window.c", - src_dir ++ "x11_monitor.c", - }, &.{"-D_GLFW_X11"}); - }, - } -} - -inline fn thisDir() []const u8 { - return comptime std.fs.path.dirname(@src().file) orelse "."; -} diff --git a/libs/zglfw/libs/glfw/LICENSE.md b/libs/zglfw/libs/glfw/LICENSE.md deleted file mode 100644 index 7494a3f68..000000000 --- a/libs/zglfw/libs/glfw/LICENSE.md +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2002-2006 Marcus Geelnard - -Copyright (c) 2006-2019 Camilla Löwy - -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. - diff --git a/libs/zglfw/libs/glfw/include/GLFW/glfw3.h b/libs/zglfw/libs/glfw/include/GLFW/glfw3.h deleted file mode 100644 index 31b201ae5..000000000 --- a/libs/zglfw/libs/glfw/include/GLFW/glfw3.h +++ /dev/null @@ -1,5912 +0,0 @@ -/************************************************************************* - * GLFW 3.3 - www.glfw.org - * A library for OpenGL, window and input - *------------------------------------------------------------------------ - * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2019 Camilla Löwy - * - * 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 _glfw3_h_ -#define _glfw3_h_ - -#ifdef __cplusplus -extern "C" { -#endif - - -/************************************************************************* - * Doxygen documentation - *************************************************************************/ - -/*! @file glfw3.h - * @brief The header of the GLFW 3 API. - * - * This is the header file of the GLFW 3 API. It defines all its types and - * declares all its functions. - * - * For more information about how to use this file, see @ref build_include. - */ -/*! @defgroup context Context reference - * @brief Functions and types related to OpenGL and OpenGL ES contexts. - * - * This is the reference documentation for OpenGL and OpenGL ES context related - * functions. For more task-oriented information, see the @ref context_guide. - */ -/*! @defgroup vulkan Vulkan support reference - * @brief Functions and types related to Vulkan. - * - * This is the reference documentation for Vulkan related functions and types. - * For more task-oriented information, see the @ref vulkan_guide. - */ -/*! @defgroup init Initialization, version and error reference - * @brief Functions and types related to initialization and error handling. - * - * This is the reference documentation for initialization and termination of - * the library, version management and error handling. For more task-oriented - * information, see the @ref intro_guide. - */ -/*! @defgroup input Input reference - * @brief Functions and types related to input handling. - * - * This is the reference documentation for input related functions and types. - * For more task-oriented information, see the @ref input_guide. - */ -/*! @defgroup monitor Monitor reference - * @brief Functions and types related to monitors. - * - * This is the reference documentation for monitor related functions and types. - * For more task-oriented information, see the @ref monitor_guide. - */ -/*! @defgroup window Window reference - * @brief Functions and types related to windows. - * - * This is the reference documentation for window related functions and types, - * including creation, deletion and event polling. For more task-oriented - * information, see the @ref window_guide. - */ - - -/************************************************************************* - * Compiler- and platform-specific preprocessor work - *************************************************************************/ - -/* If we are we on Windows, we want a single define for it. - */ -#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)) - #define _WIN32 -#endif /* _WIN32 */ - -/* Include because most Windows GLU headers need wchar_t and - * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. - * Include it unconditionally to avoid surprising side-effects. - */ -#include - -/* Include because it is needed by Vulkan and related functions. - * Include it unconditionally to avoid surprising side-effects. - */ -#include - -#if defined(GLFW_INCLUDE_VULKAN) - #include -#endif /* Vulkan header */ - -/* The Vulkan header may have indirectly included windows.h (because of - * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it. - */ - -/* It is customary to use APIENTRY for OpenGL function pointer declarations on - * all platforms. Additionally, the Windows OpenGL header needs APIENTRY. - */ -#if !defined(APIENTRY) - #if defined(_WIN32) - #define APIENTRY __stdcall - #else - #define APIENTRY - #endif - #define GLFW_APIENTRY_DEFINED -#endif /* APIENTRY */ - -/* Some Windows OpenGL headers need this. - */ -#if !defined(WINGDIAPI) && defined(_WIN32) - #define WINGDIAPI __declspec(dllimport) - #define GLFW_WINGDIAPI_DEFINED -#endif /* WINGDIAPI */ - -/* Some Windows GLU headers need this. - */ -#if !defined(CALLBACK) && defined(_WIN32) - #define CALLBACK __stdcall - #define GLFW_CALLBACK_DEFINED -#endif /* CALLBACK */ - -/* Include the chosen OpenGL or OpenGL ES headers. - */ -#if defined(GLFW_INCLUDE_ES1) - - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - -#elif defined(GLFW_INCLUDE_ES2) - - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - -#elif defined(GLFW_INCLUDE_ES3) - - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - -#elif defined(GLFW_INCLUDE_ES31) - - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - -#elif defined(GLFW_INCLUDE_ES32) - - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - -#elif defined(GLFW_INCLUDE_GLCOREARB) - - #if defined(__APPLE__) - - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif /*GLFW_INCLUDE_GLEXT*/ - - #else /*__APPLE__*/ - - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - - #endif /*__APPLE__*/ - -#elif defined(GLFW_INCLUDE_GLU) - - #if defined(__APPLE__) - - #if defined(GLFW_INCLUDE_GLU) - #include - #endif - - #else /*__APPLE__*/ - - #if defined(GLFW_INCLUDE_GLU) - #include - #endif - - #endif /*__APPLE__*/ - -#elif !defined(GLFW_INCLUDE_NONE) && \ - !defined(__gl_h_) && \ - !defined(__gles1_gl_h_) && \ - !defined(__gles2_gl2_h_) && \ - !defined(__gles2_gl3_h_) && \ - !defined(__gles2_gl31_h_) && \ - !defined(__gles2_gl32_h_) && \ - !defined(__gl_glcorearb_h_) && \ - !defined(__gl2_h_) /*legacy*/ && \ - !defined(__gl3_h_) /*legacy*/ && \ - !defined(__gl31_h_) /*legacy*/ && \ - !defined(__gl32_h_) /*legacy*/ && \ - !defined(__glcorearb_h_) /*legacy*/ && \ - !defined(__GL_H__) /*non-standard*/ && \ - !defined(__gltypes_h_) /*non-standard*/ && \ - !defined(__glee_h_) /*non-standard*/ - - #if defined(__APPLE__) - - #if !defined(GLFW_INCLUDE_GLEXT) - #define GL_GLEXT_LEGACY - #endif - #include - - #else /*__APPLE__*/ - - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - - #endif /*__APPLE__*/ - -#endif /* OpenGL and OpenGL ES headers */ - -#if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) - /* GLFW_DLL must be defined by applications that are linking against the DLL - * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW - * configuration header when compiling the DLL version of the library. - */ - #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined" -#endif - -/* GLFWAPI is used to declare public API functions for export - * from the DLL / shared library / dynamic library. - */ -#if defined(_WIN32) && defined(_GLFW_BUILD_DLL) - /* We are building GLFW as a Win32 DLL */ - #define GLFWAPI __declspec(dllexport) -#elif defined(_WIN32) && defined(GLFW_DLL) - /* We are calling a GLFW Win32 DLL */ - #define GLFWAPI __declspec(dllimport) -#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) - /* We are building GLFW as a Unix shared library */ - #define GLFWAPI __attribute__((visibility("default"))) -#else - #define GLFWAPI -#endif - - -/************************************************************************* - * GLFW API tokens - *************************************************************************/ - -/*! @name GLFW version macros - * @{ */ -/*! @brief The major version number of the GLFW header. - * - * The major version number of the GLFW header. This is incremented when the - * API is changed in non-compatible ways. - * @ingroup init - */ -#define GLFW_VERSION_MAJOR 3 -/*! @brief The minor version number of the GLFW header. - * - * The minor version number of the GLFW header. This is incremented when - * features are added to the API but it remains backward-compatible. - * @ingroup init - */ -#define GLFW_VERSION_MINOR 3 -/*! @brief The revision number of the GLFW header. - * - * The revision number of the GLFW header. This is incremented when a bug fix - * release is made that does not contain any API changes. - * @ingroup init - */ -#define GLFW_VERSION_REVISION 8 -/*! @} */ - -/*! @brief One. - * - * This is only semantic sugar for the number 1. You can instead use `1` or - * `true` or `_True` or `GL_TRUE` or `VK_TRUE` or anything else that is equal - * to one. - * - * @ingroup init - */ -#define GLFW_TRUE 1 -/*! @brief Zero. - * - * This is only semantic sugar for the number 0. You can instead use `0` or - * `false` or `_False` or `GL_FALSE` or `VK_FALSE` or anything else that is - * equal to zero. - * - * @ingroup init - */ -#define GLFW_FALSE 0 - -/*! @name Key and button actions - * @{ */ -/*! @brief The key or mouse button was released. - * - * The key or mouse button was released. - * - * @ingroup input - */ -#define GLFW_RELEASE 0 -/*! @brief The key or mouse button was pressed. - * - * The key or mouse button was pressed. - * - * @ingroup input - */ -#define GLFW_PRESS 1 -/*! @brief The key was held down until it repeated. - * - * The key was held down until it repeated. - * - * @ingroup input - */ -#define GLFW_REPEAT 2 -/*! @} */ - -/*! @defgroup hat_state Joystick hat states - * @brief Joystick hat states. - * - * See [joystick hat input](@ref joystick_hat) for how these are used. - * - * @ingroup input - * @{ */ -#define GLFW_HAT_CENTERED 0 -#define GLFW_HAT_UP 1 -#define GLFW_HAT_RIGHT 2 -#define GLFW_HAT_DOWN 4 -#define GLFW_HAT_LEFT 8 -#define GLFW_HAT_RIGHT_UP (GLFW_HAT_RIGHT | GLFW_HAT_UP) -#define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN) -#define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP) -#define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN) -/*! @} */ - -/*! @defgroup keys Keyboard keys - * @brief Keyboard key IDs. - * - * See [key input](@ref input_key) for how these are used. - * - * These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60), - * but re-arranged to map to 7-bit ASCII for printable keys (function keys are - * put in the 256+ range). - * - * The naming of the key codes follow these rules: - * - The US keyboard layout is used - * - Names of printable alpha-numeric characters are used (e.g. "A", "R", - * "3", etc.) - * - For non-alphanumeric characters, Unicode:ish names are used (e.g. - * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not - * correspond to the Unicode standard (usually for brevity) - * - Keys that lack a clear US mapping are named "WORLD_x" - * - For non-printable keys, custom names are used (e.g. "F4", - * "BACKSPACE", etc.) - * - * @ingroup input - * @{ - */ - -/* The unknown key */ -#define GLFW_KEY_UNKNOWN -1 - -/* Printable keys */ -#define GLFW_KEY_SPACE 32 -#define GLFW_KEY_APOSTROPHE 39 /* ' */ -#define GLFW_KEY_COMMA 44 /* , */ -#define GLFW_KEY_MINUS 45 /* - */ -#define GLFW_KEY_PERIOD 46 /* . */ -#define GLFW_KEY_SLASH 47 /* / */ -#define GLFW_KEY_0 48 -#define GLFW_KEY_1 49 -#define GLFW_KEY_2 50 -#define GLFW_KEY_3 51 -#define GLFW_KEY_4 52 -#define GLFW_KEY_5 53 -#define GLFW_KEY_6 54 -#define GLFW_KEY_7 55 -#define GLFW_KEY_8 56 -#define GLFW_KEY_9 57 -#define GLFW_KEY_SEMICOLON 59 /* ; */ -#define GLFW_KEY_EQUAL 61 /* = */ -#define GLFW_KEY_A 65 -#define GLFW_KEY_B 66 -#define GLFW_KEY_C 67 -#define GLFW_KEY_D 68 -#define GLFW_KEY_E 69 -#define GLFW_KEY_F 70 -#define GLFW_KEY_G 71 -#define GLFW_KEY_H 72 -#define GLFW_KEY_I 73 -#define GLFW_KEY_J 74 -#define GLFW_KEY_K 75 -#define GLFW_KEY_L 76 -#define GLFW_KEY_M 77 -#define GLFW_KEY_N 78 -#define GLFW_KEY_O 79 -#define GLFW_KEY_P 80 -#define GLFW_KEY_Q 81 -#define GLFW_KEY_R 82 -#define GLFW_KEY_S 83 -#define GLFW_KEY_T 84 -#define GLFW_KEY_U 85 -#define GLFW_KEY_V 86 -#define GLFW_KEY_W 87 -#define GLFW_KEY_X 88 -#define GLFW_KEY_Y 89 -#define GLFW_KEY_Z 90 -#define GLFW_KEY_LEFT_BRACKET 91 /* [ */ -#define GLFW_KEY_BACKSLASH 92 /* \ */ -#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */ -#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */ -#define GLFW_KEY_WORLD_1 161 /* non-US #1 */ -#define GLFW_KEY_WORLD_2 162 /* non-US #2 */ - -/* Function keys */ -#define GLFW_KEY_ESCAPE 256 -#define GLFW_KEY_ENTER 257 -#define GLFW_KEY_TAB 258 -#define GLFW_KEY_BACKSPACE 259 -#define GLFW_KEY_INSERT 260 -#define GLFW_KEY_DELETE 261 -#define GLFW_KEY_RIGHT 262 -#define GLFW_KEY_LEFT 263 -#define GLFW_KEY_DOWN 264 -#define GLFW_KEY_UP 265 -#define GLFW_KEY_PAGE_UP 266 -#define GLFW_KEY_PAGE_DOWN 267 -#define GLFW_KEY_HOME 268 -#define GLFW_KEY_END 269 -#define GLFW_KEY_CAPS_LOCK 280 -#define GLFW_KEY_SCROLL_LOCK 281 -#define GLFW_KEY_NUM_LOCK 282 -#define GLFW_KEY_PRINT_SCREEN 283 -#define GLFW_KEY_PAUSE 284 -#define GLFW_KEY_F1 290 -#define GLFW_KEY_F2 291 -#define GLFW_KEY_F3 292 -#define GLFW_KEY_F4 293 -#define GLFW_KEY_F5 294 -#define GLFW_KEY_F6 295 -#define GLFW_KEY_F7 296 -#define GLFW_KEY_F8 297 -#define GLFW_KEY_F9 298 -#define GLFW_KEY_F10 299 -#define GLFW_KEY_F11 300 -#define GLFW_KEY_F12 301 -#define GLFW_KEY_F13 302 -#define GLFW_KEY_F14 303 -#define GLFW_KEY_F15 304 -#define GLFW_KEY_F16 305 -#define GLFW_KEY_F17 306 -#define GLFW_KEY_F18 307 -#define GLFW_KEY_F19 308 -#define GLFW_KEY_F20 309 -#define GLFW_KEY_F21 310 -#define GLFW_KEY_F22 311 -#define GLFW_KEY_F23 312 -#define GLFW_KEY_F24 313 -#define GLFW_KEY_F25 314 -#define GLFW_KEY_KP_0 320 -#define GLFW_KEY_KP_1 321 -#define GLFW_KEY_KP_2 322 -#define GLFW_KEY_KP_3 323 -#define GLFW_KEY_KP_4 324 -#define GLFW_KEY_KP_5 325 -#define GLFW_KEY_KP_6 326 -#define GLFW_KEY_KP_7 327 -#define GLFW_KEY_KP_8 328 -#define GLFW_KEY_KP_9 329 -#define GLFW_KEY_KP_DECIMAL 330 -#define GLFW_KEY_KP_DIVIDE 331 -#define GLFW_KEY_KP_MULTIPLY 332 -#define GLFW_KEY_KP_SUBTRACT 333 -#define GLFW_KEY_KP_ADD 334 -#define GLFW_KEY_KP_ENTER 335 -#define GLFW_KEY_KP_EQUAL 336 -#define GLFW_KEY_LEFT_SHIFT 340 -#define GLFW_KEY_LEFT_CONTROL 341 -#define GLFW_KEY_LEFT_ALT 342 -#define GLFW_KEY_LEFT_SUPER 343 -#define GLFW_KEY_RIGHT_SHIFT 344 -#define GLFW_KEY_RIGHT_CONTROL 345 -#define GLFW_KEY_RIGHT_ALT 346 -#define GLFW_KEY_RIGHT_SUPER 347 -#define GLFW_KEY_MENU 348 - -#define GLFW_KEY_LAST GLFW_KEY_MENU - -/*! @} */ - -/*! @defgroup mods Modifier key flags - * @brief Modifier key flags. - * - * See [key input](@ref input_key) for how these are used. - * - * @ingroup input - * @{ */ - -/*! @brief If this bit is set one or more Shift keys were held down. - * - * If this bit is set one or more Shift keys were held down. - */ -#define GLFW_MOD_SHIFT 0x0001 -/*! @brief If this bit is set one or more Control keys were held down. - * - * If this bit is set one or more Control keys were held down. - */ -#define GLFW_MOD_CONTROL 0x0002 -/*! @brief If this bit is set one or more Alt keys were held down. - * - * If this bit is set one or more Alt keys were held down. - */ -#define GLFW_MOD_ALT 0x0004 -/*! @brief If this bit is set one or more Super keys were held down. - * - * If this bit is set one or more Super keys were held down. - */ -#define GLFW_MOD_SUPER 0x0008 -/*! @brief If this bit is set the Caps Lock key is enabled. - * - * If this bit is set the Caps Lock key is enabled and the @ref - * GLFW_LOCK_KEY_MODS input mode is set. - */ -#define GLFW_MOD_CAPS_LOCK 0x0010 -/*! @brief If this bit is set the Num Lock key is enabled. - * - * If this bit is set the Num Lock key is enabled and the @ref - * GLFW_LOCK_KEY_MODS input mode is set. - */ -#define GLFW_MOD_NUM_LOCK 0x0020 - -/*! @} */ - -/*! @defgroup buttons Mouse buttons - * @brief Mouse button IDs. - * - * See [mouse button input](@ref input_mouse_button) for how these are used. - * - * @ingroup input - * @{ */ -#define GLFW_MOUSE_BUTTON_1 0 -#define GLFW_MOUSE_BUTTON_2 1 -#define GLFW_MOUSE_BUTTON_3 2 -#define GLFW_MOUSE_BUTTON_4 3 -#define GLFW_MOUSE_BUTTON_5 4 -#define GLFW_MOUSE_BUTTON_6 5 -#define GLFW_MOUSE_BUTTON_7 6 -#define GLFW_MOUSE_BUTTON_8 7 -#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8 -#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1 -#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2 -#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3 -/*! @} */ - -/*! @defgroup joysticks Joysticks - * @brief Joystick IDs. - * - * See [joystick input](@ref joystick) for how these are used. - * - * @ingroup input - * @{ */ -#define GLFW_JOYSTICK_1 0 -#define GLFW_JOYSTICK_2 1 -#define GLFW_JOYSTICK_3 2 -#define GLFW_JOYSTICK_4 3 -#define GLFW_JOYSTICK_5 4 -#define GLFW_JOYSTICK_6 5 -#define GLFW_JOYSTICK_7 6 -#define GLFW_JOYSTICK_8 7 -#define GLFW_JOYSTICK_9 8 -#define GLFW_JOYSTICK_10 9 -#define GLFW_JOYSTICK_11 10 -#define GLFW_JOYSTICK_12 11 -#define GLFW_JOYSTICK_13 12 -#define GLFW_JOYSTICK_14 13 -#define GLFW_JOYSTICK_15 14 -#define GLFW_JOYSTICK_16 15 -#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 -/*! @} */ - -/*! @defgroup gamepad_buttons Gamepad buttons - * @brief Gamepad buttons. - * - * See @ref gamepad for how these are used. - * - * @ingroup input - * @{ */ -#define GLFW_GAMEPAD_BUTTON_A 0 -#define GLFW_GAMEPAD_BUTTON_B 1 -#define GLFW_GAMEPAD_BUTTON_X 2 -#define GLFW_GAMEPAD_BUTTON_Y 3 -#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER 4 -#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER 5 -#define GLFW_GAMEPAD_BUTTON_BACK 6 -#define GLFW_GAMEPAD_BUTTON_START 7 -#define GLFW_GAMEPAD_BUTTON_GUIDE 8 -#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB 9 -#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB 10 -#define GLFW_GAMEPAD_BUTTON_DPAD_UP 11 -#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT 12 -#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN 13 -#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT 14 -#define GLFW_GAMEPAD_BUTTON_LAST GLFW_GAMEPAD_BUTTON_DPAD_LEFT - -#define GLFW_GAMEPAD_BUTTON_CROSS GLFW_GAMEPAD_BUTTON_A -#define GLFW_GAMEPAD_BUTTON_CIRCLE GLFW_GAMEPAD_BUTTON_B -#define GLFW_GAMEPAD_BUTTON_SQUARE GLFW_GAMEPAD_BUTTON_X -#define GLFW_GAMEPAD_BUTTON_TRIANGLE GLFW_GAMEPAD_BUTTON_Y -/*! @} */ - -/*! @defgroup gamepad_axes Gamepad axes - * @brief Gamepad axes. - * - * See @ref gamepad for how these are used. - * - * @ingroup input - * @{ */ -#define GLFW_GAMEPAD_AXIS_LEFT_X 0 -#define GLFW_GAMEPAD_AXIS_LEFT_Y 1 -#define GLFW_GAMEPAD_AXIS_RIGHT_X 2 -#define GLFW_GAMEPAD_AXIS_RIGHT_Y 3 -#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER 4 -#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5 -#define GLFW_GAMEPAD_AXIS_LAST GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER -/*! @} */ - -/*! @defgroup errors Error codes - * @brief Error codes. - * - * See [error handling](@ref error_handling) for how these are used. - * - * @ingroup init - * @{ */ -/*! @brief No error has occurred. - * - * No error has occurred. - * - * @analysis Yay. - */ -#define GLFW_NO_ERROR 0 -/*! @brief GLFW has not been initialized. - * - * This occurs if a GLFW function was called that must not be called unless the - * library is [initialized](@ref intro_init). - * - * @analysis Application programmer error. Initialize GLFW before calling any - * function that requires initialization. - */ -#define GLFW_NOT_INITIALIZED 0x00010001 -/*! @brief No context is current for this thread. - * - * This occurs if a GLFW function was called that needs and operates on the - * current OpenGL or OpenGL ES context but no context is current on the calling - * thread. One such function is @ref glfwSwapInterval. - * - * @analysis Application programmer error. Ensure a context is current before - * calling functions that require a current context. - */ -#define GLFW_NO_CURRENT_CONTEXT 0x00010002 -/*! @brief One of the arguments to the function was an invalid enum value. - * - * One of the arguments to the function was an invalid enum value, for example - * requesting @ref GLFW_RED_BITS with @ref glfwGetWindowAttrib. - * - * @analysis Application programmer error. Fix the offending call. - */ -#define GLFW_INVALID_ENUM 0x00010003 -/*! @brief One of the arguments to the function was an invalid value. - * - * One of the arguments to the function was an invalid value, for example - * requesting a non-existent OpenGL or OpenGL ES version like 2.7. - * - * Requesting a valid but unavailable OpenGL or OpenGL ES version will instead - * result in a @ref GLFW_VERSION_UNAVAILABLE error. - * - * @analysis Application programmer error. Fix the offending call. - */ -#define GLFW_INVALID_VALUE 0x00010004 -/*! @brief A memory allocation failed. - * - * A memory allocation failed. - * - * @analysis A bug in GLFW or the underlying operating system. Report the bug - * to our [issue tracker](https://github.com/glfw/glfw/issues). - */ -#define GLFW_OUT_OF_MEMORY 0x00010005 -/*! @brief GLFW could not find support for the requested API on the system. - * - * GLFW could not find support for the requested API on the system. - * - * @analysis The installed graphics driver does not support the requested - * API, or does not support it via the chosen context creation backend. - * Below are a few examples. - * - * @par - * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only - * supports OpenGL ES via EGL, while Nvidia and Intel only support it via - * a WGL or GLX extension. macOS does not provide OpenGL ES at all. The Mesa - * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary - * driver. Older graphics drivers do not support Vulkan. - */ -#define GLFW_API_UNAVAILABLE 0x00010006 -/*! @brief The requested OpenGL or OpenGL ES version is not available. - * - * The requested OpenGL or OpenGL ES version (including any requested context - * or framebuffer hints) is not available on this machine. - * - * @analysis The machine does not support your requirements. If your - * application is sufficiently flexible, downgrade your requirements and try - * again. Otherwise, inform the user that their machine does not match your - * requirements. - * - * @par - * Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0 - * comes out before the 4.x series gets that far, also fail with this error and - * not @ref GLFW_INVALID_VALUE, because GLFW cannot know what future versions - * will exist. - */ -#define GLFW_VERSION_UNAVAILABLE 0x00010007 -/*! @brief A platform-specific error occurred that does not match any of the - * more specific categories. - * - * A platform-specific error occurred that does not match any of the more - * specific categories. - * - * @analysis A bug or configuration error in GLFW, the underlying operating - * system or its drivers, or a lack of required resources. Report the issue to - * our [issue tracker](https://github.com/glfw/glfw/issues). - */ -#define GLFW_PLATFORM_ERROR 0x00010008 -/*! @brief The requested format is not supported or available. - * - * If emitted during window creation, the requested pixel format is not - * supported. - * - * If emitted when querying the clipboard, the contents of the clipboard could - * not be converted to the requested format. - * - * @analysis If emitted during window creation, one or more - * [hard constraints](@ref window_hints_hard) did not match any of the - * available pixel formats. If your application is sufficiently flexible, - * downgrade your requirements and try again. Otherwise, inform the user that - * their machine does not match your requirements. - * - * @par - * If emitted when querying the clipboard, ignore the error or report it to - * the user, as appropriate. - */ -#define GLFW_FORMAT_UNAVAILABLE 0x00010009 -/*! @brief The specified window does not have an OpenGL or OpenGL ES context. - * - * A window that does not have an OpenGL or OpenGL ES context was passed to - * a function that requires it to have one. - * - * @analysis Application programmer error. Fix the offending call. - */ -#define GLFW_NO_WINDOW_CONTEXT 0x0001000A -/*! @} */ - -/*! @addtogroup window - * @{ */ -/*! @brief Input focus window hint and attribute - * - * Input focus [window hint](@ref GLFW_FOCUSED_hint) or - * [window attribute](@ref GLFW_FOCUSED_attrib). - */ -#define GLFW_FOCUSED 0x00020001 -/*! @brief Window iconification window attribute - * - * Window iconification [window attribute](@ref GLFW_ICONIFIED_attrib). - */ -#define GLFW_ICONIFIED 0x00020002 -/*! @brief Window resize-ability window hint and attribute - * - * Window resize-ability [window hint](@ref GLFW_RESIZABLE_hint) and - * [window attribute](@ref GLFW_RESIZABLE_attrib). - */ -#define GLFW_RESIZABLE 0x00020003 -/*! @brief Window visibility window hint and attribute - * - * Window visibility [window hint](@ref GLFW_VISIBLE_hint) and - * [window attribute](@ref GLFW_VISIBLE_attrib). - */ -#define GLFW_VISIBLE 0x00020004 -/*! @brief Window decoration window hint and attribute - * - * Window decoration [window hint](@ref GLFW_DECORATED_hint) and - * [window attribute](@ref GLFW_DECORATED_attrib). - */ -#define GLFW_DECORATED 0x00020005 -/*! @brief Window auto-iconification window hint and attribute - * - * Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) and - * [window attribute](@ref GLFW_AUTO_ICONIFY_attrib). - */ -#define GLFW_AUTO_ICONIFY 0x00020006 -/*! @brief Window decoration window hint and attribute - * - * Window decoration [window hint](@ref GLFW_FLOATING_hint) and - * [window attribute](@ref GLFW_FLOATING_attrib). - */ -#define GLFW_FLOATING 0x00020007 -/*! @brief Window maximization window hint and attribute - * - * Window maximization [window hint](@ref GLFW_MAXIMIZED_hint) and - * [window attribute](@ref GLFW_MAXIMIZED_attrib). - */ -#define GLFW_MAXIMIZED 0x00020008 -/*! @brief Cursor centering window hint - * - * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). - */ -#define GLFW_CENTER_CURSOR 0x00020009 -/*! @brief Window framebuffer transparency hint and attribute - * - * Window framebuffer transparency - * [window hint](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint) and - * [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib). - */ -#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A -/*! @brief Mouse cursor hover window attribute. - * - * Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib). - */ -#define GLFW_HOVERED 0x0002000B -/*! @brief Input focus on calling show window hint and attribute - * - * Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or - * [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib). - */ -#define GLFW_FOCUS_ON_SHOW 0x0002000C - -/*! @brief Framebuffer bit depth hint. - * - * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). - */ -#define GLFW_RED_BITS 0x00021001 -/*! @brief Framebuffer bit depth hint. - * - * Framebuffer bit depth [hint](@ref GLFW_GREEN_BITS). - */ -#define GLFW_GREEN_BITS 0x00021002 -/*! @brief Framebuffer bit depth hint. - * - * Framebuffer bit depth [hint](@ref GLFW_BLUE_BITS). - */ -#define GLFW_BLUE_BITS 0x00021003 -/*! @brief Framebuffer bit depth hint. - * - * Framebuffer bit depth [hint](@ref GLFW_ALPHA_BITS). - */ -#define GLFW_ALPHA_BITS 0x00021004 -/*! @brief Framebuffer bit depth hint. - * - * Framebuffer bit depth [hint](@ref GLFW_DEPTH_BITS). - */ -#define GLFW_DEPTH_BITS 0x00021005 -/*! @brief Framebuffer bit depth hint. - * - * Framebuffer bit depth [hint](@ref GLFW_STENCIL_BITS). - */ -#define GLFW_STENCIL_BITS 0x00021006 -/*! @brief Framebuffer bit depth hint. - * - * Framebuffer bit depth [hint](@ref GLFW_ACCUM_RED_BITS). - */ -#define GLFW_ACCUM_RED_BITS 0x00021007 -/*! @brief Framebuffer bit depth hint. - * - * Framebuffer bit depth [hint](@ref GLFW_ACCUM_GREEN_BITS). - */ -#define GLFW_ACCUM_GREEN_BITS 0x00021008 -/*! @brief Framebuffer bit depth hint. - * - * Framebuffer bit depth [hint](@ref GLFW_ACCUM_BLUE_BITS). - */ -#define GLFW_ACCUM_BLUE_BITS 0x00021009 -/*! @brief Framebuffer bit depth hint. - * - * Framebuffer bit depth [hint](@ref GLFW_ACCUM_ALPHA_BITS). - */ -#define GLFW_ACCUM_ALPHA_BITS 0x0002100A -/*! @brief Framebuffer auxiliary buffer hint. - * - * Framebuffer auxiliary buffer [hint](@ref GLFW_AUX_BUFFERS). - */ -#define GLFW_AUX_BUFFERS 0x0002100B -/*! @brief OpenGL stereoscopic rendering hint. - * - * OpenGL stereoscopic rendering [hint](@ref GLFW_STEREO). - */ -#define GLFW_STEREO 0x0002100C -/*! @brief Framebuffer MSAA samples hint. - * - * Framebuffer MSAA samples [hint](@ref GLFW_SAMPLES). - */ -#define GLFW_SAMPLES 0x0002100D -/*! @brief Framebuffer sRGB hint. - * - * Framebuffer sRGB [hint](@ref GLFW_SRGB_CAPABLE). - */ -#define GLFW_SRGB_CAPABLE 0x0002100E -/*! @brief Monitor refresh rate hint. - * - * Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE). - */ -#define GLFW_REFRESH_RATE 0x0002100F -/*! @brief Framebuffer double buffering hint. - * - * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). - */ -#define GLFW_DOUBLEBUFFER 0x00021010 - -/*! @brief Context client API hint and attribute. - * - * Context client API [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). - */ -#define GLFW_CLIENT_API 0x00022001 -/*! @brief Context client API major version hint and attribute. - * - * Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint) - * and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib). - */ -#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 -/*! @brief Context client API minor version hint and attribute. - * - * Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint) - * and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib). - */ -#define GLFW_CONTEXT_VERSION_MINOR 0x00022003 -/*! @brief Context client API revision number attribute. - * - * Context client API revision number - * [attribute](@ref GLFW_CONTEXT_REVISION_attrib). - */ -#define GLFW_CONTEXT_REVISION 0x00022004 -/*! @brief Context robustness hint and attribute. - * - * Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint) - * and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib). - */ -#define GLFW_CONTEXT_ROBUSTNESS 0x00022005 -/*! @brief OpenGL forward-compatibility hint and attribute. - * - * OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) - * and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib). - */ -#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 -/*! @brief Debug mode context hint and attribute. - * - * Debug mode context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and - * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib). - */ -#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 -/*! @brief OpenGL profile hint and attribute. - * - * OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and - * [attribute](@ref GLFW_OPENGL_PROFILE_attrib). - */ -#define GLFW_OPENGL_PROFILE 0x00022008 -/*! @brief Context flush-on-release hint and attribute. - * - * Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and - * [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib). - */ -#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 -/*! @brief Context error suppression hint and attribute. - * - * Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and - * [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib). - */ -#define GLFW_CONTEXT_NO_ERROR 0x0002200A -/*! @brief Context creation API hint and attribute. - * - * Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and - * [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib). - */ -#define GLFW_CONTEXT_CREATION_API 0x0002200B -/*! @brief Window content area scaling window - * [window hint](@ref GLFW_SCALE_TO_MONITOR). - */ -#define GLFW_SCALE_TO_MONITOR 0x0002200C -/*! @brief macOS specific - * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint). - */ -#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 -/*! @brief macOS specific - * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint). - */ -#define GLFW_COCOA_FRAME_NAME 0x00023002 -/*! @brief macOS specific - * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint). - */ -#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 -/*! @brief X11 specific - * [window hint](@ref GLFW_X11_CLASS_NAME_hint). - */ -#define GLFW_X11_CLASS_NAME 0x00024001 -/*! @brief X11 specific - * [window hint](@ref GLFW_X11_CLASS_NAME_hint). - */ -#define GLFW_X11_INSTANCE_NAME 0x00024002 -/*! @} */ - -#define GLFW_NO_API 0 -#define GLFW_OPENGL_API 0x00030001 -#define GLFW_OPENGL_ES_API 0x00030002 - -#define GLFW_NO_ROBUSTNESS 0 -#define GLFW_NO_RESET_NOTIFICATION 0x00031001 -#define GLFW_LOSE_CONTEXT_ON_RESET 0x00031002 - -#define GLFW_OPENGL_ANY_PROFILE 0 -#define GLFW_OPENGL_CORE_PROFILE 0x00032001 -#define GLFW_OPENGL_COMPAT_PROFILE 0x00032002 - -#define GLFW_CURSOR 0x00033001 -#define GLFW_STICKY_KEYS 0x00033002 -#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 -#define GLFW_LOCK_KEY_MODS 0x00033004 -#define GLFW_RAW_MOUSE_MOTION 0x00033005 - -#define GLFW_CURSOR_NORMAL 0x00034001 -#define GLFW_CURSOR_HIDDEN 0x00034002 -#define GLFW_CURSOR_DISABLED 0x00034003 - -#define GLFW_ANY_RELEASE_BEHAVIOR 0 -#define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001 -#define GLFW_RELEASE_BEHAVIOR_NONE 0x00035002 - -#define GLFW_NATIVE_CONTEXT_API 0x00036001 -#define GLFW_EGL_CONTEXT_API 0x00036002 -#define GLFW_OSMESA_CONTEXT_API 0x00036003 - -/*! @defgroup shapes Standard cursor shapes - * @brief Standard system cursor shapes. - * - * See [standard cursor creation](@ref cursor_standard) for how these are used. - * - * @ingroup input - * @{ */ - -/*! @brief The regular arrow cursor shape. - * - * The regular arrow cursor. - */ -#define GLFW_ARROW_CURSOR 0x00036001 -/*! @brief The text input I-beam cursor shape. - * - * The text input I-beam cursor shape. - */ -#define GLFW_IBEAM_CURSOR 0x00036002 -/*! @brief The crosshair shape. - * - * The crosshair shape. - */ -#define GLFW_CROSSHAIR_CURSOR 0x00036003 -/*! @brief The hand shape. - * - * The hand shape. - */ -#define GLFW_HAND_CURSOR 0x00036004 -/*! @brief The horizontal resize arrow shape. - * - * The horizontal resize arrow shape. - */ -#define GLFW_HRESIZE_CURSOR 0x00036005 -/*! @brief The vertical resize arrow shape. - * - * The vertical resize arrow shape. - */ -#define GLFW_VRESIZE_CURSOR 0x00036006 -/*! @} */ - -#define GLFW_CONNECTED 0x00040001 -#define GLFW_DISCONNECTED 0x00040002 - -/*! @addtogroup init - * @{ */ -/*! @brief Joystick hat buttons init hint. - * - * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS). - */ -#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 -/*! @brief macOS specific init hint. - * - * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint). - */ -#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 -/*! @brief macOS specific init hint. - * - * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). - */ -#define GLFW_COCOA_MENUBAR 0x00051002 -/*! @} */ - -#define GLFW_DONT_CARE -1 - - -/************************************************************************* - * GLFW API types - *************************************************************************/ - -/*! @brief Client API function pointer type. - * - * Generic function pointer used for returning client API function pointers - * without forcing a cast from a regular pointer. - * - * @sa @ref context_glext - * @sa @ref glfwGetProcAddress - * - * @since Added in version 3.0. - * - * @ingroup context - */ -typedef void (*GLFWglproc)(void); - -/*! @brief Vulkan API function pointer type. - * - * Generic function pointer used for returning Vulkan API function pointers - * without forcing a cast from a regular pointer. - * - * @sa @ref vulkan_proc - * @sa @ref glfwGetInstanceProcAddress - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -typedef void (*GLFWvkproc)(void); - -/*! @brief Opaque monitor object. - * - * Opaque monitor object. - * - * @see @ref monitor_object - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -typedef struct GLFWmonitor GLFWmonitor; - -/*! @brief Opaque window object. - * - * Opaque window object. - * - * @see @ref window_object - * - * @since Added in version 3.0. - * - * @ingroup window - */ -typedef struct GLFWwindow GLFWwindow; - -/*! @brief Opaque cursor object. - * - * Opaque cursor object. - * - * @see @ref cursor_object - * - * @since Added in version 3.1. - * - * @ingroup input - */ -typedef struct GLFWcursor GLFWcursor; - -/*! @brief The function pointer type for error callbacks. - * - * This is the function pointer type for error callbacks. An error callback - * function has the following signature: - * @code - * void callback_name(int error_code, const char* description) - * @endcode - * - * @param[in] error_code An [error code](@ref errors). Future releases may add - * more error codes. - * @param[in] description A UTF-8 encoded string describing the error. - * - * @pointer_lifetime The error description string is valid until the callback - * function returns. - * - * @sa @ref error_handling - * @sa @ref glfwSetErrorCallback - * - * @since Added in version 3.0. - * - * @ingroup init - */ -typedef void (* GLFWerrorfun)(int error_code, const char* description); - -/*! @brief The function pointer type for window position callbacks. - * - * This is the function pointer type for window position callbacks. A window - * position callback function has the following signature: - * @code - * void callback_name(GLFWwindow* window, int xpos, int ypos) - * @endcode - * - * @param[in] window The window that was moved. - * @param[in] xpos The new x-coordinate, in screen coordinates, of the - * upper-left corner of the content area of the window. - * @param[in] ypos The new y-coordinate, in screen coordinates, of the - * upper-left corner of the content area of the window. - * - * @sa @ref window_pos - * @sa @ref glfwSetWindowPosCallback - * - * @since Added in version 3.0. - * - * @ingroup window - */ -typedef void (* GLFWwindowposfun)(GLFWwindow* window, int xpos, int ypos); - -/*! @brief The function pointer type for window size callbacks. - * - * This is the function pointer type for window size callbacks. A window size - * callback function has the following signature: - * @code - * void callback_name(GLFWwindow* window, int width, int height) - * @endcode - * - * @param[in] window The window that was resized. - * @param[in] width The new width, in screen coordinates, of the window. - * @param[in] height The new height, in screen coordinates, of the window. - * - * @sa @ref window_size - * @sa @ref glfwSetWindowSizeCallback - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -typedef void (* GLFWwindowsizefun)(GLFWwindow* window, int width, int height); - -/*! @brief The function pointer type for window close callbacks. - * - * This is the function pointer type for window close callbacks. A window - * close callback function has the following signature: - * @code - * void function_name(GLFWwindow* window) - * @endcode - * - * @param[in] window The window that the user attempted to close. - * - * @sa @ref window_close - * @sa @ref glfwSetWindowCloseCallback - * - * @since Added in version 2.5. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -typedef void (* GLFWwindowclosefun)(GLFWwindow* window); - -/*! @brief The function pointer type for window content refresh callbacks. - * - * This is the function pointer type for window content refresh callbacks. - * A window content refresh callback function has the following signature: - * @code - * void function_name(GLFWwindow* window); - * @endcode - * - * @param[in] window The window whose content needs to be refreshed. - * - * @sa @ref window_refresh - * @sa @ref glfwSetWindowRefreshCallback - * - * @since Added in version 2.5. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -typedef void (* GLFWwindowrefreshfun)(GLFWwindow* window); - -/*! @brief The function pointer type for window focus callbacks. - * - * This is the function pointer type for window focus callbacks. A window - * focus callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, int focused) - * @endcode - * - * @param[in] window The window that gained or lost input focus. - * @param[in] focused `GLFW_TRUE` if the window was given input focus, or - * `GLFW_FALSE` if it lost it. - * - * @sa @ref window_focus - * @sa @ref glfwSetWindowFocusCallback - * - * @since Added in version 3.0. - * - * @ingroup window - */ -typedef void (* GLFWwindowfocusfun)(GLFWwindow* window, int focused); - -/*! @brief The function pointer type for window iconify callbacks. - * - * This is the function pointer type for window iconify callbacks. A window - * iconify callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, int iconified) - * @endcode - * - * @param[in] window The window that was iconified or restored. - * @param[in] iconified `GLFW_TRUE` if the window was iconified, or - * `GLFW_FALSE` if it was restored. - * - * @sa @ref window_iconify - * @sa @ref glfwSetWindowIconifyCallback - * - * @since Added in version 3.0. - * - * @ingroup window - */ -typedef void (* GLFWwindowiconifyfun)(GLFWwindow* window, int iconified); - -/*! @brief The function pointer type for window maximize callbacks. - * - * This is the function pointer type for window maximize callbacks. A window - * maximize callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, int maximized) - * @endcode - * - * @param[in] window The window that was maximized or restored. - * @param[in] maximized `GLFW_TRUE` if the window was maximized, or - * `GLFW_FALSE` if it was restored. - * - * @sa @ref window_maximize - * @sa glfwSetWindowMaximizeCallback - * - * @since Added in version 3.3. - * - * @ingroup window - */ -typedef void (* GLFWwindowmaximizefun)(GLFWwindow* window, int maximized); - -/*! @brief The function pointer type for framebuffer size callbacks. - * - * This is the function pointer type for framebuffer size callbacks. - * A framebuffer size callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, int width, int height) - * @endcode - * - * @param[in] window The window whose framebuffer was resized. - * @param[in] width The new width, in pixels, of the framebuffer. - * @param[in] height The new height, in pixels, of the framebuffer. - * - * @sa @ref window_fbsize - * @sa @ref glfwSetFramebufferSizeCallback - * - * @since Added in version 3.0. - * - * @ingroup window - */ -typedef void (* GLFWframebuffersizefun)(GLFWwindow* window, int width, int height); - -/*! @brief The function pointer type for window content scale callbacks. - * - * This is the function pointer type for window content scale callbacks. - * A window content scale callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, float xscale, float yscale) - * @endcode - * - * @param[in] window The window whose content scale changed. - * @param[in] xscale The new x-axis content scale of the window. - * @param[in] yscale The new y-axis content scale of the window. - * - * @sa @ref window_scale - * @sa @ref glfwSetWindowContentScaleCallback - * - * @since Added in version 3.3. - * - * @ingroup window - */ -typedef void (* GLFWwindowcontentscalefun)(GLFWwindow* window, float xscale, float yscale); - -/*! @brief The function pointer type for mouse button callbacks. - * - * This is the function pointer type for mouse button callback functions. - * A mouse button callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, int button, int action, int mods) - * @endcode - * - * @param[in] window The window that received the event. - * @param[in] button The [mouse button](@ref buttons) that was pressed or - * released. - * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases - * may add more actions. - * @param[in] mods Bit field describing which [modifier keys](@ref mods) were - * held down. - * - * @sa @ref input_mouse_button - * @sa @ref glfwSetMouseButtonCallback - * - * @since Added in version 1.0. - * @glfw3 Added window handle and modifier mask parameters. - * - * @ingroup input - */ -typedef void (* GLFWmousebuttonfun)(GLFWwindow* window, int button, int action, int mods); - -/*! @brief The function pointer type for cursor position callbacks. - * - * This is the function pointer type for cursor position callbacks. A cursor - * position callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, double xpos, double ypos); - * @endcode - * - * @param[in] window The window that received the event. - * @param[in] xpos The new cursor x-coordinate, relative to the left edge of - * the content area. - * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the - * content area. - * - * @sa @ref cursor_pos - * @sa @ref glfwSetCursorPosCallback - * - * @since Added in version 3.0. Replaces `GLFWmouseposfun`. - * - * @ingroup input - */ -typedef void (* GLFWcursorposfun)(GLFWwindow* window, double xpos, double ypos); - -/*! @brief The function pointer type for cursor enter/leave callbacks. - * - * This is the function pointer type for cursor enter/leave callbacks. - * A cursor enter/leave callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, int entered) - * @endcode - * - * @param[in] window The window that received the event. - * @param[in] entered `GLFW_TRUE` if the cursor entered the window's content - * area, or `GLFW_FALSE` if it left it. - * - * @sa @ref cursor_enter - * @sa @ref glfwSetCursorEnterCallback - * - * @since Added in version 3.0. - * - * @ingroup input - */ -typedef void (* GLFWcursorenterfun)(GLFWwindow* window, int entered); - -/*! @brief The function pointer type for scroll callbacks. - * - * This is the function pointer type for scroll callbacks. A scroll callback - * function has the following signature: - * @code - * void function_name(GLFWwindow* window, double xoffset, double yoffset) - * @endcode - * - * @param[in] window The window that received the event. - * @param[in] xoffset The scroll offset along the x-axis. - * @param[in] yoffset The scroll offset along the y-axis. - * - * @sa @ref scrolling - * @sa @ref glfwSetScrollCallback - * - * @since Added in version 3.0. Replaces `GLFWmousewheelfun`. - * - * @ingroup input - */ -typedef void (* GLFWscrollfun)(GLFWwindow* window, double xoffset, double yoffset); - -/*! @brief The function pointer type for keyboard key callbacks. - * - * This is the function pointer type for keyboard key callbacks. A keyboard - * key callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) - * @endcode - * - * @param[in] window The window that received the event. - * @param[in] key The [keyboard key](@ref keys) that was pressed or released. - * @param[in] scancode The system-specific scancode of the key. - * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future - * releases may add more actions. - * @param[in] mods Bit field describing which [modifier keys](@ref mods) were - * held down. - * - * @sa @ref input_key - * @sa @ref glfwSetKeyCallback - * - * @since Added in version 1.0. - * @glfw3 Added window handle, scancode and modifier mask parameters. - * - * @ingroup input - */ -typedef void (* GLFWkeyfun)(GLFWwindow* window, int key, int scancode, int action, int mods); - -/*! @brief The function pointer type for Unicode character callbacks. - * - * This is the function pointer type for Unicode character callbacks. - * A Unicode character callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, unsigned int codepoint) - * @endcode - * - * @param[in] window The window that received the event. - * @param[in] codepoint The Unicode code point of the character. - * - * @sa @ref input_char - * @sa @ref glfwSetCharCallback - * - * @since Added in version 2.4. - * @glfw3 Added window handle parameter. - * - * @ingroup input - */ -typedef void (* GLFWcharfun)(GLFWwindow* window, unsigned int codepoint); - -/*! @brief The function pointer type for Unicode character with modifiers - * callbacks. - * - * This is the function pointer type for Unicode character with modifiers - * callbacks. It is called for each input character, regardless of what - * modifier keys are held down. A Unicode character with modifiers callback - * function has the following signature: - * @code - * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) - * @endcode - * - * @param[in] window The window that received the event. - * @param[in] codepoint The Unicode code point of the character. - * @param[in] mods Bit field describing which [modifier keys](@ref mods) were - * held down. - * - * @sa @ref input_char - * @sa @ref glfwSetCharModsCallback - * - * @deprecated Scheduled for removal in version 4.0. - * - * @since Added in version 3.1. - * - * @ingroup input - */ -typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int mods); - -/*! @brief The function pointer type for path drop callbacks. - * - * This is the function pointer type for path drop callbacks. A path drop - * callback function has the following signature: - * @code - * void function_name(GLFWwindow* window, int path_count, const char* paths[]) - * @endcode - * - * @param[in] window The window that received the event. - * @param[in] path_count The number of dropped paths. - * @param[in] paths The UTF-8 encoded file and/or directory path names. - * - * @pointer_lifetime The path array and its strings are valid until the - * callback function returns. - * - * @sa @ref path_drop - * @sa @ref glfwSetDropCallback - * - * @since Added in version 3.1. - * - * @ingroup input - */ -typedef void (* GLFWdropfun)(GLFWwindow* window, int path_count, const char* paths[]); - -/*! @brief The function pointer type for monitor configuration callbacks. - * - * This is the function pointer type for monitor configuration callbacks. - * A monitor callback function has the following signature: - * @code - * void function_name(GLFWmonitor* monitor, int event) - * @endcode - * - * @param[in] monitor The monitor that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future - * releases may add more events. - * - * @sa @ref monitor_event - * @sa @ref glfwSetMonitorCallback - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event); - -/*! @brief The function pointer type for joystick configuration callbacks. - * - * This is the function pointer type for joystick configuration callbacks. - * A joystick configuration callback function has the following signature: - * @code - * void function_name(int jid, int event) - * @endcode - * - * @param[in] jid The joystick that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future - * releases may add more events. - * - * @sa @ref joystick_event - * @sa @ref glfwSetJoystickCallback - * - * @since Added in version 3.2. - * - * @ingroup input - */ -typedef void (* GLFWjoystickfun)(int jid, int event); - -/*! @brief Video mode type. - * - * This describes a single video mode. - * - * @sa @ref monitor_modes - * @sa @ref glfwGetVideoMode - * @sa @ref glfwGetVideoModes - * - * @since Added in version 1.0. - * @glfw3 Added refresh rate member. - * - * @ingroup monitor - */ -typedef struct GLFWvidmode -{ - /*! The width, in screen coordinates, of the video mode. - */ - int width; - /*! The height, in screen coordinates, of the video mode. - */ - int height; - /*! The bit depth of the red channel of the video mode. - */ - int redBits; - /*! The bit depth of the green channel of the video mode. - */ - int greenBits; - /*! The bit depth of the blue channel of the video mode. - */ - int blueBits; - /*! The refresh rate, in Hz, of the video mode. - */ - int refreshRate; -} GLFWvidmode; - -/*! @brief Gamma ramp. - * - * This describes the gamma ramp for a monitor. - * - * @sa @ref monitor_gamma - * @sa @ref glfwGetGammaRamp - * @sa @ref glfwSetGammaRamp - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -typedef struct GLFWgammaramp -{ - /*! An array of value describing the response of the red channel. - */ - unsigned short* red; - /*! An array of value describing the response of the green channel. - */ - unsigned short* green; - /*! An array of value describing the response of the blue channel. - */ - unsigned short* blue; - /*! The number of elements in each array. - */ - unsigned int size; -} GLFWgammaramp; - -/*! @brief Image data. - * - * This describes a single 2D image. See the documentation for each related - * function what the expected pixel format is. - * - * @sa @ref cursor_custom - * @sa @ref window_icon - * - * @since Added in version 2.1. - * @glfw3 Removed format and bytes-per-pixel members. - * - * @ingroup window - */ -typedef struct GLFWimage -{ - /*! The width, in pixels, of this image. - */ - int width; - /*! The height, in pixels, of this image. - */ - int height; - /*! The pixel data of this image, arranged left-to-right, top-to-bottom. - */ - unsigned char* pixels; -} GLFWimage; - -/*! @brief Gamepad input state - * - * This describes the input state of a gamepad. - * - * @sa @ref gamepad - * @sa @ref glfwGetGamepadState - * - * @since Added in version 3.3. - * - * @ingroup input - */ -typedef struct GLFWgamepadstate -{ - /*! The states of each [gamepad button](@ref gamepad_buttons), `GLFW_PRESS` - * or `GLFW_RELEASE`. - */ - unsigned char buttons[15]; - /*! The states of each [gamepad axis](@ref gamepad_axes), in the range -1.0 - * to 1.0 inclusive. - */ - float axes[6]; -} GLFWgamepadstate; - - -/************************************************************************* - * GLFW API functions - *************************************************************************/ - -/*! @brief Initializes the GLFW library. - * - * This function initializes the GLFW library. Before most GLFW functions can - * be used, GLFW must be initialized, and before an application terminates GLFW - * should be terminated in order to free any resources allocated during or - * after initialization. - * - * If this function fails, it calls @ref glfwTerminate before returning. If it - * succeeds, you should call @ref glfwTerminate before the application exits. - * - * Additional calls to this function after successful initialization but before - * termination will return `GLFW_TRUE` immediately. - * - * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. - * - * @remark @macos This function will change the current directory of the - * application to the `Contents/Resources` subdirectory of the application's - * bundle, if present. This can be disabled with the @ref - * GLFW_COCOA_CHDIR_RESOURCES init hint. - * - * @remark @x11 This function will set the `LC_CTYPE` category of the - * application locale according to the current environment if that category is - * still "C". This is because the "C" locale breaks Unicode text input. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref intro_init - * @sa @ref glfwTerminate - * - * @since Added in version 1.0. - * - * @ingroup init - */ -GLFWAPI int glfwInit(void); - -/*! @brief Terminates the GLFW library. - * - * This function destroys all remaining windows and cursors, restores any - * modified gamma ramps and frees any other allocated resources. Once this - * function is called, you must again call @ref glfwInit successfully before - * you will be able to use most GLFW functions. - * - * If GLFW has been successfully initialized, this function should be called - * before the application exits. If initialization fails, there is no need to - * call this function, as it is called by @ref glfwInit before it returns - * failure. - * - * This function has no effect if GLFW is not initialized. - * - * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. - * - * @remark This function may be called before @ref glfwInit. - * - * @warning The contexts of any remaining windows must not be current on any - * other thread when this function is called. - * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref intro_init - * @sa @ref glfwInit - * - * @since Added in version 1.0. - * - * @ingroup init - */ -GLFWAPI void glfwTerminate(void); - -/*! @brief Sets the specified init hint to the desired value. - * - * This function sets hints for the next initialization of GLFW. - * - * The values you set hints to are never reset by GLFW, but they only take - * effect during initialization. Once GLFW has been initialized, any values - * you set will be ignored until the library is terminated and initialized - * again. - * - * Some hints are platform specific. These may be set on any platform but they - * will only affect their specific platform. Other platforms will ignore them. - * Setting these hints requires no platform specific headers or functions. - * - * @param[in] hint The [init hint](@ref init_hints) to set. - * @param[in] value The new value of the init hint. - * - * @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref - * GLFW_INVALID_VALUE. - * - * @remarks This function may be called before @ref glfwInit. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa init_hints - * @sa glfwInit - * - * @since Added in version 3.3. - * - * @ingroup init - */ -GLFWAPI void glfwInitHint(int hint, int value); - -/*! @brief Retrieves the version of the GLFW library. - * - * This function retrieves the major, minor and revision numbers of the GLFW - * library. It is intended for when you are using GLFW as a shared library and - * want to ensure that you are using the minimum required version. - * - * Any or all of the version arguments may be `NULL`. - * - * @param[out] major Where to store the major version number, or `NULL`. - * @param[out] minor Where to store the minor version number, or `NULL`. - * @param[out] rev Where to store the revision number, or `NULL`. - * - * @errors None. - * - * @remark This function may be called before @ref glfwInit. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref intro_version - * @sa @ref glfwGetVersionString - * - * @since Added in version 1.0. - * - * @ingroup init - */ -GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); - -/*! @brief Returns a string describing the compile-time configuration. - * - * This function returns the compile-time generated - * [version string](@ref intro_version_string) of the GLFW library binary. It - * describes the version, platform, compiler and any platform-specific - * compile-time options. It should not be confused with the OpenGL or OpenGL - * ES version string, queried with `glGetString`. - * - * __Do not use the version string__ to parse the GLFW library version. The - * @ref glfwGetVersion function provides the version of the running library - * binary in numerical format. - * - * @return The ASCII encoded GLFW version string. - * - * @errors None. - * - * @remark This function may be called before @ref glfwInit. - * - * @pointer_lifetime The returned string is static and compile-time generated. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref intro_version - * @sa @ref glfwGetVersion - * - * @since Added in version 3.0. - * - * @ingroup init - */ -GLFWAPI const char* glfwGetVersionString(void); - -/*! @brief Returns and clears the last error for the calling thread. - * - * This function returns and clears the [error code](@ref errors) of the last - * error that occurred on the calling thread, and optionally a UTF-8 encoded - * human-readable description of it. If no error has occurred since the last - * call, it returns @ref GLFW_NO_ERROR (zero) and the description pointer is - * set to `NULL`. - * - * @param[in] description Where to store the error description pointer, or `NULL`. - * @return The last error code for the calling thread, or @ref GLFW_NO_ERROR - * (zero). - * - * @errors None. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is guaranteed to be valid only until the - * next error occurs or the library is terminated. - * - * @remark This function may be called before @ref glfwInit. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref error_handling - * @sa @ref glfwSetErrorCallback - * - * @since Added in version 3.3. - * - * @ingroup init - */ -GLFWAPI int glfwGetError(const char** description); - -/*! @brief Sets the error callback. - * - * This function sets the error callback, which is called with an error code - * and a human-readable description each time a GLFW error occurs. - * - * The error code is set before the callback is called. Calling @ref - * glfwGetError from the error callback will return the same value as the error - * code argument. - * - * The error callback is called on the thread where the error occurred. If you - * are using GLFW from multiple threads, your error callback needs to be - * written accordingly. - * - * Because the description string may have been generated specifically for that - * error, it is not guaranteed to be valid after the callback has returned. If - * you wish to use it after the callback returns, you need to make a copy. - * - * Once set, the error callback remains set even after the library has been - * terminated. - * - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set. - * - * @callback_signature - * @code - * void callback_name(int error_code, const char* description) - * @endcode - * For more information about the callback parameters, see the - * [callback pointer type](@ref GLFWerrorfun). - * - * @errors None. - * - * @remark This function may be called before @ref glfwInit. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref error_handling - * @sa @ref glfwGetError - * - * @since Added in version 3.0. - * - * @ingroup init - */ -GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback); - -/*! @brief Returns the currently connected monitors. - * - * This function returns an array of handles for all currently connected - * monitors. The primary monitor is always first in the returned array. If no - * monitors were found, this function returns `NULL`. - * - * @param[out] count Where to store the number of monitors in the returned - * array. This is set to zero if an error occurred. - * @return An array of monitor handles, or `NULL` if no monitors were found or - * if an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is guaranteed to be valid only until the - * monitor configuration changes or the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_monitors - * @sa @ref monitor_event - * @sa @ref glfwGetPrimaryMonitor - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); - -/*! @brief Returns the primary monitor. - * - * This function returns the primary monitor. This is usually the monitor - * where elements like the task bar or global menu bar are located. - * - * @return The primary monitor, or `NULL` if no monitors were found or if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @remark The primary monitor is always first in the array returned by @ref - * glfwGetMonitors. - * - * @sa @ref monitor_monitors - * @sa @ref glfwGetMonitors - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); - -/*! @brief Returns the position of the monitor's viewport on the virtual screen. - * - * This function returns the position, in screen coordinates, of the upper-left - * corner of the specified monitor. - * - * Any or all of the position arguments may be `NULL`. If an error occurs, all - * non-`NULL` position arguments will be set to zero. - * - * @param[in] monitor The monitor to query. - * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. - * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_properties - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); - -/*! @brief Retrieves the work area of the monitor. - * - * This function returns the position, in screen coordinates, of the upper-left - * corner of the work area of the specified monitor along with the work area - * size in screen coordinates. The work area is defined as the area of the - * monitor not occluded by the operating system task bar where present. If no - * task bar exists then the work area is the monitor resolution in screen - * coordinates. - * - * Any or all of the position and size arguments may be `NULL`. If an error - * occurs, all non-`NULL` position and size arguments will be set to zero. - * - * @param[in] monitor The monitor to query. - * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. - * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. - * @param[out] width Where to store the monitor width, or `NULL`. - * @param[out] height Where to store the monitor height, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_workarea - * - * @since Added in version 3.3. - * - * @ingroup monitor - */ -GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); - -/*! @brief Returns the physical size of the monitor. - * - * This function returns the size, in millimetres, of the display area of the - * specified monitor. - * - * Some systems do not provide accurate monitor size information, either - * because the monitor - * [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data) - * data is incorrect or because the driver does not report it accurately. - * - * Any or all of the size arguments may be `NULL`. If an error occurs, all - * non-`NULL` size arguments will be set to zero. - * - * @param[in] monitor The monitor to query. - * @param[out] widthMM Where to store the width, in millimetres, of the - * monitor's display area, or `NULL`. - * @param[out] heightMM Where to store the height, in millimetres, of the - * monitor's display area, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @remark @win32 On Windows 8 and earlier the physical size is calculated from - * the current resolution and system DPI instead of querying the monitor EDID data. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_properties - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); - -/*! @brief Retrieves the content scale for the specified monitor. - * - * This function retrieves the content scale for the specified monitor. The - * content scale is the ratio between the current DPI and the platform's - * default DPI. This is especially important for text and any UI elements. If - * the pixel dimensions of your UI scaled by this look appropriate on your - * machine then it should appear at a reasonable size on other machines - * regardless of their DPI and scaling settings. This relies on the system DPI - * and scaling settings being somewhat correct. - * - * The content scale may depend on both the monitor resolution and pixel - * density and on user settings. It may be very different from the raw DPI - * calculated from the physical size and current resolution. - * - * @param[in] monitor The monitor to query. - * @param[out] xscale Where to store the x-axis content scale, or `NULL`. - * @param[out] yscale Where to store the y-axis content scale, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_scale - * @sa @ref glfwGetWindowContentScale - * - * @since Added in version 3.3. - * - * @ingroup monitor - */ -GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale); - -/*! @brief Returns the name of the specified monitor. - * - * This function returns a human-readable name, encoded as UTF-8, of the - * specified monitor. The name typically reflects the make and model of the - * monitor and is not guaranteed to be unique among the connected monitors. - * - * @param[in] monitor The monitor to query. - * @return The UTF-8 encoded name of the monitor, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified monitor is - * disconnected or the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_properties - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); - -/*! @brief Sets the user pointer of the specified monitor. - * - * This function sets the user-defined pointer of the specified monitor. The - * current value is retained until the monitor is disconnected. The initial - * value is `NULL`. - * - * This function may be called from the monitor callback, even for a monitor - * that is being disconnected. - * - * @param[in] monitor The monitor whose pointer to set. - * @param[in] pointer The new value. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @sa @ref monitor_userptr - * @sa @ref glfwGetMonitorUserPointer - * - * @since Added in version 3.3. - * - * @ingroup monitor - */ -GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* monitor, void* pointer); - -/*! @brief Returns the user pointer of the specified monitor. - * - * This function returns the current value of the user-defined pointer of the - * specified monitor. The initial value is `NULL`. - * - * This function may be called from the monitor callback, even for a monitor - * that is being disconnected. - * - * @param[in] monitor The monitor whose pointer to return. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @sa @ref monitor_userptr - * @sa @ref glfwSetMonitorUserPointer - * - * @since Added in version 3.3. - * - * @ingroup monitor - */ -GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); - -/*! @brief Sets the monitor configuration callback. - * - * This function sets the monitor configuration callback, or removes the - * currently set callback. This is called when a monitor is connected to or - * disconnected from the system. - * - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWmonitor* monitor, int event) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWmonitorfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_event - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun callback); - -/*! @brief Returns the available video modes for the specified monitor. - * - * This function returns an array of all video modes supported by the specified - * monitor. The returned array is sorted in ascending order, first by color - * bit depth (the sum of all channel depths), then by resolution area (the - * product of width and height), then resolution width and finally by refresh - * rate. - * - * @param[in] monitor The monitor to query. - * @param[out] count Where to store the number of video modes in the returned - * array. This is set to zero if an error occurred. - * @return An array of video modes, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified monitor is - * disconnected, this function is called again for that monitor or the library - * is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_modes - * @sa @ref glfwGetVideoMode - * - * @since Added in version 1.0. - * @glfw3 Changed to return an array of modes for a specific monitor. - * - * @ingroup monitor - */ -GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); - -/*! @brief Returns the current mode of the specified monitor. - * - * This function returns the current video mode of the specified monitor. If - * you have created a full screen window for that monitor, the return value - * will depend on whether that window is iconified. - * - * @param[in] monitor The monitor to query. - * @return The current mode of the monitor, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified monitor is - * disconnected or the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_modes - * @sa @ref glfwGetVideoModes - * - * @since Added in version 3.0. Replaces `glfwGetDesktopMode`. - * - * @ingroup monitor - */ -GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); - -/*! @brief Generates a gamma ramp and sets it for the specified monitor. - * - * This function generates an appropriately sized gamma ramp from the specified - * exponent and then calls @ref glfwSetGammaRamp with it. The value must be - * a finite number greater than zero. - * - * The software controlled gamma ramp is applied _in addition_ to the hardware - * gamma correction, which today is usually an approximation of sRGB gamma. - * This means that setting a perfectly linear ramp, or gamma 1.0, will produce - * the default (usually sRGB-like) behavior. - * - * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref - * GLFW_SRGB_CAPABLE hint. - * - * @param[in] monitor The monitor whose gamma ramp to set. - * @param[in] gamma The desired exponent. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. - * - * @remark @wayland Gamma handling is a privileged protocol, this function - * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_gamma - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); - -/*! @brief Returns the current gamma ramp for the specified monitor. - * - * This function returns the current gamma ramp of the specified monitor. - * - * @param[in] monitor The monitor to query. - * @return The current gamma ramp, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @wayland Gamma handling is a privileged protocol, this function - * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while - * returning `NULL`. - * - * @pointer_lifetime The returned structure and its arrays are allocated and - * freed by GLFW. You should not free them yourself. They are valid until the - * specified monitor is disconnected, this function is called again for that - * monitor or the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_gamma - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); - -/*! @brief Sets the current gamma ramp for the specified monitor. - * - * This function sets the current gamma ramp for the specified monitor. The - * original gamma ramp for that monitor is saved by GLFW the first time this - * function is called and is restored by @ref glfwTerminate. - * - * The software controlled gamma ramp is applied _in addition_ to the hardware - * gamma correction, which today is usually an approximation of sRGB gamma. - * This means that setting a perfectly linear ramp, or gamma 1.0, will produce - * the default (usually sRGB-like) behavior. - * - * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref - * GLFW_SRGB_CAPABLE hint. - * - * @param[in] monitor The monitor whose gamma ramp to set. - * @param[in] ramp The gamma ramp to use. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark The size of the specified gamma ramp should match the size of the - * current ramp for that monitor. - * - * @remark @win32 The gamma ramp size must be 256. - * - * @remark @wayland Gamma handling is a privileged protocol, this function - * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The specified gamma ramp is copied before this function - * returns. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref monitor_gamma - * - * @since Added in version 3.0. - * - * @ingroup monitor - */ -GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); - -/*! @brief Resets all window hints to their default values. - * - * This function resets all window hints to their - * [default values](@ref window_hints_values). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_hints - * @sa @ref glfwWindowHint - * @sa @ref glfwWindowHintString - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI void glfwDefaultWindowHints(void); - -/*! @brief Sets the specified window hint to the desired value. - * - * This function sets hints for the next call to @ref glfwCreateWindow. The - * hints, once set, retain their values until changed by a call to this - * function or @ref glfwDefaultWindowHints, or until the library is terminated. - * - * Only integer value hints can be set with this function. String value hints - * are set with @ref glfwWindowHintString. - * - * This function does not check whether the specified hint values are valid. - * If you set hints to invalid values this will instead be reported by the next - * call to @ref glfwCreateWindow. - * - * Some hints are platform specific. These may be set on any platform but they - * will only affect their specific platform. Other platforms will ignore them. - * Setting these hints requires no platform specific headers or functions. - * - * @param[in] hint The [window hint](@ref window_hints) to set. - * @param[in] value The new value of the window hint. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_hints - * @sa @ref glfwWindowHintString - * @sa @ref glfwDefaultWindowHints - * - * @since Added in version 3.0. Replaces `glfwOpenWindowHint`. - * - * @ingroup window - */ -GLFWAPI void glfwWindowHint(int hint, int value); - -/*! @brief Sets the specified window hint to the desired value. - * - * This function sets hints for the next call to @ref glfwCreateWindow. The - * hints, once set, retain their values until changed by a call to this - * function or @ref glfwDefaultWindowHints, or until the library is terminated. - * - * Only string type hints can be set with this function. Integer value hints - * are set with @ref glfwWindowHint. - * - * This function does not check whether the specified hint values are valid. - * If you set hints to invalid values this will instead be reported by the next - * call to @ref glfwCreateWindow. - * - * Some hints are platform specific. These may be set on any platform but they - * will only affect their specific platform. Other platforms will ignore them. - * Setting these hints requires no platform specific headers or functions. - * - * @param[in] hint The [window hint](@ref window_hints) to set. - * @param[in] value The new value of the window hint. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @pointer_lifetime The specified string is copied before this function - * returns. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_hints - * @sa @ref glfwWindowHint - * @sa @ref glfwDefaultWindowHints - * - * @since Added in version 3.3. - * - * @ingroup window - */ -GLFWAPI void glfwWindowHintString(int hint, const char* value); - -/*! @brief Creates a window and its associated context. - * - * This function creates a window and its associated OpenGL or OpenGL ES - * context. Most of the options controlling how the window and its context - * should be created are specified with [window hints](@ref window_hints). - * - * Successful creation does not change which context is current. Before you - * can use the newly created context, you need to - * [make it current](@ref context_current). For information about the `share` - * parameter, see @ref context_sharing. - * - * The created window, framebuffer and context may differ from what you - * requested, as not all parameters and hints are - * [hard constraints](@ref window_hints_hard). This includes the size of the - * window, especially for full screen windows. To query the actual attributes - * of the created window, framebuffer and context, see @ref - * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize. - * - * To create a full screen window, you need to specify the monitor the window - * will cover. If no monitor is specified, the window will be windowed mode. - * Unless you have a way for the user to choose a specific monitor, it is - * recommended that you pick the primary monitor. For more information on how - * to query connected monitors, see @ref monitor_monitors. - * - * For full screen windows, the specified size becomes the resolution of the - * window's _desired video mode_. As long as a full screen window is not - * iconified, the supported video mode most closely matching the desired video - * mode is set for the specified monitor. For more information about full - * screen windows, including the creation of so called _windowed full screen_ - * or _borderless full screen_ windows, see @ref window_windowed_full_screen. - * - * Once you have created the window, you can switch it between windowed and - * full screen mode with @ref glfwSetWindowMonitor. This will not affect its - * OpenGL or OpenGL ES context. - * - * By default, newly created windows use the placement recommended by the - * window system. To create the window at a specific position, make it - * initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window - * hint, set its [position](@ref window_pos) and then [show](@ref window_hide) - * it. - * - * As long as at least one full screen window is not iconified, the screensaver - * is prohibited from starting. - * - * Window systems put limits on window sizes. Very large or very small window - * dimensions may be overridden by the window system on creation. Check the - * actual [size](@ref window_size) after creation. - * - * The [swap interval](@ref buffer_swap) is not set during window creation and - * the initial value may vary depending on driver settings and defaults. - * - * @param[in] width The desired width, in screen coordinates, of the window. - * This must be greater than zero. - * @param[in] height The desired height, in screen coordinates, of the window. - * This must be greater than zero. - * @param[in] title The initial, UTF-8 encoded window title. - * @param[in] monitor The monitor to use for full screen mode, or `NULL` for - * windowed mode. - * @param[in] share The window whose context to share resources with, or `NULL` - * to not share resources. - * @return The handle of the created window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref - * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @win32 Window creation will fail if the Microsoft GDI software - * OpenGL implementation is the only one available. - * - * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it - * will be set as the initial icon for the window. If no such icon is present, - * the `IDI_APPLICATION` icon will be used instead. To set a different icon, - * see @ref glfwSetWindowIcon. - * - * @remark @win32 The context to share resources with must not be current on - * any other thread. - * - * @remark @macos The OS only supports forward-compatible core profile contexts - * for OpenGL versions 3.2 and later. Before creating an OpenGL context of - * version 3.2 or later you must set the - * [GLFW_OPENGL_FORWARD_COMPAT](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) and - * [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint) hints accordingly. - * OpenGL 3.0 and 3.1 contexts are not supported at all on macOS. - * - * @remark @macos The GLFW window has no icon, as it is not a document - * window, but the dock icon will be the same as the application bundle's icon. - * For more information on bundles, see the - * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) - * in the Mac Developer Library. - * - * @remark @macos The first time a window is created the menu bar is created. - * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu - * bar. Otherwise a minimal menu bar is created manually with common commands - * like Hide, Quit and About. The About entry opens a minimal about dialog - * with information from the application's bundle. Menu bar creation can be - * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint. - * - * @remark @macos On OS X 10.10 and later the window frame will not be rendered - * at full resolution on Retina displays unless the - * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) - * hint is `GLFW_TRUE` and the `NSHighResolutionCapable` key is enabled in the - * application bundle's `Info.plist`. For more information, see - * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) - * in the Mac Developer Library. The GLFW test and example programs use - * a custom `Info.plist` template for this, which can be found as - * `CMake/MacOSXBundleInfo.plist.in` in the source tree. - * - * @remark @macos When activating frame autosaving with - * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified - * window size and position may be overridden by previously saved values. - * - * @remark @x11 Some window managers will not respect the placement of - * initially hidden windows. - * - * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for - * a window to reach its requested state. This means you may not be able to - * query the final size, position or other attributes directly after window - * creation. - * - * @remark @x11 The class part of the `WM_CLASS` window property will by - * default be set to the window title passed to this function. The instance - * part will use the contents of the `RESOURCE_NAME` environment variable, if - * present and not empty, or fall back to the window title. Set the - * [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and - * [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to - * override this. - * - * @remark @wayland Compositors should implement the xdg-decoration protocol - * for GLFW to decorate the window properly. If this protocol isn't - * supported, or if the compositor prefers client-side decorations, a very - * simple fallback frame will be drawn using the wp_viewporter protocol. A - * compositor can still emit close, maximize or fullscreen events, using for - * instance a keybind mechanism. If neither of these protocols is supported, - * the window won't be decorated. - * - * @remark @wayland A full screen window will not attempt to change the mode, - * no matter what the requested size or refresh rate. - * - * @remark @wayland Screensaver inhibition requires the idle-inhibit protocol - * to be implemented in the user's compositor. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_creation - * @sa @ref glfwDestroyWindow - * - * @since Added in version 3.0. Replaces `glfwOpenWindow`. - * - * @ingroup window - */ -GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share); - -/*! @brief Destroys the specified window and its context. - * - * This function destroys the specified window and its context. On calling - * this function, no further callbacks will be called for that window. - * - * If the context of the specified window is current on the main thread, it is - * detached before being destroyed. - * - * @param[in] window The window to destroy. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @note The context of the specified window must not be current on any other - * thread when this function is called. - * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_creation - * @sa @ref glfwCreateWindow - * - * @since Added in version 3.0. Replaces `glfwCloseWindow`. - * - * @ingroup window - */ -GLFWAPI void glfwDestroyWindow(GLFWwindow* window); - -/*! @brief Checks the close flag of the specified window. - * - * This function returns the value of the close flag of the specified window. - * - * @param[in] window The window to query. - * @return The value of the close flag. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @sa @ref window_close - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); - -/*! @brief Sets the close flag of the specified window. - * - * This function sets the value of the close flag of the specified window. - * This can be used to override the user's attempt to close the window, or - * to signal that it should be closed. - * - * @param[in] window The window whose flag to change. - * @param[in] value The new value. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @sa @ref window_close - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); - -/*! @brief Sets the title of the specified window. - * - * This function sets the window title, encoded as UTF-8, of the specified - * window. - * - * @param[in] window The window whose title to change. - * @param[in] title The UTF-8 encoded window title. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @macos The window title will not be updated until the next time you - * process events. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_title - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); - -/*! @brief Sets the icon for the specified window. - * - * This function sets the icon of the specified window. If passed an array of - * candidate images, those of or closest to the sizes desired by the system are - * selected. If no images are specified, the window reverts to its default - * icon. - * - * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight - * bits per channel with the red channel first. They are arranged canonically - * as packed sequential rows, starting from the top-left corner. - * - * The desired image sizes varies depending on platform and system settings. - * The selected images will be rescaled as needed. Good sizes include 16x16, - * 32x32 and 48x48. - * - * @param[in] window The window whose icon to set. - * @param[in] count The number of images in the specified array, or zero to - * revert to the default window icon. - * @param[in] images The images to create the icon from. This is ignored if - * count is zero. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The specified image data is copied before this function - * returns. - * - * @remark @macos The GLFW window has no icon, as it is not a document - * window, so this function does nothing. The dock icon will be the same as - * the application bundle's icon. For more information on bundles, see the - * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) - * in the Mac Developer Library. - * - * @remark @wayland There is no existing protocol to change an icon, the - * window will thus inherit the one defined in the application's desktop file. - * This function always emits @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_icon - * - * @since Added in version 3.2. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); - -/*! @brief Retrieves the position of the content area of the specified window. - * - * This function retrieves the position, in screen coordinates, of the - * upper-left corner of the content area of the specified window. - * - * Any or all of the position arguments may be `NULL`. If an error occurs, all - * non-`NULL` position arguments will be set to zero. - * - * @param[in] window The window to query. - * @param[out] xpos Where to store the x-coordinate of the upper-left corner of - * the content area, or `NULL`. - * @param[out] ypos Where to store the y-coordinate of the upper-left corner of - * the content area, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @wayland There is no way for an application to retrieve the global - * position of its windows, this function will always emit @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_pos - * @sa @ref glfwSetWindowPos - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); - -/*! @brief Sets the position of the content area of the specified window. - * - * This function sets the position, in screen coordinates, of the upper-left - * corner of the content area of the specified windowed mode window. If the - * window is a full screen window, this function does nothing. - * - * __Do not use this function__ to move an already visible window unless you - * have very good reasons for doing so, as it will confuse and annoy the user. - * - * The window manager may put limits on what positions are allowed. GLFW - * cannot and should not override these limits. - * - * @param[in] window The window to query. - * @param[in] xpos The x-coordinate of the upper-left corner of the content area. - * @param[in] ypos The y-coordinate of the upper-left corner of the content area. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @wayland There is no way for an application to set the global - * position of its windows, this function will always emit @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_pos - * @sa @ref glfwGetWindowPos - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); - -/*! @brief Retrieves the size of the content area of the specified window. - * - * This function retrieves the size, in screen coordinates, of the content area - * of the specified window. If you wish to retrieve the size of the - * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize. - * - * Any or all of the size arguments may be `NULL`. If an error occurs, all - * non-`NULL` size arguments will be set to zero. - * - * @param[in] window The window whose size to retrieve. - * @param[out] width Where to store the width, in screen coordinates, of the - * content area, or `NULL`. - * @param[out] height Where to store the height, in screen coordinates, of the - * content area, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_size - * @sa @ref glfwSetWindowSize - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); - -/*! @brief Sets the size limits of the specified window. - * - * This function sets the size limits of the content area of the specified - * window. If the window is full screen, the size limits only take effect - * once it is made windowed. If the window is not resizable, this function - * does nothing. - * - * The size limits are applied immediately to a windowed mode window and may - * cause it to be resized. - * - * The maximum dimensions must be greater than or equal to the minimum - * dimensions and all must be greater than or equal to zero. - * - * @param[in] window The window to set limits for. - * @param[in] minwidth The minimum width, in screen coordinates, of the content - * area, or `GLFW_DONT_CARE`. - * @param[in] minheight The minimum height, in screen coordinates, of the - * content area, or `GLFW_DONT_CARE`. - * @param[in] maxwidth The maximum width, in screen coordinates, of the content - * area, or `GLFW_DONT_CARE`. - * @param[in] maxheight The maximum height, in screen coordinates, of the - * content area, or `GLFW_DONT_CARE`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. - * - * @remark If you set size limits and an aspect ratio that conflict, the - * results are undefined. - * - * @remark @wayland The size limits will not be applied until the window is - * actually resized, either by the user or by the compositor. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_sizelimits - * @sa @ref glfwSetWindowAspectRatio - * - * @since Added in version 3.2. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); - -/*! @brief Sets the aspect ratio of the specified window. - * - * This function sets the required aspect ratio of the content area of the - * specified window. If the window is full screen, the aspect ratio only takes - * effect once it is made windowed. If the window is not resizable, this - * function does nothing. - * - * The aspect ratio is specified as a numerator and a denominator and both - * values must be greater than zero. For example, the common 16:9 aspect ratio - * is specified as 16 and 9, respectively. - * - * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect - * ratio limit is disabled. - * - * The aspect ratio is applied immediately to a windowed mode window and may - * cause it to be resized. - * - * @param[in] window The window to set limits for. - * @param[in] numer The numerator of the desired aspect ratio, or - * `GLFW_DONT_CARE`. - * @param[in] denom The denominator of the desired aspect ratio, or - * `GLFW_DONT_CARE`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. - * - * @remark If you set size limits and an aspect ratio that conflict, the - * results are undefined. - * - * @remark @wayland The aspect ratio will not be applied until the window is - * actually resized, either by the user or by the compositor. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_sizelimits - * @sa @ref glfwSetWindowSizeLimits - * - * @since Added in version 3.2. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); - -/*! @brief Sets the size of the content area of the specified window. - * - * This function sets the size, in screen coordinates, of the content area of - * the specified window. - * - * For full screen windows, this function updates the resolution of its desired - * video mode and switches to the video mode closest to it, without affecting - * the window's context. As the context is unaffected, the bit depths of the - * framebuffer remain unchanged. - * - * If you wish to update the refresh rate of the desired video mode in addition - * to its resolution, see @ref glfwSetWindowMonitor. - * - * The window manager may put limits on what sizes are allowed. GLFW cannot - * and should not override these limits. - * - * @param[in] window The window to resize. - * @param[in] width The desired width, in screen coordinates, of the window - * content area. - * @param[in] height The desired height, in screen coordinates, of the window - * content area. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @wayland A full screen window will not attempt to change the mode, - * no matter what the requested size. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_size - * @sa @ref glfwGetWindowSize - * @sa @ref glfwSetWindowMonitor - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); - -/*! @brief Retrieves the size of the framebuffer of the specified window. - * - * This function retrieves the size, in pixels, of the framebuffer of the - * specified window. If you wish to retrieve the size of the window in screen - * coordinates, see @ref glfwGetWindowSize. - * - * Any or all of the size arguments may be `NULL`. If an error occurs, all - * non-`NULL` size arguments will be set to zero. - * - * @param[in] window The window whose framebuffer to query. - * @param[out] width Where to store the width, in pixels, of the framebuffer, - * or `NULL`. - * @param[out] height Where to store the height, in pixels, of the framebuffer, - * or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_fbsize - * @sa @ref glfwSetFramebufferSizeCallback - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height); - -/*! @brief Retrieves the size of the frame of the window. - * - * This function retrieves the size, in screen coordinates, of each edge of the - * frame of the specified window. This size includes the title bar, if the - * window has one. The size of the frame may vary depending on the - * [window-related hints](@ref window_hints_wnd) used to create it. - * - * Because this function retrieves the size of each window frame edge and not - * the offset along a particular coordinate axis, the retrieved values will - * always be zero or positive. - * - * Any or all of the size arguments may be `NULL`. If an error occurs, all - * non-`NULL` size arguments will be set to zero. - * - * @param[in] window The window whose frame size to query. - * @param[out] left Where to store the size, in screen coordinates, of the left - * edge of the window frame, or `NULL`. - * @param[out] top Where to store the size, in screen coordinates, of the top - * edge of the window frame, or `NULL`. - * @param[out] right Where to store the size, in screen coordinates, of the - * right edge of the window frame, or `NULL`. - * @param[out] bottom Where to store the size, in screen coordinates, of the - * bottom edge of the window frame, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_size - * - * @since Added in version 3.1. - * - * @ingroup window - */ -GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); - -/*! @brief Retrieves the content scale for the specified window. - * - * This function retrieves the content scale for the specified window. The - * content scale is the ratio between the current DPI and the platform's - * default DPI. This is especially important for text and any UI elements. If - * the pixel dimensions of your UI scaled by this look appropriate on your - * machine then it should appear at a reasonable size on other machines - * regardless of their DPI and scaling settings. This relies on the system DPI - * and scaling settings being somewhat correct. - * - * On systems where each monitors can have its own content scale, the window - * content scale will depend on which monitor the system considers the window - * to be on. - * - * @param[in] window The window to query. - * @param[out] xscale Where to store the x-axis content scale, or `NULL`. - * @param[out] yscale Where to store the y-axis content scale, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_scale - * @sa @ref glfwSetWindowContentScaleCallback - * @sa @ref glfwGetMonitorContentScale - * - * @since Added in version 3.3. - * - * @ingroup window - */ -GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale); - -/*! @brief Returns the opacity of the whole window. - * - * This function returns the opacity of the window, including any decorations. - * - * The opacity (or alpha) value is a positive finite number between zero and - * one, where zero is fully transparent and one is fully opaque. If the system - * does not support whole window transparency, this function always returns one. - * - * The initial opacity value for newly created windows is one. - * - * @param[in] window The window to query. - * @return The opacity value of the specified window. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_transparency - * @sa @ref glfwSetWindowOpacity - * - * @since Added in version 3.3. - * - * @ingroup window - */ -GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window); - -/*! @brief Sets the opacity of the whole window. - * - * This function sets the opacity of the window, including any decorations. - * - * The opacity (or alpha) value is a positive finite number between zero and - * one, where zero is fully transparent and one is fully opaque. - * - * The initial opacity value for newly created windows is one. - * - * A window created with framebuffer transparency may not use whole window - * transparency. The results of doing this are undefined. - * - * @param[in] window The window to set the opacity for. - * @param[in] opacity The desired opacity of the specified window. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_transparency - * @sa @ref glfwGetWindowOpacity - * - * @since Added in version 3.3. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); - -/*! @brief Iconifies the specified window. - * - * This function iconifies (minimizes) the specified window if it was - * previously restored. If the window is already iconified, this function does - * nothing. - * - * If the specified window is a full screen window, GLFW restores the original - * video mode of the monitor. The window's desired video mode is set again - * when the window is restored. - * - * @param[in] window The window to iconify. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_iconify - * @sa @ref glfwRestoreWindow - * @sa @ref glfwMaximizeWindow - * - * @since Added in version 2.1. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -GLFWAPI void glfwIconifyWindow(GLFWwindow* window); - -/*! @brief Restores the specified window. - * - * This function restores the specified window if it was previously iconified - * (minimized) or maximized. If the window is already restored, this function - * does nothing. - * - * If the specified window is an iconified full screen window, its desired - * video mode is set again for its monitor when the window is restored. - * - * @param[in] window The window to restore. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_iconify - * @sa @ref glfwIconifyWindow - * @sa @ref glfwMaximizeWindow - * - * @since Added in version 2.1. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -GLFWAPI void glfwRestoreWindow(GLFWwindow* window); - -/*! @brief Maximizes the specified window. - * - * This function maximizes the specified window if it was previously not - * maximized. If the window is already maximized, this function does nothing. - * - * If the specified window is a full screen window, this function does nothing. - * - * @param[in] window The window to maximize. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @par Thread Safety - * This function may only be called from the main thread. - * - * @sa @ref window_iconify - * @sa @ref glfwIconifyWindow - * @sa @ref glfwRestoreWindow - * - * @since Added in GLFW 3.2. - * - * @ingroup window - */ -GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); - -/*! @brief Makes the specified window visible. - * - * This function makes the specified window visible if it was previously - * hidden. If the window is already visible or is in full screen mode, this - * function does nothing. - * - * By default, windowed mode windows are focused when shown - * Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint - * to change this behavior for all newly created windows, or change the - * behavior for an existing window with @ref glfwSetWindowAttrib. - * - * @param[in] window The window to make visible. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @wayland Because Wayland wants every frame of the desktop to be - * complete, this function does not immediately make the window visible. - * Instead it will become visible the next time the window framebuffer is - * updated after this call. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_hide - * @sa @ref glfwHideWindow - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI void glfwShowWindow(GLFWwindow* window); - -/*! @brief Hides the specified window. - * - * This function hides the specified window if it was previously visible. If - * the window is already hidden or is in full screen mode, this function does - * nothing. - * - * @param[in] window The window to hide. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_hide - * @sa @ref glfwShowWindow - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI void glfwHideWindow(GLFWwindow* window); - -/*! @brief Brings the specified window to front and sets input focus. - * - * This function brings the specified window to front and sets input focus. - * The window should already be visible and not iconified. - * - * By default, both windowed and full screen mode windows are focused when - * initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to - * disable this behavior. - * - * Also by default, windowed mode windows are focused when shown - * with @ref glfwShowWindow. Set the - * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior. - * - * __Do not use this function__ to steal focus from other applications unless - * you are certain that is what the user wants. Focus stealing can be - * extremely disruptive. - * - * For a less disruptive way of getting the user's attention, see - * [attention requests](@ref window_attention). - * - * @param[in] window The window to give input focus. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @wayland It is not possible for an application to bring its windows - * to front, this function will always emit @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_focus - * @sa @ref window_attention - * - * @since Added in version 3.2. - * - * @ingroup window - */ -GLFWAPI void glfwFocusWindow(GLFWwindow* window); - -/*! @brief Requests user attention to the specified window. - * - * This function requests user attention to the specified window. On - * platforms where this is not supported, attention is requested to the - * application as a whole. - * - * Once the user has given attention, usually by focusing the window or - * application, the system will end the request automatically. - * - * @param[in] window The window to request attention to. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @macos Attention is requested to the application as a whole, not the - * specific window. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_attention - * - * @since Added in version 3.3. - * - * @ingroup window - */ -GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window); - -/*! @brief Returns the monitor that the window uses for full screen mode. - * - * This function returns the handle of the monitor that the specified window is - * in full screen on. - * - * @param[in] window The window to query. - * @return The monitor, or `NULL` if the window is in windowed mode or an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_monitor - * @sa @ref glfwSetWindowMonitor - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); - -/*! @brief Sets the mode, monitor, video mode and placement of a window. - * - * This function sets the monitor that the window uses for full screen mode or, - * if the monitor is `NULL`, makes it windowed mode. - * - * When setting a monitor, this function updates the width, height and refresh - * rate of the desired video mode and switches to the video mode closest to it. - * The window position is ignored when setting a monitor. - * - * When the monitor is `NULL`, the position, width and height are used to - * place the window content area. The refresh rate is ignored when no monitor - * is specified. - * - * If you only wish to update the resolution of a full screen window or the - * size of a windowed mode window, see @ref glfwSetWindowSize. - * - * When a window transitions from full screen to windowed mode, this function - * restores any previous window settings such as whether it is decorated, - * floating, resizable, has size or aspect ratio limits, etc. - * - * @param[in] window The window whose monitor, size or video mode to set. - * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. - * @param[in] xpos The desired x-coordinate of the upper-left corner of the - * content area. - * @param[in] ypos The desired y-coordinate of the upper-left corner of the - * content area. - * @param[in] width The desired with, in screen coordinates, of the content - * area or video mode. - * @param[in] height The desired height, in screen coordinates, of the content - * area or video mode. - * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, - * or `GLFW_DONT_CARE`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark The OpenGL or OpenGL ES context will not be destroyed or otherwise - * affected by any resizing or mode switching, although you may need to update - * your viewport if the framebuffer size has changed. - * - * @remark @wayland The desired window position is ignored, as there is no way - * for an application to set this property. - * - * @remark @wayland Setting the window to full screen will not attempt to - * change the mode, no matter what the requested size or refresh rate. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_monitor - * @sa @ref window_full_screen - * @sa @ref glfwGetWindowMonitor - * @sa @ref glfwSetWindowSize - * - * @since Added in version 3.2. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); - -/*! @brief Returns an attribute of the specified window. - * - * This function returns the value of an attribute of the specified window or - * its OpenGL or OpenGL ES context. - * - * @param[in] window The window to query. - * @param[in] attrib The [window attribute](@ref window_attribs) whose value to - * return. - * @return The value of the attribute, or zero if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @remark Framebuffer related hints are not window attributes. See @ref - * window_attribs_fb for more information. - * - * @remark Zero is a valid value for many window and context related - * attributes so you cannot use a return value of zero as an indication of - * errors. However, this function should not fail as long as it is passed - * valid arguments and the library has been [initialized](@ref intro_init). - * - * @remark @wayland The Wayland protocol provides no way to check whether a - * window is iconfied, so @ref GLFW_ICONIFIED always returns `GLFW_FALSE`. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_attribs - * @sa @ref glfwSetWindowAttrib - * - * @since Added in version 3.0. Replaces `glfwGetWindowParam` and - * `glfwGetGLVersion`. - * - * @ingroup window - */ -GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); - -/*! @brief Sets an attribute of the specified window. - * - * This function sets the value of an attribute of the specified window. - * - * The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), - * [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), - * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib), - * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and - * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib). - * - * Some of these attributes are ignored for full screen windows. The new - * value will take effect if the window is later made windowed. - * - * Some of these attributes are ignored for windowed mode windows. The new - * value will take effect if the window is later made full screen. - * - * @param[in] window The window to set the attribute for. - * @param[in] attrib A supported window attribute. - * @param[in] value `GLFW_TRUE` or `GLFW_FALSE`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. - * - * @remark Calling @ref glfwGetWindowAttrib will always return the latest - * value, even if that value is ignored by the current mode of the window. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_attribs - * @sa @ref glfwGetWindowAttrib - * - * @since Added in version 3.3. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowAttrib(GLFWwindow* window, int attrib, int value); - -/*! @brief Sets the user pointer of the specified window. - * - * This function sets the user-defined pointer of the specified window. The - * current value is retained until the window is destroyed. The initial value - * is `NULL`. - * - * @param[in] window The window whose pointer to set. - * @param[in] pointer The new value. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @sa @ref window_userptr - * @sa @ref glfwGetWindowUserPointer - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); - -/*! @brief Returns the user pointer of the specified window. - * - * This function returns the current value of the user-defined pointer of the - * specified window. The initial value is `NULL`. - * - * @param[in] window The window whose pointer to return. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @sa @ref window_userptr - * @sa @ref glfwSetWindowUserPointer - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); - -/*! @brief Sets the position callback for the specified window. - * - * This function sets the position callback of the specified window, which is - * called when the window is moved. The callback is provided with the - * position, in screen coordinates, of the upper-left corner of the content - * area of the window. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, int xpos, int ypos) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWwindowposfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @remark @wayland This callback will never be called, as there is no way for - * an application to know its global position. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_pos - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback); - -/*! @brief Sets the size callback for the specified window. - * - * This function sets the size callback of the specified window, which is - * called when the window is resized. The callback is provided with the size, - * in screen coordinates, of the content area of the window. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, int width, int height) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWwindowsizefun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_size - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter and return value. - * - * @ingroup window - */ -GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback); - -/*! @brief Sets the close callback for the specified window. - * - * This function sets the close callback of the specified window, which is - * called when the user attempts to close the window, for example by clicking - * the close widget in the title bar. - * - * The close flag is set before this callback is called, but you can modify it - * at any time with @ref glfwSetWindowShouldClose. - * - * The close callback is not triggered by @ref glfwDestroyWindow. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWwindowclosefun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @remark @macos Selecting Quit from the application menu will trigger the - * close callback for all windows. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_close - * - * @since Added in version 2.5. - * @glfw3 Added window handle parameter and return value. - * - * @ingroup window - */ -GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback); - -/*! @brief Sets the refresh callback for the specified window. - * - * This function sets the refresh callback of the specified window, which is - * called when the content area of the window needs to be redrawn, for example - * if the window has been exposed after having been covered by another window. - * - * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where - * the window contents are saved off-screen, this callback may be called only - * very infrequently or never at all. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window); - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWwindowrefreshfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_refresh - * - * @since Added in version 2.5. - * @glfw3 Added window handle parameter and return value. - * - * @ingroup window - */ -GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback); - -/*! @brief Sets the focus callback for the specified window. - * - * This function sets the focus callback of the specified window, which is - * called when the window gains or loses input focus. - * - * After the focus callback is called for a window that lost input focus, - * synthetic key and mouse button release events will be generated for all such - * that had been pressed. For more information, see @ref glfwSetKeyCallback - * and @ref glfwSetMouseButtonCallback. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, int focused) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWwindowfocusfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_focus - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback); - -/*! @brief Sets the iconify callback for the specified window. - * - * This function sets the iconification callback of the specified window, which - * is called when the window is iconified or restored. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, int iconified) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWwindowiconifyfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @remark @wayland The XDG-shell protocol has no event for iconification, so - * this callback will never be called. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_iconify - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun callback); - -/*! @brief Sets the maximize callback for the specified window. - * - * This function sets the maximization callback of the specified window, which - * is called when the window is maximized or restored. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, int maximized) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWwindowmaximizefun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_maximize - * - * @since Added in version 3.3. - * - * @ingroup window - */ -GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback); - -/*! @brief Sets the framebuffer resize callback for the specified window. - * - * This function sets the framebuffer resize callback of the specified window, - * which is called when the framebuffer of the specified window is resized. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, int width, int height) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWframebuffersizefun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_fbsize - * - * @since Added in version 3.0. - * - * @ingroup window - */ -GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun callback); - -/*! @brief Sets the window content scale callback for the specified window. - * - * This function sets the window content scale callback of the specified window, - * which is called when the content scale of the specified window changes. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, float xscale, float yscale) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWwindowcontentscalefun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_scale - * @sa @ref glfwGetWindowContentScale - * - * @since Added in version 3.3. - * - * @ingroup window - */ -GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun callback); - -/*! @brief Processes all pending events. - * - * This function processes only those events that are already in the event - * queue and then returns immediately. Processing events will cause the window - * and input callbacks associated with those events to be called. - * - * On some platforms, a window move, resize or menu operation will cause event - * processing to block. This is due to how event processing is designed on - * those platforms. You can use the - * [window refresh callback](@ref window_refresh) to redraw the contents of - * your window when necessary during such operations. - * - * Do not assume that callbacks you set will _only_ be called in response to - * event processing functions like this one. While it is necessary to poll for - * events, window systems that require GLFW to register callbacks of its own - * can pass events to GLFW in response to many window system function calls. - * GLFW will pass those events on to the application callbacks before - * returning. - * - * Event processing is not required for joystick input to work. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref events - * @sa @ref glfwWaitEvents - * @sa @ref glfwWaitEventsTimeout - * - * @since Added in version 1.0. - * - * @ingroup window - */ -GLFWAPI void glfwPollEvents(void); - -/*! @brief Waits until events are queued and processes them. - * - * This function puts the calling thread to sleep until at least one event is - * available in the event queue. Once one or more events are available, - * it behaves exactly like @ref glfwPollEvents, i.e. the events in the queue - * are processed and the function then returns immediately. Processing events - * will cause the window and input callbacks associated with those events to be - * called. - * - * Since not all events are associated with callbacks, this function may return - * without a callback having been called even if you are monitoring all - * callbacks. - * - * On some platforms, a window move, resize or menu operation will cause event - * processing to block. This is due to how event processing is designed on - * those platforms. You can use the - * [window refresh callback](@ref window_refresh) to redraw the contents of - * your window when necessary during such operations. - * - * Do not assume that callbacks you set will _only_ be called in response to - * event processing functions like this one. While it is necessary to poll for - * events, window systems that require GLFW to register callbacks of its own - * can pass events to GLFW in response to many window system function calls. - * GLFW will pass those events on to the application callbacks before - * returning. - * - * Event processing is not required for joystick input to work. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref events - * @sa @ref glfwPollEvents - * @sa @ref glfwWaitEventsTimeout - * - * @since Added in version 2.5. - * - * @ingroup window - */ -GLFWAPI void glfwWaitEvents(void); - -/*! @brief Waits with timeout until events are queued and processes them. - * - * This function puts the calling thread to sleep until at least one event is - * available in the event queue, or until the specified timeout is reached. If - * one or more events are available, it behaves exactly like @ref - * glfwPollEvents, i.e. the events in the queue are processed and the function - * then returns immediately. Processing events will cause the window and input - * callbacks associated with those events to be called. - * - * The timeout value must be a positive finite number. - * - * Since not all events are associated with callbacks, this function may return - * without a callback having been called even if you are monitoring all - * callbacks. - * - * On some platforms, a window move, resize or menu operation will cause event - * processing to block. This is due to how event processing is designed on - * those platforms. You can use the - * [window refresh callback](@ref window_refresh) to redraw the contents of - * your window when necessary during such operations. - * - * Do not assume that callbacks you set will _only_ be called in response to - * event processing functions like this one. While it is necessary to poll for - * events, window systems that require GLFW to register callbacks of its own - * can pass events to GLFW in response to many window system function calls. - * GLFW will pass those events on to the application callbacks before - * returning. - * - * Event processing is not required for joystick input to work. - * - * @param[in] timeout The maximum amount of time, in seconds, to wait. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. - * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref events - * @sa @ref glfwPollEvents - * @sa @ref glfwWaitEvents - * - * @since Added in version 3.2. - * - * @ingroup window - */ -GLFWAPI void glfwWaitEventsTimeout(double timeout); - -/*! @brief Posts an empty event to the event queue. - * - * This function posts an empty event from the current thread to the event - * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref events - * @sa @ref glfwWaitEvents - * @sa @ref glfwWaitEventsTimeout - * - * @since Added in version 3.1. - * - * @ingroup window - */ -GLFWAPI void glfwPostEmptyEvent(void); - -/*! @brief Returns the value of an input option for the specified window. - * - * This function returns the value of an input option for the specified window. - * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, - * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or - * @ref GLFW_RAW_MOUSE_MOTION. - * - * @param[in] window The window to query. - * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or - * `GLFW_RAW_MOUSE_MOTION`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref glfwSetInputMode - * - * @since Added in version 3.0. - * - * @ingroup input - */ -GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); - -/*! @brief Sets an input option for the specified window. - * - * This function sets an input mode option for the specified window. The mode - * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, - * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or - * @ref GLFW_RAW_MOUSE_MOTION. - * - * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor - * modes: - * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. - * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the - * content area of the window but does not restrict the cursor from leaving. - * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual - * and unlimited cursor movement. This is useful for implementing for - * example 3D camera controls. - * - * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to - * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are - * enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS` - * the next time it is called even if the key had been released before the - * call. This is useful when you are only interested in whether keys have been - * pressed but not when or in which order. - * - * If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either - * `GLFW_TRUE` to enable sticky mouse buttons, or `GLFW_FALSE` to disable it. - * If sticky mouse buttons are enabled, a mouse button press will ensure that - * @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even - * if the mouse button had been released before the call. This is useful when - * you are only interested in whether mouse buttons have been pressed but not - * when or in which order. - * - * If the mode is `GLFW_LOCK_KEY_MODS`, the value must be either `GLFW_TRUE` to - * enable lock key modifier bits, or `GLFW_FALSE` to disable them. If enabled, - * callbacks that receive modifier bits will also have the @ref - * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on, - * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on. - * - * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE` - * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is - * disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported, - * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref - * glfwRawMouseMotionSupported to check for support. - * - * @param[in] window The window whose input mode to set. - * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or - * `GLFW_RAW_MOUSE_MOTION`. - * @param[in] value The new value of the specified input mode. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref glfwGetInputMode - * - * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`. - * - * @ingroup input - */ -GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); - -/*! @brief Returns whether raw mouse motion is supported. - * - * This function returns whether raw mouse motion is supported on the current - * system. This status does not change after GLFW has been initialized so you - * only need to check this once. If you attempt to enable raw motion on - * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted. - * - * Raw mouse motion is closer to the actual motion of the mouse across - * a surface. It is not affected by the scaling and acceleration applied to - * the motion of the desktop cursor. That processing is suitable for a cursor - * while raw motion is better for controlling for example a 3D camera. Because - * of this, raw mouse motion is only provided when the cursor is disabled. - * - * @return `GLFW_TRUE` if raw mouse motion is supported on the current machine, - * or `GLFW_FALSE` otherwise. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref raw_mouse_motion - * @sa @ref glfwSetInputMode - * - * @since Added in version 3.3. - * - * @ingroup input - */ -GLFWAPI int glfwRawMouseMotionSupported(void); - -/*! @brief Returns the layout-specific name of the specified printable key. - * - * This function returns the name of the specified printable key, encoded as - * UTF-8. This is typically the character that key would produce without any - * modifier keys, intended for displaying key bindings to the user. For dead - * keys, it is typically the diacritic it would add to a character. - * - * __Do not use this function__ for [text input](@ref input_char). You will - * break text input for many languages even if it happens to work for yours. - * - * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used to identify the key, - * otherwise the scancode is ignored. If you specify a non-printable key, or - * `GLFW_KEY_UNKNOWN` and a scancode that maps to a non-printable key, this - * function returns `NULL` but does not emit an error. - * - * This behavior allows you to always pass in the arguments in the - * [key callback](@ref input_key) without modification. - * - * The printable keys are: - * - `GLFW_KEY_APOSTROPHE` - * - `GLFW_KEY_COMMA` - * - `GLFW_KEY_MINUS` - * - `GLFW_KEY_PERIOD` - * - `GLFW_KEY_SLASH` - * - `GLFW_KEY_SEMICOLON` - * - `GLFW_KEY_EQUAL` - * - `GLFW_KEY_LEFT_BRACKET` - * - `GLFW_KEY_RIGHT_BRACKET` - * - `GLFW_KEY_BACKSLASH` - * - `GLFW_KEY_WORLD_1` - * - `GLFW_KEY_WORLD_2` - * - `GLFW_KEY_0` to `GLFW_KEY_9` - * - `GLFW_KEY_A` to `GLFW_KEY_Z` - * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9` - * - `GLFW_KEY_KP_DECIMAL` - * - `GLFW_KEY_KP_DIVIDE` - * - `GLFW_KEY_KP_MULTIPLY` - * - `GLFW_KEY_KP_SUBTRACT` - * - `GLFW_KEY_KP_ADD` - * - `GLFW_KEY_KP_EQUAL` - * - * Names for printable keys depend on keyboard layout, while names for - * non-printable keys are the same across layouts but depend on the application - * language and should be localized along with other user interface text. - * - * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. - * @param[in] scancode The scancode of the key to query. - * @return The UTF-8 encoded, layout-specific name of the key, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark The contents of the returned string may change when a keyboard - * layout change event is received. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref input_key_name - * - * @since Added in version 3.2. - * - * @ingroup input - */ -GLFWAPI const char* glfwGetKeyName(int key, int scancode); - -/*! @brief Returns the platform-specific scancode of the specified key. - * - * This function returns the platform-specific scancode of the specified key. - * - * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this - * method will return `-1`. - * - * @param[in] key Any [named key](@ref keys). - * @return The platform-specific scancode for the key, or `-1` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref input_key - * - * @since Added in version 3.3. - * - * @ingroup input - */ -GLFWAPI int glfwGetKeyScancode(int key); - -/*! @brief Returns the last reported state of a keyboard key for the specified - * window. - * - * This function returns the last state reported for the specified key to the - * specified window. The returned state is one of `GLFW_PRESS` or - * `GLFW_RELEASE`. The action `GLFW_REPEAT` is only reported to the key callback. - * - * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns - * `GLFW_PRESS` the first time you call it for a key that was pressed, even if - * that key has already been released. - * - * The key functions deal with physical keys, with [key tokens](@ref keys) - * named after their use on the standard US keyboard layout. If you want to - * input text, use the Unicode character callback instead. - * - * The [modifier key bit masks](@ref mods) are not key tokens and cannot be - * used with this function. - * - * __Do not use this function__ to implement [text input](@ref input_char). - * - * @param[in] window The desired window. - * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is - * not a valid key for this function. - * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref input_key - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. - * - * @ingroup input - */ -GLFWAPI int glfwGetKey(GLFWwindow* window, int key); - -/*! @brief Returns the last reported state of a mouse button for the specified - * window. - * - * This function returns the last state reported for the specified mouse button - * to the specified window. The returned state is one of `GLFW_PRESS` or - * `GLFW_RELEASE`. - * - * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function - * returns `GLFW_PRESS` the first time you call it for a mouse button that was - * pressed, even if that mouse button has already been released. - * - * @param[in] window The desired window. - * @param[in] button The desired [mouse button](@ref buttons). - * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref input_mouse_button - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. - * - * @ingroup input - */ -GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); - -/*! @brief Retrieves the position of the cursor relative to the content area of - * the window. - * - * This function returns the position of the cursor, in screen coordinates, - * relative to the upper-left corner of the content area of the specified - * window. - * - * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor - * position is unbounded and limited only by the minimum and maximum values of - * a `double`. - * - * The coordinate can be converted to their integer equivalents with the - * `floor` function. Casting directly to an integer type works for positive - * coordinates, but fails for negative ones. - * - * Any or all of the position arguments may be `NULL`. If an error occurs, all - * non-`NULL` position arguments will be set to zero. - * - * @param[in] window The desired window. - * @param[out] xpos Where to store the cursor x-coordinate, relative to the - * left edge of the content area, or `NULL`. - * @param[out] ypos Where to store the cursor y-coordinate, relative to the to - * top edge of the content area, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref cursor_pos - * @sa @ref glfwSetCursorPos - * - * @since Added in version 3.0. Replaces `glfwGetMousePos`. - * - * @ingroup input - */ -GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); - -/*! @brief Sets the position of the cursor, relative to the content area of the - * window. - * - * This function sets the position, in screen coordinates, of the cursor - * relative to the upper-left corner of the content area of the specified - * window. The window must have input focus. If the window does not have - * input focus when this function is called, it fails silently. - * - * __Do not use this function__ to implement things like camera controls. GLFW - * already provides the `GLFW_CURSOR_DISABLED` cursor mode that hides the - * cursor, transparently re-centers it and provides unconstrained cursor - * motion. See @ref glfwSetInputMode for more information. - * - * If the cursor mode is `GLFW_CURSOR_DISABLED` then the cursor position is - * unconstrained and limited only by the minimum and maximum values of - * a `double`. - * - * @param[in] window The desired window. - * @param[in] xpos The desired x-coordinate, relative to the left edge of the - * content area. - * @param[in] ypos The desired y-coordinate, relative to the top edge of the - * content area. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @wayland This function will only work when the cursor mode is - * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref cursor_pos - * @sa @ref glfwGetCursorPos - * - * @since Added in version 3.0. Replaces `glfwSetMousePos`. - * - * @ingroup input - */ -GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); - -/*! @brief Creates a custom cursor. - * - * Creates a new custom cursor image that can be set for a window with @ref - * glfwSetCursor. The cursor can be destroyed with @ref glfwDestroyCursor. - * Any remaining cursors are destroyed by @ref glfwTerminate. - * - * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight - * bits per channel with the red channel first. They are arranged canonically - * as packed sequential rows, starting from the top-left corner. - * - * The cursor hotspot is specified in pixels, relative to the upper-left corner - * of the cursor image. Like all other coordinate systems in GLFW, the X-axis - * points to the right and the Y-axis points down. - * - * @param[in] image The desired cursor image. - * @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot. - * @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot. - * @return The handle of the created cursor, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The specified image data is copied before this function - * returns. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref cursor_object - * @sa @ref glfwDestroyCursor - * @sa @ref glfwCreateStandardCursor - * - * @since Added in version 3.1. - * - * @ingroup input - */ -GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot); - -/*! @brief Creates a cursor with a standard shape. - * - * Returns a cursor with a [standard shape](@ref shapes), that can be set for - * a window with @ref glfwSetCursor. - * - * @param[in] shape One of the [standard shapes](@ref shapes). - * @return A new cursor ready to use or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref cursor_object - * @sa @ref glfwCreateCursor - * - * @since Added in version 3.1. - * - * @ingroup input - */ -GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); - -/*! @brief Destroys a cursor. - * - * This function destroys a cursor previously created with @ref - * glfwCreateCursor. Any remaining cursors will be destroyed by @ref - * glfwTerminate. - * - * If the specified cursor is current for any window, that window will be - * reverted to the default cursor. This does not affect the cursor mode. - * - * @param[in] cursor The cursor object to destroy. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref cursor_object - * @sa @ref glfwCreateCursor - * - * @since Added in version 3.1. - * - * @ingroup input - */ -GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); - -/*! @brief Sets the cursor for the window. - * - * This function sets the cursor image to be used when the cursor is over the - * content area of the specified window. The set cursor will only be visible - * when the [cursor mode](@ref cursor_mode) of the window is - * `GLFW_CURSOR_NORMAL`. - * - * On some platforms, the set cursor may not be visible unless the window also - * has input focus. - * - * @param[in] window The window to set the cursor for. - * @param[in] cursor The cursor to set, or `NULL` to switch back to the default - * arrow cursor. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref cursor_object - * - * @since Added in version 3.1. - * - * @ingroup input - */ -GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); - -/*! @brief Sets the key callback. - * - * This function sets the key callback of the specified window, which is called - * when a key is pressed, repeated or released. - * - * The key functions deal with physical keys, with layout independent - * [key tokens](@ref keys) named after their values in the standard US keyboard - * layout. If you want to input text, use the - * [character callback](@ref glfwSetCharCallback) instead. - * - * When a window loses input focus, it will generate synthetic key release - * events for all pressed keys. You can tell these events from user-generated - * events by the fact that the synthetic ones are generated after the focus - * loss event has been processed, i.e. after the - * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. - * - * The scancode of a key is specific to that platform or sometimes even to that - * machine. Scancodes are intended to allow users to bind keys that don't have - * a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their - * state is not saved and so it cannot be queried with @ref glfwGetKey. - * - * Sometimes GLFW needs to generate synthetic key events, in which case the - * scancode may be zero. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new key callback, or `NULL` to remove the currently - * set callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWkeyfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref input_key - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter and return value. - * - * @ingroup input - */ -GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback); - -/*! @brief Sets the Unicode character callback. - * - * This function sets the character callback of the specified window, which is - * called when a Unicode character is input. - * - * The character callback is intended for Unicode text input. As it deals with - * characters, it is keyboard layout dependent, whereas the - * [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1 - * to physical keys, as a key may produce zero, one or more characters. If you - * want to know whether a specific physical key was pressed or released, see - * the key callback instead. - * - * The character callback behaves as system text input normally does and will - * not be called if modifier keys are held down that would prevent normal text - * input on that platform, for example a Super (Command) key on macOS or Alt key - * on Windows. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, unsigned int codepoint) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWcharfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref input_char - * - * @since Added in version 2.4. - * @glfw3 Added window handle parameter and return value. - * - * @ingroup input - */ -GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun callback); - -/*! @brief Sets the Unicode character with modifiers callback. - * - * This function sets the character with modifiers callback of the specified - * window, which is called when a Unicode character is input regardless of what - * modifier keys are used. - * - * The character with modifiers callback is intended for implementing custom - * Unicode character input. For regular Unicode text input, see the - * [character callback](@ref glfwSetCharCallback). Like the character - * callback, the character with modifiers callback deals with characters and is - * keyboard layout dependent. Characters do not map 1:1 to physical keys, as - * a key may produce zero, one or more characters. If you want to know whether - * a specific physical key was pressed or released, see the - * [key callback](@ref glfwSetKeyCallback) instead. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or an - * [error](@ref error_handling) occurred. - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWcharmodsfun). - * - * @deprecated Scheduled for removal in version 4.0. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref input_char - * - * @since Added in version 3.1. - * - * @ingroup input - */ -GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun callback); - -/*! @brief Sets the mouse button callback. - * - * This function sets the mouse button callback of the specified window, which - * is called when a mouse button is pressed or released. - * - * When a window loses input focus, it will generate synthetic mouse button - * release events for all pressed mouse buttons. You can tell these events - * from user-generated events by the fact that the synthetic ones are generated - * after the focus loss event has been processed, i.e. after the - * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, int button, int action, int mods) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWmousebuttonfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref input_mouse_button - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter and return value. - * - * @ingroup input - */ -GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback); - -/*! @brief Sets the cursor position callback. - * - * This function sets the cursor position callback of the specified window, - * which is called when the cursor is moved. The callback is provided with the - * position, in screen coordinates, relative to the upper-left corner of the - * content area of the window. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, double xpos, double ypos); - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWcursorposfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref cursor_pos - * - * @since Added in version 3.0. Replaces `glfwSetMousePosCallback`. - * - * @ingroup input - */ -GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback); - -/*! @brief Sets the cursor enter/leave callback. - * - * This function sets the cursor boundary crossing callback of the specified - * window, which is called when the cursor enters or leaves the content area of - * the window. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, int entered) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWcursorenterfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref cursor_enter - * - * @since Added in version 3.0. - * - * @ingroup input - */ -GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun callback); - -/*! @brief Sets the scroll callback. - * - * This function sets the scroll callback of the specified window, which is - * called when a scrolling device is used, such as a mouse wheel or scrolling - * area of a touchpad. - * - * The scroll callback receives all scrolling input, like that from a mouse - * wheel or a touchpad scrolling area. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new scroll callback, or `NULL` to remove the - * currently set callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, double xoffset, double yoffset) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWscrollfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref scrolling - * - * @since Added in version 3.0. Replaces `glfwSetMouseWheelCallback`. - * - * @ingroup input - */ -GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback); - -/*! @brief Sets the path drop callback. - * - * This function sets the path drop callback of the specified window, which is - * called when one or more dragged paths are dropped on the window. - * - * Because the path array and its strings may have been generated specifically - * for that event, they are not guaranteed to be valid after the callback has - * returned. If you wish to use them after the callback returns, you need to - * make a deep copy. - * - * @param[in] window The window whose callback to set. - * @param[in] callback The new file drop callback, or `NULL` to remove the - * currently set callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(GLFWwindow* window, int path_count, const char* paths[]) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWdropfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @remark @wayland File drop is currently unimplemented. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref path_drop - * - * @since Added in version 3.1. - * - * @ingroup input - */ -GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback); - -/*! @brief Returns whether the specified joystick is present. - * - * This function returns whether the specified joystick is present. - * - * There is no need to call this function before other functions that accept - * a joystick ID, as they all check for presence before performing any other - * work. - * - * @param[in] jid The [joystick](@ref joysticks) to query. - * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref joystick - * - * @since Added in version 3.0. Replaces `glfwGetJoystickParam`. - * - * @ingroup input - */ -GLFWAPI int glfwJoystickPresent(int jid); - -/*! @brief Returns the values of all axes of the specified joystick. - * - * This function returns the values of all axes of the specified joystick. - * Each element in the array is a value between -1.0 and 1.0. - * - * If the specified joystick is not present this function will return `NULL` - * but will not generate an error. This can be used instead of first calling - * @ref glfwJoystickPresent. - * - * @param[in] jid The [joystick](@ref joysticks) to query. - * @param[out] count Where to store the number of axis values in the returned - * array. This is set to zero if the joystick is not present or an error - * occurred. - * @return An array of axis values, or `NULL` if the joystick is not present or - * an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified joystick is - * disconnected or the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref joystick_axis - * - * @since Added in version 3.0. Replaces `glfwGetJoystickPos`. - * - * @ingroup input - */ -GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); - -/*! @brief Returns the state of all buttons of the specified joystick. - * - * This function returns the state of all buttons of the specified joystick. - * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. - * - * For backward compatibility with earlier versions that did not have @ref - * glfwGetJoystickHats, the button array also includes all hats, each - * represented as four buttons. The hats are in the same order as returned by - * __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and - * _left_. To disable these extra buttons, set the @ref - * GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization. - * - * If the specified joystick is not present this function will return `NULL` - * but will not generate an error. This can be used instead of first calling - * @ref glfwJoystickPresent. - * - * @param[in] jid The [joystick](@ref joysticks) to query. - * @param[out] count Where to store the number of button states in the returned - * array. This is set to zero if the joystick is not present or an error - * occurred. - * @return An array of button states, or `NULL` if the joystick is not present - * or an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified joystick is - * disconnected or the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref joystick_button - * - * @since Added in version 2.2. - * @glfw3 Changed to return a dynamic array. - * - * @ingroup input - */ -GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); - -/*! @brief Returns the state of all hats of the specified joystick. - * - * This function returns the state of all hats of the specified joystick. - * Each element in the array is one of the following values: - * - * Name | Value - * ---- | ----- - * `GLFW_HAT_CENTERED` | 0 - * `GLFW_HAT_UP` | 1 - * `GLFW_HAT_RIGHT` | 2 - * `GLFW_HAT_DOWN` | 4 - * `GLFW_HAT_LEFT` | 8 - * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` - * `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` - * `GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` - * `GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` - * - * The diagonal directions are bitwise combinations of the primary (up, right, - * down and left) directions and you can test for these individually by ANDing - * it with the corresponding direction. - * - * @code - * if (hats[2] & GLFW_HAT_RIGHT) - * { - * // State of hat 2 could be right-up, right or right-down - * } - * @endcode - * - * If the specified joystick is not present this function will return `NULL` - * but will not generate an error. This can be used instead of first calling - * @ref glfwJoystickPresent. - * - * @param[in] jid The [joystick](@ref joysticks) to query. - * @param[out] count Where to store the number of hat states in the returned - * array. This is set to zero if the joystick is not present or an error - * occurred. - * @return An array of hat states, or `NULL` if the joystick is not present - * or an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified joystick is - * disconnected, this function is called again for that joystick or the library - * is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref joystick_hat - * - * @since Added in version 3.3. - * - * @ingroup input - */ -GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); - -/*! @brief Returns the name of the specified joystick. - * - * This function returns the name, encoded as UTF-8, of the specified joystick. - * The returned string is allocated and freed by GLFW. You should not free it - * yourself. - * - * If the specified joystick is not present this function will return `NULL` - * but will not generate an error. This can be used instead of first calling - * @ref glfwJoystickPresent. - * - * @param[in] jid The [joystick](@ref joysticks) to query. - * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick - * is not present or an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified joystick is - * disconnected or the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref joystick_name - * - * @since Added in version 3.0. - * - * @ingroup input - */ -GLFWAPI const char* glfwGetJoystickName(int jid); - -/*! @brief Returns the SDL compatible GUID of the specified joystick. - * - * This function returns the SDL compatible GUID, as a UTF-8 encoded - * hexadecimal string, of the specified joystick. The returned string is - * allocated and freed by GLFW. You should not free it yourself. - * - * The GUID is what connects a joystick to a gamepad mapping. A connected - * joystick will always have a GUID even if there is no gamepad mapping - * assigned to it. - * - * If the specified joystick is not present this function will return `NULL` - * but will not generate an error. This can be used instead of first calling - * @ref glfwJoystickPresent. - * - * The GUID uses the format introduced in SDL 2.0.5. This GUID tries to - * uniquely identify the make and model of a joystick but does not identify - * a specific unit, e.g. all wired Xbox 360 controllers will have the same - * GUID on that platform. The GUID for a unit may vary between platforms - * depending on what hardware information the platform specific APIs provide. - * - * @param[in] jid The [joystick](@ref joysticks) to query. - * @return The UTF-8 encoded GUID of the joystick, or `NULL` if the joystick - * is not present or an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified joystick is - * disconnected or the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref gamepad - * - * @since Added in version 3.3. - * - * @ingroup input - */ -GLFWAPI const char* glfwGetJoystickGUID(int jid); - -/*! @brief Sets the user pointer of the specified joystick. - * - * This function sets the user-defined pointer of the specified joystick. The - * current value is retained until the joystick is disconnected. The initial - * value is `NULL`. - * - * This function may be called from the joystick callback, even for a joystick - * that is being disconnected. - * - * @param[in] jid The joystick whose pointer to set. - * @param[in] pointer The new value. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @sa @ref joystick_userptr - * @sa @ref glfwGetJoystickUserPointer - * - * @since Added in version 3.3. - * - * @ingroup input - */ -GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer); - -/*! @brief Returns the user pointer of the specified joystick. - * - * This function returns the current value of the user-defined pointer of the - * specified joystick. The initial value is `NULL`. - * - * This function may be called from the joystick callback, even for a joystick - * that is being disconnected. - * - * @param[in] jid The joystick whose pointer to return. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @sa @ref joystick_userptr - * @sa @ref glfwSetJoystickUserPointer - * - * @since Added in version 3.3. - * - * @ingroup input - */ -GLFWAPI void* glfwGetJoystickUserPointer(int jid); - -/*! @brief Returns whether the specified joystick has a gamepad mapping. - * - * This function returns whether the specified joystick is both present and has - * a gamepad mapping. - * - * If the specified joystick is present but does not have a gamepad mapping - * this function will return `GLFW_FALSE` but will not generate an error. Call - * @ref glfwJoystickPresent to check if a joystick is present regardless of - * whether it has a mapping. - * - * @param[in] jid The [joystick](@ref joysticks) to query. - * @return `GLFW_TRUE` if a joystick is both present and has a gamepad mapping, - * or `GLFW_FALSE` otherwise. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref gamepad - * @sa @ref glfwGetGamepadState - * - * @since Added in version 3.3. - * - * @ingroup input - */ -GLFWAPI int glfwJoystickIsGamepad(int jid); - -/*! @brief Sets the joystick configuration callback. - * - * This function sets the joystick configuration callback, or removes the - * currently set callback. This is called when a joystick is connected to or - * disconnected from the system. - * - * For joystick connection and disconnection events to be delivered on all - * platforms, you need to call one of the [event processing](@ref events) - * functions. Joystick disconnection may also be detected and the callback - * called by joystick functions. The function will then return whatever it - * returns if the joystick is not present. - * - * @param[in] callback The new callback, or `NULL` to remove the currently set - * callback. - * @return The previously set callback, or `NULL` if no callback was set or the - * library had not been [initialized](@ref intro_init). - * - * @callback_signature - * @code - * void function_name(int jid, int event) - * @endcode - * For more information about the callback parameters, see the - * [function pointer type](@ref GLFWjoystickfun). - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref joystick_event - * - * @since Added in version 3.2. - * - * @ingroup input - */ -GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback); - -/*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. - * - * This function parses the specified ASCII encoded string and updates the - * internal list with any gamepad mappings it finds. This string may - * contain either a single gamepad mapping or many mappings separated by - * newlines. The parser supports the full format of the `gamecontrollerdb.txt` - * source file including empty lines and comments. - * - * See @ref gamepad_mapping for a description of the format. - * - * If there is already a gamepad mapping for a given GUID in the internal list, - * it will be replaced by the one passed to this function. If the library is - * terminated and re-initialized the internal list will revert to the built-in - * default. - * - * @param[in] string The string containing the gamepad mappings. - * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_VALUE. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref gamepad - * @sa @ref glfwJoystickIsGamepad - * @sa @ref glfwGetGamepadName - * - * @since Added in version 3.3. - * - * @ingroup input - */ -GLFWAPI int glfwUpdateGamepadMappings(const char* string); - -/*! @brief Returns the human-readable gamepad name for the specified joystick. - * - * This function returns the human-readable name of the gamepad from the - * gamepad mapping assigned to the specified joystick. - * - * If the specified joystick is not present or does not have a gamepad mapping - * this function will return `NULL` but will not generate an error. Call - * @ref glfwJoystickPresent to check whether it is present regardless of - * whether it has a mapping. - * - * @param[in] jid The [joystick](@ref joysticks) to query. - * @return The UTF-8 encoded name of the gamepad, or `NULL` if the - * joystick is not present, does not have a mapping or an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref GLFW_INVALID_ENUM. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified joystick is - * disconnected, the gamepad mappings are updated or the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref gamepad - * @sa @ref glfwJoystickIsGamepad - * - * @since Added in version 3.3. - * - * @ingroup input - */ -GLFWAPI const char* glfwGetGamepadName(int jid); - -/*! @brief Retrieves the state of the specified joystick remapped as a gamepad. - * - * This function retrieves the state of the specified joystick remapped to - * an Xbox-like gamepad. - * - * If the specified joystick is not present or does not have a gamepad mapping - * this function will return `GLFW_FALSE` but will not generate an error. Call - * @ref glfwJoystickPresent to check whether it is present regardless of - * whether it has a mapping. - * - * The Guide button may not be available for input as it is often hooked by the - * system or the Steam client. - * - * Not all devices have all the buttons or axes provided by @ref - * GLFWgamepadstate. Unavailable buttons and axes will always report - * `GLFW_RELEASE` and 0.0 respectively. - * - * @param[in] jid The [joystick](@ref joysticks) to query. - * @param[out] state The gamepad input state of the joystick. - * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if no joystick is - * connected, it has no gamepad mapping or an [error](@ref error_handling) - * occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref gamepad - * @sa @ref glfwUpdateGamepadMappings - * @sa @ref glfwJoystickIsGamepad - * - * @since Added in version 3.3. - * - * @ingroup input - */ -GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); - -/*! @brief Sets the clipboard to the specified string. - * - * This function sets the system clipboard to the specified, UTF-8 encoded - * string. - * - * @param[in] window Deprecated. Any valid window or `NULL`. - * @param[in] string A UTF-8 encoded string. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The specified string is copied before this function - * returns. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref clipboard - * @sa @ref glfwGetClipboardString - * - * @since Added in version 3.0. - * - * @ingroup input - */ -GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); - -/*! @brief Returns the contents of the clipboard as a string. - * - * This function returns the contents of the system clipboard, if it contains - * or is convertible to a UTF-8 encoded string. If the clipboard is empty or - * if its contents cannot be converted, `NULL` is returned and a @ref - * GLFW_FORMAT_UNAVAILABLE error is generated. - * - * @param[in] window Deprecated. Any valid window or `NULL`. - * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` - * if an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the next call to @ref - * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library - * is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref clipboard - * @sa @ref glfwSetClipboardString - * - * @since Added in version 3.0. - * - * @ingroup input - */ -GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); - -/*! @brief Returns the GLFW time. - * - * This function returns the current GLFW time, in seconds. Unless the time - * has been set using @ref glfwSetTime it measures time elapsed since GLFW was - * initialized. - * - * This function and @ref glfwSetTime are helper functions on top of @ref - * glfwGetTimerFrequency and @ref glfwGetTimerValue. - * - * The resolution of the timer is system dependent, but is usually on the order - * of a few micro- or nanoseconds. It uses the highest-resolution monotonic - * time source on each supported platform. - * - * @return The current time, in seconds, or zero if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Reading and - * writing of the internal base time is not atomic, so it needs to be - * externally synchronized with calls to @ref glfwSetTime. - * - * @sa @ref time - * - * @since Added in version 1.0. - * - * @ingroup input - */ -GLFWAPI double glfwGetTime(void); - -/*! @brief Sets the GLFW time. - * - * This function sets the current GLFW time, in seconds. The value must be - * a positive finite number less than or equal to 18446744073.0, which is - * approximately 584.5 years. - * - * This function and @ref glfwGetTime are helper functions on top of @ref - * glfwGetTimerFrequency and @ref glfwGetTimerValue. - * - * @param[in] time The new value, in seconds. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_VALUE. - * - * @remark The upper limit of GLFW time is calculated as - * floor((264 - 1) / 109) and is due to implementations - * storing nanoseconds in 64 bits. The limit may be increased in the future. - * - * @thread_safety This function may be called from any thread. Reading and - * writing of the internal base time is not atomic, so it needs to be - * externally synchronized with calls to @ref glfwGetTime. - * - * @sa @ref time - * - * @since Added in version 2.2. - * - * @ingroup input - */ -GLFWAPI void glfwSetTime(double time); - -/*! @brief Returns the current value of the raw timer. - * - * This function returns the current value of the raw timer, measured in - * 1 / frequency seconds. To get the frequency, call @ref - * glfwGetTimerFrequency. - * - * @return The value of the timer, or zero if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref time - * @sa @ref glfwGetTimerFrequency - * - * @since Added in version 3.2. - * - * @ingroup input - */ -GLFWAPI uint64_t glfwGetTimerValue(void); - -/*! @brief Returns the frequency, in Hz, of the raw timer. - * - * This function returns the frequency, in Hz, of the raw timer. - * - * @return The frequency of the timer, in Hz, or zero if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref time - * @sa @ref glfwGetTimerValue - * - * @since Added in version 3.2. - * - * @ingroup input - */ -GLFWAPI uint64_t glfwGetTimerFrequency(void); - -/*! @brief Makes the context of the specified window current for the calling - * thread. - * - * This function makes the OpenGL or OpenGL ES context of the specified window - * current on the calling thread. A context must only be made current on - * a single thread at a time and each thread can have only a single current - * context at a time. - * - * When moving a context between threads, you must make it non-current on the - * old thread before making it current on the new one. - * - * By default, making a context non-current implicitly forces a pipeline flush. - * On machines that support `GL_KHR_context_flush_control`, you can control - * whether a context performs this flush by setting the - * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) - * hint. - * - * The specified window must have an OpenGL or OpenGL ES context. Specifying - * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT - * error. - * - * @param[in] window The window whose context to make current, or `NULL` to - * detach the current context. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref context_current - * @sa @ref glfwGetCurrentContext - * - * @since Added in version 3.0. - * - * @ingroup context - */ -GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); - -/*! @brief Returns the window whose context is current on the calling thread. - * - * This function returns the window whose OpenGL or OpenGL ES context is - * current on the calling thread. - * - * @return The window whose context is current, or `NULL` if no window's - * context is current. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref context_current - * @sa @ref glfwMakeContextCurrent - * - * @since Added in version 3.0. - * - * @ingroup context - */ -GLFWAPI GLFWwindow* glfwGetCurrentContext(void); - -/*! @brief Swaps the front and back buffers of the specified window. - * - * This function swaps the front and back buffers of the specified window when - * rendering with OpenGL or OpenGL ES. If the swap interval is greater than - * zero, the GPU driver waits the specified number of screen updates before - * swapping the buffers. - * - * The specified window must have an OpenGL or OpenGL ES context. Specifying - * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT - * error. - * - * This function does not apply to Vulkan. If you are rendering with Vulkan, - * see `vkQueuePresentKHR` instead. - * - * @param[in] window The window whose buffers to swap. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. - * - * @remark __EGL:__ The context of the specified window must be current on the - * calling thread. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref buffer_swap - * @sa @ref glfwSwapInterval - * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -GLFWAPI void glfwSwapBuffers(GLFWwindow* window); - -/*! @brief Sets the swap interval for the current context. - * - * This function sets the swap interval for the current OpenGL or OpenGL ES - * context, i.e. the number of screen updates to wait from the time @ref - * glfwSwapBuffers was called before swapping the buffers and returning. This - * is sometimes called _vertical synchronization_, _vertical retrace - * synchronization_ or just _vsync_. - * - * A context that supports either of the `WGL_EXT_swap_control_tear` and - * `GLX_EXT_swap_control_tear` extensions also accepts _negative_ swap - * intervals, which allows the driver to swap immediately even if a frame - * arrives a little bit late. You can check for these extensions with @ref - * glfwExtensionSupported. - * - * A context must be current on the calling thread. Calling this function - * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. - * - * This function does not apply to Vulkan. If you are rendering with Vulkan, - * see the present mode of your swapchain instead. - * - * @param[in] interval The minimum number of screen updates to wait for - * until the buffers are swapped by @ref glfwSwapBuffers. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. - * - * @remark This function is not called during context creation, leaving the - * swap interval set to whatever is the default on that platform. This is done - * because some swap interval extensions used by GLFW do not allow the swap - * interval to be reset to zero once it has been set to a non-zero value. - * - * @remark Some GPU drivers do not honor the requested swap interval, either - * because of a user setting that overrides the application's request or due to - * bugs in the driver. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref buffer_swap - * @sa @ref glfwSwapBuffers - * - * @since Added in version 1.0. - * - * @ingroup context - */ -GLFWAPI void glfwSwapInterval(int interval); - -/*! @brief Returns whether the specified extension is available. - * - * This function returns whether the specified - * [API extension](@ref context_glext) is supported by the current OpenGL or - * OpenGL ES context. It searches both for client API extension and context - * creation API extensions. - * - * A context must be current on the calling thread. Calling this function - * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. - * - * As this functions retrieves and searches one or more extension strings each - * call, it is recommended that you cache its results if it is going to be used - * frequently. The extension strings will not change during the lifetime of - * a context, so there is no danger in doing this. - * - * This function does not apply to Vulkan. If you are using Vulkan, see @ref - * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties` - * and `vkEnumerateDeviceExtensionProperties` instead. - * - * @param[in] extension The ASCII encoded name of the extension. - * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE` - * otherwise. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref context_glext - * @sa @ref glfwGetProcAddress - * - * @since Added in version 1.0. - * - * @ingroup context - */ -GLFWAPI int glfwExtensionSupported(const char* extension); - -/*! @brief Returns the address of the specified function for the current - * context. - * - * This function returns the address of the specified OpenGL or OpenGL ES - * [core or extension function](@ref context_glext), if it is supported - * by the current context. - * - * A context must be current on the calling thread. Calling this function - * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. - * - * This function does not apply to Vulkan. If you are rendering with Vulkan, - * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and - * `vkGetDeviceProcAddr` instead. - * - * @param[in] procname The ASCII encoded name of the function. - * @return The address of the function, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. - * - * @remark The address of a given function is not guaranteed to be the same - * between contexts. - * - * @remark This function may return a non-`NULL` address despite the - * associated version or extension not being available. Always check the - * context version or extension string first. - * - * @pointer_lifetime The returned function pointer is valid until the context - * is destroyed or the library is terminated. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref context_glext - * @sa @ref glfwExtensionSupported - * - * @since Added in version 1.0. - * - * @ingroup context - */ -GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); - -/*! @brief Returns whether the Vulkan loader and an ICD have been found. - * - * This function returns whether the Vulkan loader and any minimally functional - * ICD have been found. - * - * The availability of a Vulkan loader and even an ICD does not by itself guarantee that - * surface creation or even instance creation is possible. Call @ref - * glfwGetRequiredInstanceExtensions to check whether the extensions necessary for Vulkan - * surface creation are available and @ref glfwGetPhysicalDevicePresentationSupport to - * check whether a queue family of a physical device supports image presentation. - * - * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` - * otherwise. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref vulkan_support - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -GLFWAPI int glfwVulkanSupported(void); - -/*! @brief Returns the Vulkan instance extensions required by GLFW. - * - * This function returns an array of names of Vulkan instance extensions required - * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the - * list will always contain `VK_KHR_surface`, so if you don't require any - * additional extensions you can pass this list directly to the - * `VkInstanceCreateInfo` struct. - * - * If Vulkan is not available on the machine, this function returns `NULL` and - * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is at least minimally available. - * - * If Vulkan is available but no set of extensions allowing window surface - * creation was found, this function returns `NULL`. You may still use Vulkan - * for off-screen rendering and compute work. - * - * @param[out] count Where to store the number of extensions in the returned - * array. This is set to zero if an error occurred. - * @return An array of ASCII encoded extension names, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_API_UNAVAILABLE. - * - * @remark Additional extensions may be required by future versions of GLFW. - * You should check if any extensions you wish to enable are already in the - * returned array, as it is an error to specify an extension more than once in - * the `VkInstanceCreateInfo` struct. - * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is guaranteed to be valid only until the - * library is terminated. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref vulkan_ext - * @sa @ref glfwCreateWindowSurface - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count); - -#if defined(VK_VERSION_1_0) - -/*! @brief Returns the address of the specified Vulkan instance function. - * - * This function returns the address of the specified Vulkan core or extension - * function for the specified instance. If instance is set to `NULL` it can - * return any function exported from the Vulkan loader, including at least the - * following functions: - * - * - `vkEnumerateInstanceExtensionProperties` - * - `vkEnumerateInstanceLayerProperties` - * - `vkCreateInstance` - * - `vkGetInstanceProcAddr` - * - * If Vulkan is not available on the machine, this function returns `NULL` and - * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is at least minimally available. - * - * This function is equivalent to calling `vkGetInstanceProcAddr` with - * a platform-specific query of the Vulkan loader as a fallback. - * - * @param[in] instance The Vulkan instance to query, or `NULL` to retrieve - * functions related to instance creation. - * @param[in] procname The ASCII encoded name of the function. - * @return The address of the function, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_API_UNAVAILABLE. - * - * @pointer_lifetime The returned function pointer is valid until the library - * is terminated. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref vulkan_proc - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname); - -/*! @brief Returns whether the specified queue family can present images. - * - * This function returns whether the specified queue family of the specified - * physical device supports presentation to the platform GLFW was built for. - * - * If Vulkan or the required window surface creation instance extensions are - * not available on the machine, or if the specified instance was not created - * with the required extensions, this function returns `GLFW_FALSE` and - * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is at least minimally available and @ref - * glfwGetRequiredInstanceExtensions to check what instance extensions are - * required. - * - * @param[in] instance The instance that the physical device belongs to. - * @param[in] device The physical device that the queue family belongs to. - * @param[in] queuefamily The index of the queue family to query. - * @return `GLFW_TRUE` if the queue family supports presentation, or - * `GLFW_FALSE` otherwise. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. - * - * @remark @macos This function currently always returns `GLFW_TRUE`, as the - * `VK_MVK_macos_surface` and `VK_EXT_metal_surface` extensions do not provide - * a `vkGetPhysicalDevice*PresentationSupport` type function. - * - * @thread_safety This function may be called from any thread. For - * synchronization details of Vulkan objects, see the Vulkan specification. - * - * @sa @ref vulkan_present - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); - -/*! @brief Creates a Vulkan surface for the specified window. - * - * This function creates a Vulkan surface for the specified window. - * - * If the Vulkan loader or at least one minimally functional ICD were not found, - * this function returns `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref - * GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported to check whether - * Vulkan is at least minimally available. - * - * If the required window surface creation instance extensions are not - * available or if the specified instance was not created with these extensions - * enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and - * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref - * glfwGetRequiredInstanceExtensions to check what instance extensions are - * required. - * - * The window surface cannot be shared with another API so the window must - * have been created with the [client api hint](@ref GLFW_CLIENT_API_attrib) - * set to `GLFW_NO_API` otherwise it generates a @ref GLFW_INVALID_VALUE error - * and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`. - * - * The window surface must be destroyed before the specified Vulkan instance. - * It is the responsibility of the caller to destroy the window surface. GLFW - * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the - * surface. - * - * @param[in] instance The Vulkan instance to create the surface in. - * @param[in] window The window to create the surface for. - * @param[in] allocator The allocator to use, or `NULL` to use the default - * allocator. - * @param[out] surface Where to store the handle of the surface. This is set - * to `VK_NULL_HANDLE` if an error occurred. - * @return `VK_SUCCESS` if successful, or a Vulkan error code if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_API_UNAVAILABLE, @ref GLFW_PLATFORM_ERROR and @ref GLFW_INVALID_VALUE - * - * @remark If an error occurs before the creation call is made, GLFW returns - * the Vulkan error code most appropriate for the error. Appropriate use of - * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should - * eliminate almost all occurrences of these errors. - * - * @remark @macos GLFW prefers the `VK_EXT_metal_surface` extension, with the - * `VK_MVK_macos_surface` extension as a fallback. The name of the selected - * extension, if any, is included in the array returned by @ref - * glfwGetRequiredInstanceExtensions. - * - * @remark @macos This function creates and sets a `CAMetalLayer` instance for - * the window content view, which is required for MoltenVK to function. - * - * @thread_safety This function may be called from any thread. For - * synchronization details of Vulkan objects, see the Vulkan specification. - * - * @sa @ref vulkan_surface - * @sa @ref glfwGetRequiredInstanceExtensions - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); - -#endif /*VK_VERSION_1_0*/ - - -/************************************************************************* - * Global definition cleanup - *************************************************************************/ - -/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ - -#ifdef GLFW_WINGDIAPI_DEFINED - #undef WINGDIAPI - #undef GLFW_WINGDIAPI_DEFINED -#endif - -#ifdef GLFW_CALLBACK_DEFINED - #undef CALLBACK - #undef GLFW_CALLBACK_DEFINED -#endif - -/* Some OpenGL related headers need GLAPIENTRY, but it is unconditionally - * defined by some gl.h variants (OpenBSD) so define it after if needed. - */ -#ifndef GLAPIENTRY - #define GLAPIENTRY APIENTRY - #define GLFW_GLAPIENTRY_DEFINED -#endif - -/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ - - -#ifdef __cplusplus -} -#endif - -#endif /* _glfw3_h_ */ - diff --git a/libs/zglfw/libs/glfw/include/GLFW/glfw3native.h b/libs/zglfw/libs/glfw/include/GLFW/glfw3native.h deleted file mode 100644 index 7be0227d5..000000000 --- a/libs/zglfw/libs/glfw/include/GLFW/glfw3native.h +++ /dev/null @@ -1,628 +0,0 @@ -/************************************************************************* - * GLFW 3.3 - www.glfw.org - * A library for OpenGL, window and input - *------------------------------------------------------------------------ - * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2018 Camilla Löwy - * - * 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 _glfw3_native_h_ -#define _glfw3_native_h_ - -#ifdef __cplusplus -extern "C" { -#endif - - -/************************************************************************* - * Doxygen documentation - *************************************************************************/ - -/*! @file glfw3native.h - * @brief The header of the native access functions. - * - * This is the header file of the native access functions. See @ref native for - * more information. - */ -/*! @defgroup native Native access - * @brief Functions related to accessing native handles. - * - * **By using the native access functions you assert that you know what you're - * doing and how to fix problems caused by using them. If you don't, you - * shouldn't be using them.** - * - * Before the inclusion of @ref glfw3native.h, you may define zero or more - * window system API macro and zero or more context creation API macros. - * - * The chosen backends must match those the library was compiled for. Failure - * to do this will cause a link-time error. - * - * The available window API macros are: - * * `GLFW_EXPOSE_NATIVE_WIN32` - * * `GLFW_EXPOSE_NATIVE_COCOA` - * * `GLFW_EXPOSE_NATIVE_X11` - * * `GLFW_EXPOSE_NATIVE_WAYLAND` - * - * The available context API macros are: - * * `GLFW_EXPOSE_NATIVE_WGL` - * * `GLFW_EXPOSE_NATIVE_NSGL` - * * `GLFW_EXPOSE_NATIVE_GLX` - * * `GLFW_EXPOSE_NATIVE_EGL` - * * `GLFW_EXPOSE_NATIVE_OSMESA` - * - * These macros select which of the native access functions that are declared - * and which platform-specific headers to include. It is then up your (by - * definition platform-specific) code to handle which of these should be - * defined. - * - * If you do not want the platform-specific headers to be included, define - * `GLFW_NATIVE_INCLUDE_NONE` before including the @ref glfw3native.h header. - * - * @code - * #define GLFW_EXPOSE_NATIVE_WIN32 - * #define GLFW_EXPOSE_NATIVE_WGL - * #define GLFW_NATIVE_INCLUDE_NONE - * #include - * @endcode - */ - - -/************************************************************************* - * System headers and types - *************************************************************************/ - -#if !defined(GLFW_NATIVE_INCLUDE_NONE) - - #if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) - /* This is a workaround for the fact that glfw3.h needs to export APIENTRY (for - * example to allow applications to correctly declare a GL_KHR_debug callback) - * but windows.h assumes no one will define APIENTRY before it does - */ - #if defined(GLFW_APIENTRY_DEFINED) - #undef APIENTRY - #undef GLFW_APIENTRY_DEFINED - #endif - #include - #elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) - #if defined(__OBJC__) - #import - #else - #include - #include - #endif - #elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) - #include - #include - #elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) - #include - #endif - - #if defined(GLFW_EXPOSE_NATIVE_WGL) - /* WGL is declared by windows.h */ - #endif - #if defined(GLFW_EXPOSE_NATIVE_NSGL) - /* NSGL is declared by Cocoa.h */ - #endif - #if defined(GLFW_EXPOSE_NATIVE_GLX) - /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by - * default it also acts as an OpenGL header - * However, glx.h will include gl.h, which will define it unconditionally - */ - #if defined(GLFW_GLAPIENTRY_DEFINED) - #undef GLAPIENTRY - #undef GLFW_GLAPIENTRY_DEFINED - #endif - #include - #endif - #if defined(GLFW_EXPOSE_NATIVE_EGL) - #include - #endif - #if defined(GLFW_EXPOSE_NATIVE_OSMESA) - /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by - * default it also acts as an OpenGL header - * However, osmesa.h will include gl.h, which will define it unconditionally - */ - #if defined(GLFW_GLAPIENTRY_DEFINED) - #undef GLAPIENTRY - #undef GLFW_GLAPIENTRY_DEFINED - #endif - #include - #endif - -#endif /*GLFW_NATIVE_INCLUDE_NONE*/ - - -/************************************************************************* - * Functions - *************************************************************************/ - -#if defined(GLFW_EXPOSE_NATIVE_WIN32) -/*! @brief Returns the adapter device name of the specified monitor. - * - * @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`) - * of the specified monitor, or `NULL` if an [error](@ref error_handling) - * occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.1. - * - * @ingroup native - */ -GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); - -/*! @brief Returns the display device name of the specified monitor. - * - * @return The UTF-8 encoded display device name (for example - * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.1. - * - * @ingroup native - */ -GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); - -/*! @brief Returns the `HWND` of the specified window. - * - * @return The `HWND` of the specified window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @remark The `HDC` associated with the window can be queried with the - * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) - * function. - * @code - * HDC dc = GetDC(glfwGetWin32Window(window)); - * @endcode - * This DC is private and does not need to be released. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.0. - * - * @ingroup native - */ -GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); -#endif - -#if defined(GLFW_EXPOSE_NATIVE_WGL) -/*! @brief Returns the `HGLRC` of the specified window. - * - * @return The `HGLRC` of the specified window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref - * GLFW_NOT_INITIALIZED. - * - * @remark The `HDC` associated with the window can be queried with the - * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) - * function. - * @code - * HDC dc = GetDC(glfwGetWin32Window(window)); - * @endcode - * This DC is private and does not need to be released. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.0. - * - * @ingroup native - */ -GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); -#endif - -#if defined(GLFW_EXPOSE_NATIVE_COCOA) -/*! @brief Returns the `CGDirectDisplayID` of the specified monitor. - * - * @return The `CGDirectDisplayID` of the specified monitor, or - * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.1. - * - * @ingroup native - */ -GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); - -/*! @brief Returns the `NSWindow` of the specified window. - * - * @return The `NSWindow` of the specified window, or `nil` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.0. - * - * @ingroup native - */ -GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); -#endif - -#if defined(GLFW_EXPOSE_NATIVE_NSGL) -/*! @brief Returns the `NSOpenGLContext` of the specified window. - * - * @return The `NSOpenGLContext` of the specified window, or `nil` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref - * GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.0. - * - * @ingroup native - */ -GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); -#endif - -#if defined(GLFW_EXPOSE_NATIVE_X11) -/*! @brief Returns the `Display` used by GLFW. - * - * @return The `Display` used by GLFW, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.0. - * - * @ingroup native - */ -GLFWAPI Display* glfwGetX11Display(void); - -/*! @brief Returns the `RRCrtc` of the specified monitor. - * - * @return The `RRCrtc` of the specified monitor, or `None` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.1. - * - * @ingroup native - */ -GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); - -/*! @brief Returns the `RROutput` of the specified monitor. - * - * @return The `RROutput` of the specified monitor, or `None` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.1. - * - * @ingroup native - */ -GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); - -/*! @brief Returns the `Window` of the specified window. - * - * @return The `Window` of the specified window, or `None` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.0. - * - * @ingroup native - */ -GLFWAPI Window glfwGetX11Window(GLFWwindow* window); - -/*! @brief Sets the current primary selection to the specified string. - * - * @param[in] string A UTF-8 encoded string. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The specified string is copied before this function - * returns. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref clipboard - * @sa glfwGetX11SelectionString - * @sa glfwSetClipboardString - * - * @since Added in version 3.3. - * - * @ingroup native - */ -GLFWAPI void glfwSetX11SelectionString(const char* string); - -/*! @brief Returns the contents of the current primary selection as a string. - * - * If the selection is empty or if its contents cannot be converted, `NULL` - * is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated. - * - * @return The contents of the selection as a UTF-8 encoded string, or `NULL` - * if an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the next call to @ref - * glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the - * library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref clipboard - * @sa glfwSetX11SelectionString - * @sa glfwGetClipboardString - * - * @since Added in version 3.3. - * - * @ingroup native - */ -GLFWAPI const char* glfwGetX11SelectionString(void); -#endif - -#if defined(GLFW_EXPOSE_NATIVE_GLX) -/*! @brief Returns the `GLXContext` of the specified window. - * - * @return The `GLXContext` of the specified window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref - * GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.0. - * - * @ingroup native - */ -GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); - -/*! @brief Returns the `GLXWindow` of the specified window. - * - * @return The `GLXWindow` of the specified window, or `None` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref - * GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); -#endif - -#if defined(GLFW_EXPOSE_NATIVE_WAYLAND) -/*! @brief Returns the `struct wl_display*` used by GLFW. - * - * @return The `struct wl_display*` used by GLFW, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); - -/*! @brief Returns the `struct wl_output*` of the specified monitor. - * - * @return The `struct wl_output*` of the specified monitor, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); - -/*! @brief Returns the main `struct wl_surface*` of the specified window. - * - * @return The main `struct wl_surface*` of the specified window, or `NULL` if - * an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); -#endif - -#if defined(GLFW_EXPOSE_NATIVE_EGL) -/*! @brief Returns the `EGLDisplay` used by GLFW. - * - * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @remark Because EGL is initialized on demand, this function will return - * `EGL_NO_DISPLAY` until the first context has been created via EGL. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.0. - * - * @ingroup native - */ -GLFWAPI EGLDisplay glfwGetEGLDisplay(void); - -/*! @brief Returns the `EGLContext` of the specified window. - * - * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref - * GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.0. - * - * @ingroup native - */ -GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); - -/*! @brief Returns the `EGLSurface` of the specified window. - * - * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref - * GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.0. - * - * @ingroup native - */ -GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); -#endif - -#if defined(GLFW_EXPOSE_NATIVE_OSMESA) -/*! @brief Retrieves the color buffer associated with the specified window. - * - * @param[in] window The window whose color buffer to retrieve. - * @param[out] width Where to store the width of the color buffer, or `NULL`. - * @param[out] height Where to store the height of the color buffer, or `NULL`. - * @param[out] format Where to store the OSMesa pixel format of the color - * buffer, or `NULL`. - * @param[out] buffer Where to store the address of the color buffer, or - * `NULL`. - * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref - * GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.3. - * - * @ingroup native - */ -GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer); - -/*! @brief Retrieves the depth buffer associated with the specified window. - * - * @param[in] window The window whose depth buffer to retrieve. - * @param[out] width Where to store the width of the depth buffer, or `NULL`. - * @param[out] height Where to store the height of the depth buffer, or `NULL`. - * @param[out] bytesPerValue Where to store the number of bytes per depth - * buffer element, or `NULL`. - * @param[out] buffer Where to store the address of the depth buffer, or - * `NULL`. - * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref - * GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.3. - * - * @ingroup native - */ -GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer); - -/*! @brief Returns the `OSMesaContext` of the specified window. - * - * @return The `OSMesaContext` of the specified window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref - * GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.3. - * - * @ingroup native - */ -GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window); -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* _glfw3_native_h_ */ - diff --git a/libs/zglfw/libs/glfw/src/cocoa_init.m b/libs/zglfw/libs/glfw/src/cocoa_init.m deleted file mode 100644 index f52731298..000000000 --- a/libs/zglfw/libs/glfw/src/cocoa_init.m +++ /dev/null @@ -1,633 +0,0 @@ -//======================================================================== -// GLFW 3.3 macOS - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2009-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" -#include // For MAXPATHLEN - -// Needed for _NSGetProgname -#include - -// Change to our application bundle's resources directory, if present -// -static void changeToResourcesDirectory(void) -{ - char resourcesPath[MAXPATHLEN]; - - CFBundleRef bundle = CFBundleGetMainBundle(); - if (!bundle) - return; - - CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); - - CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); - if (CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo) - { - CFRelease(last); - CFRelease(resourcesURL); - return; - } - - CFRelease(last); - - if (!CFURLGetFileSystemRepresentation(resourcesURL, - true, - (UInt8*) resourcesPath, - MAXPATHLEN)) - { - CFRelease(resourcesURL); - return; - } - - CFRelease(resourcesURL); - - chdir(resourcesPath); -} - -// Set up the menu bar (manually) -// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that -// could go away at any moment, lots of stuff that really should be -// localize(d|able), etc. Add a nib to save us this horror. -// -static void createMenuBar(void) -{ - size_t i; - NSString* appName = nil; - NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary]; - NSString* nameKeys[] = - { - @"CFBundleDisplayName", - @"CFBundleName", - @"CFBundleExecutable", - }; - - // Try to figure out what the calling application is called - - for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++) - { - id name = bundleInfo[nameKeys[i]]; - if (name && - [name isKindOfClass:[NSString class]] && - ![name isEqualToString:@""]) - { - appName = name; - break; - } - } - - if (!appName) - { - char** progname = _NSGetProgname(); - if (progname && *progname) - appName = @(*progname); - else - appName = @"GLFW Application"; - } - - NSMenu* bar = [[NSMenu alloc] init]; - [NSApp setMainMenu:bar]; - - NSMenuItem* appMenuItem = - [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; - NSMenu* appMenu = [[NSMenu alloc] init]; - [appMenuItem setSubmenu:appMenu]; - - [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] - action:@selector(orderFrontStandardAboutPanel:) - keyEquivalent:@""]; - [appMenu addItem:[NSMenuItem separatorItem]]; - NSMenu* servicesMenu = [[NSMenu alloc] init]; - [NSApp setServicesMenu:servicesMenu]; - [[appMenu addItemWithTitle:@"Services" - action:NULL - keyEquivalent:@""] setSubmenu:servicesMenu]; - [servicesMenu release]; - [appMenu addItem:[NSMenuItem separatorItem]]; - [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] - action:@selector(hide:) - keyEquivalent:@"h"]; - [[appMenu addItemWithTitle:@"Hide Others" - action:@selector(hideOtherApplications:) - keyEquivalent:@"h"] - setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand]; - [appMenu addItemWithTitle:@"Show All" - action:@selector(unhideAllApplications:) - keyEquivalent:@""]; - [appMenu addItem:[NSMenuItem separatorItem]]; - [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] - action:@selector(terminate:) - keyEquivalent:@"q"]; - - NSMenuItem* windowMenuItem = - [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; - [bar release]; - NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; - [NSApp setWindowsMenu:windowMenu]; - [windowMenuItem setSubmenu:windowMenu]; - - [windowMenu addItemWithTitle:@"Minimize" - action:@selector(performMiniaturize:) - keyEquivalent:@"m"]; - [windowMenu addItemWithTitle:@"Zoom" - action:@selector(performZoom:) - keyEquivalent:@""]; - [windowMenu addItem:[NSMenuItem separatorItem]]; - [windowMenu addItemWithTitle:@"Bring All to Front" - action:@selector(arrangeInFront:) - keyEquivalent:@""]; - - // TODO: Make this appear at the bottom of the menu (for consistency) - [windowMenu addItem:[NSMenuItem separatorItem]]; - [[windowMenu addItemWithTitle:@"Enter Full Screen" - action:@selector(toggleFullScreen:) - keyEquivalent:@"f"] - setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; - - // Prior to Snow Leopard, we need to use this oddly-named semi-private API - // to get the application menu working properly. - SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:"); - [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; -} - -// Create key code translation tables -// -static void createKeyTables(void) -{ - int scancode; - - memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes)); - memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes)); - - _glfw.ns.keycodes[0x1D] = GLFW_KEY_0; - _glfw.ns.keycodes[0x12] = GLFW_KEY_1; - _glfw.ns.keycodes[0x13] = GLFW_KEY_2; - _glfw.ns.keycodes[0x14] = GLFW_KEY_3; - _glfw.ns.keycodes[0x15] = GLFW_KEY_4; - _glfw.ns.keycodes[0x17] = GLFW_KEY_5; - _glfw.ns.keycodes[0x16] = GLFW_KEY_6; - _glfw.ns.keycodes[0x1A] = GLFW_KEY_7; - _glfw.ns.keycodes[0x1C] = GLFW_KEY_8; - _glfw.ns.keycodes[0x19] = GLFW_KEY_9; - _glfw.ns.keycodes[0x00] = GLFW_KEY_A; - _glfw.ns.keycodes[0x0B] = GLFW_KEY_B; - _glfw.ns.keycodes[0x08] = GLFW_KEY_C; - _glfw.ns.keycodes[0x02] = GLFW_KEY_D; - _glfw.ns.keycodes[0x0E] = GLFW_KEY_E; - _glfw.ns.keycodes[0x03] = GLFW_KEY_F; - _glfw.ns.keycodes[0x05] = GLFW_KEY_G; - _glfw.ns.keycodes[0x04] = GLFW_KEY_H; - _glfw.ns.keycodes[0x22] = GLFW_KEY_I; - _glfw.ns.keycodes[0x26] = GLFW_KEY_J; - _glfw.ns.keycodes[0x28] = GLFW_KEY_K; - _glfw.ns.keycodes[0x25] = GLFW_KEY_L; - _glfw.ns.keycodes[0x2E] = GLFW_KEY_M; - _glfw.ns.keycodes[0x2D] = GLFW_KEY_N; - _glfw.ns.keycodes[0x1F] = GLFW_KEY_O; - _glfw.ns.keycodes[0x23] = GLFW_KEY_P; - _glfw.ns.keycodes[0x0C] = GLFW_KEY_Q; - _glfw.ns.keycodes[0x0F] = GLFW_KEY_R; - _glfw.ns.keycodes[0x01] = GLFW_KEY_S; - _glfw.ns.keycodes[0x11] = GLFW_KEY_T; - _glfw.ns.keycodes[0x20] = GLFW_KEY_U; - _glfw.ns.keycodes[0x09] = GLFW_KEY_V; - _glfw.ns.keycodes[0x0D] = GLFW_KEY_W; - _glfw.ns.keycodes[0x07] = GLFW_KEY_X; - _glfw.ns.keycodes[0x10] = GLFW_KEY_Y; - _glfw.ns.keycodes[0x06] = GLFW_KEY_Z; - - _glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE; - _glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH; - _glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA; - _glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL; - _glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT; - _glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET; - _glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS; - _glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD; - _glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET; - _glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON; - _glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH; - _glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1; - - _glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE; - _glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK; - _glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE; - _glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN; - _glfw.ns.keycodes[0x77] = GLFW_KEY_END; - _glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER; - _glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE; - _glfw.ns.keycodes[0x7A] = GLFW_KEY_F1; - _glfw.ns.keycodes[0x78] = GLFW_KEY_F2; - _glfw.ns.keycodes[0x63] = GLFW_KEY_F3; - _glfw.ns.keycodes[0x76] = GLFW_KEY_F4; - _glfw.ns.keycodes[0x60] = GLFW_KEY_F5; - _glfw.ns.keycodes[0x61] = GLFW_KEY_F6; - _glfw.ns.keycodes[0x62] = GLFW_KEY_F7; - _glfw.ns.keycodes[0x64] = GLFW_KEY_F8; - _glfw.ns.keycodes[0x65] = GLFW_KEY_F9; - _glfw.ns.keycodes[0x6D] = GLFW_KEY_F10; - _glfw.ns.keycodes[0x67] = GLFW_KEY_F11; - _glfw.ns.keycodes[0x6F] = GLFW_KEY_F12; - _glfw.ns.keycodes[0x69] = GLFW_KEY_F13; - _glfw.ns.keycodes[0x6B] = GLFW_KEY_F14; - _glfw.ns.keycodes[0x71] = GLFW_KEY_F15; - _glfw.ns.keycodes[0x6A] = GLFW_KEY_F16; - _glfw.ns.keycodes[0x40] = GLFW_KEY_F17; - _glfw.ns.keycodes[0x4F] = GLFW_KEY_F18; - _glfw.ns.keycodes[0x50] = GLFW_KEY_F19; - _glfw.ns.keycodes[0x5A] = GLFW_KEY_F20; - _glfw.ns.keycodes[0x73] = GLFW_KEY_HOME; - _glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT; - _glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT; - _glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT; - _glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL; - _glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT; - _glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER; - _glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU; - _glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK; - _glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN; - _glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP; - _glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT; - _glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT; - _glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL; - _glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT; - _glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER; - _glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE; - _glfw.ns.keycodes[0x30] = GLFW_KEY_TAB; - _glfw.ns.keycodes[0x7E] = GLFW_KEY_UP; - - _glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0; - _glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1; - _glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2; - _glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3; - _glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4; - _glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5; - _glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6; - _glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7; - _glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8; - _glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9; - _glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD; - _glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL; - _glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE; - _glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER; - _glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL; - _glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY; - _glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT; - - for (scancode = 0; scancode < 256; scancode++) - { - // Store the reverse translation for faster key name lookup - if (_glfw.ns.keycodes[scancode] >= 0) - _glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode; - } -} - -// Retrieve Unicode data for the current keyboard layout -// -static GLFWbool updateUnicodeDataNS(void) -{ - if (_glfw.ns.inputSource) - { - CFRelease(_glfw.ns.inputSource); - _glfw.ns.inputSource = NULL; - _glfw.ns.unicodeData = nil; - } - - _glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource(); - if (!_glfw.ns.inputSource) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to retrieve keyboard layout input source"); - return GLFW_FALSE; - } - - _glfw.ns.unicodeData = - TISGetInputSourceProperty(_glfw.ns.inputSource, - kTISPropertyUnicodeKeyLayoutData); - if (!_glfw.ns.unicodeData) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to retrieve keyboard layout Unicode data"); - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -// Load HIToolbox.framework and the TIS symbols we need from it -// -static GLFWbool initializeTIS(void) -{ - // This works only because Cocoa has already loaded it properly - _glfw.ns.tis.bundle = - CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); - if (!_glfw.ns.tis.bundle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to load HIToolbox.framework"); - return GLFW_FALSE; - } - - CFStringRef* kPropertyUnicodeKeyLayoutData = - CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, - CFSTR("kTISPropertyUnicodeKeyLayoutData")); - _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource = - CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, - CFSTR("TISCopyCurrentKeyboardLayoutInputSource")); - _glfw.ns.tis.GetInputSourceProperty = - CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, - CFSTR("TISGetInputSourceProperty")); - _glfw.ns.tis.GetKbdType = - CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, - CFSTR("LMGetKbdType")); - - if (!kPropertyUnicodeKeyLayoutData || - !TISCopyCurrentKeyboardLayoutInputSource || - !TISGetInputSourceProperty || - !LMGetKbdType) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to load TIS API symbols"); - return GLFW_FALSE; - } - - _glfw.ns.tis.kPropertyUnicodeKeyLayoutData = - *kPropertyUnicodeKeyLayoutData; - - return updateUnicodeDataNS(); -} - -@interface GLFWHelper : NSObject -@end - -@implementation GLFWHelper - -- (void)selectedKeyboardInputSourceChanged:(NSObject* )object -{ - updateUnicodeDataNS(); -} - -- (void)doNothing:(id)object -{ -} - -@end // GLFWHelper - -@interface GLFWApplicationDelegate : NSObject -@end - -@implementation GLFWApplicationDelegate - -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender -{ - _GLFWwindow* window; - - for (window = _glfw.windowListHead; window; window = window->next) - _glfwInputWindowCloseRequest(window); - - return NSTerminateCancel; -} - -- (void)applicationDidChangeScreenParameters:(NSNotification *) notification -{ - _GLFWwindow* window; - - for (window = _glfw.windowListHead; window; window = window->next) - { - if (window->context.client != GLFW_NO_API) - [window->context.nsgl.object update]; - } - - _glfwPollMonitorsNS(); -} - -- (void)applicationWillFinishLaunching:(NSNotification *)notification -{ - if (_glfw.hints.init.ns.menubar) - { - // Menu bar setup must go between sharedApplication and finishLaunching - // in order to properly emulate the behavior of NSApplicationMain - - if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) - { - [[NSBundle mainBundle] loadNibNamed:@"MainMenu" - owner:NSApp - topLevelObjects:&_glfw.ns.nibObjects]; - } - else - createMenuBar(); - } -} - -- (void)applicationDidFinishLaunching:(NSNotification *)notification -{ - _glfw.ns.finishedLaunching = GLFW_TRUE; - _glfwPlatformPostEmptyEvent(); - - // In case we are unbundled, make us a proper UI application - if (_glfw.hints.init.ns.menubar) - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - - [NSApp stop:nil]; -} - -- (void)applicationDidHide:(NSNotification *)notification -{ - int i; - - for (i = 0; i < _glfw.monitorCount; i++) - _glfwRestoreVideoModeNS(_glfw.monitors[i]); -} - -@end // GLFWApplicationDelegate - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -void* _glfwLoadLocalVulkanLoaderNS(void) -{ - CFBundleRef bundle = CFBundleGetMainBundle(); - if (!bundle) - return NULL; - - CFURLRef frameworksUrl = CFBundleCopyPrivateFrameworksURL(bundle); - if (!frameworksUrl) - return NULL; - - CFURLRef loaderUrl = CFURLCreateCopyAppendingPathComponent( - kCFAllocatorDefault, frameworksUrl, CFSTR("libvulkan.1.dylib"), false); - if (!loaderUrl) - { - CFRelease(frameworksUrl); - return NULL; - } - - char path[PATH_MAX]; - void* handle = NULL; - - if (CFURLGetFileSystemRepresentation(loaderUrl, true, (UInt8*) path, sizeof(path) - 1)) - handle = _glfw_dlopen(path); - - CFRelease(loaderUrl); - CFRelease(frameworksUrl); - return handle; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformInit(void) -{ - @autoreleasepool { - - _glfw.ns.helper = [[GLFWHelper alloc] init]; - - [NSThread detachNewThreadSelector:@selector(doNothing:) - toTarget:_glfw.ns.helper - withObject:nil]; - - if (NSApp) - _glfw.ns.finishedLaunching = GLFW_TRUE; - - [NSApplication sharedApplication]; - - _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; - if (_glfw.ns.delegate == nil) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to create application delegate"); - return GLFW_FALSE; - } - - [NSApp setDelegate:_glfw.ns.delegate]; - - NSEvent* (^block)(NSEvent*) = ^ NSEvent* (NSEvent* event) - { - if ([event modifierFlags] & NSEventModifierFlagCommand) - [[NSApp keyWindow] sendEvent:event]; - - return event; - }; - - _glfw.ns.keyUpMonitor = - [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp - handler:block]; - - if (_glfw.hints.init.ns.chdir) - changeToResourcesDirectory(); - - // Press and Hold prevents some keys from emitting repeated characters - NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO}; - [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; - - [[NSNotificationCenter defaultCenter] - addObserver:_glfw.ns.helper - selector:@selector(selectedKeyboardInputSourceChanged:) - name:NSTextInputContextKeyboardSelectionDidChangeNotification - object:nil]; - - createKeyTables(); - - _glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); - if (!_glfw.ns.eventSource) - return GLFW_FALSE; - - CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0); - - if (!initializeTIS()) - return GLFW_FALSE; - - _glfwInitTimerNS(); - _glfwInitJoysticksNS(); - - _glfwPollMonitorsNS(); - return GLFW_TRUE; - - } // autoreleasepool -} - -void _glfwPlatformTerminate(void) -{ - @autoreleasepool { - - if (_glfw.ns.inputSource) - { - CFRelease(_glfw.ns.inputSource); - _glfw.ns.inputSource = NULL; - _glfw.ns.unicodeData = nil; - } - - if (_glfw.ns.eventSource) - { - CFRelease(_glfw.ns.eventSource); - _glfw.ns.eventSource = NULL; - } - - if (_glfw.ns.delegate) - { - [NSApp setDelegate:nil]; - [_glfw.ns.delegate release]; - _glfw.ns.delegate = nil; - } - - if (_glfw.ns.helper) - { - [[NSNotificationCenter defaultCenter] - removeObserver:_glfw.ns.helper - name:NSTextInputContextKeyboardSelectionDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] - removeObserver:_glfw.ns.helper]; - [_glfw.ns.helper release]; - _glfw.ns.helper = nil; - } - - if (_glfw.ns.keyUpMonitor) - [NSEvent removeMonitor:_glfw.ns.keyUpMonitor]; - - free(_glfw.ns.clipboardString); - - _glfwTerminateNSGL(); - _glfwTerminateEGL(); - _glfwTerminateOSMesa(); - _glfwTerminateJoysticksNS(); - - } // autoreleasepool -} - -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " Cocoa NSGL EGL OSMesa" -#if defined(_GLFW_BUILD_DLL) - " dynamic" -#endif - ; -} - diff --git a/libs/zglfw/libs/glfw/src/cocoa_joystick.h b/libs/zglfw/libs/glfw/src/cocoa_joystick.h deleted file mode 100644 index 0de867856..000000000 --- a/libs/zglfw/libs/glfw/src/cocoa_joystick.h +++ /dev/null @@ -1,51 +0,0 @@ -//======================================================================== -// GLFW 3.3 Cocoa - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#include -#include -#include -#include - -#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickNS ns -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyJoystick; } - -#define _GLFW_PLATFORM_MAPPING_NAME "Mac OS X" -#define GLFW_BUILD_COCOA_MAPPINGS - -// Cocoa-specific per-joystick data -// -typedef struct _GLFWjoystickNS -{ - IOHIDDeviceRef device; - CFMutableArrayRef axes; - CFMutableArrayRef buttons; - CFMutableArrayRef hats; -} _GLFWjoystickNS; - - -void _glfwInitJoysticksNS(void); -void _glfwTerminateJoysticksNS(void); - diff --git a/libs/zglfw/libs/glfw/src/cocoa_joystick.m b/libs/zglfw/libs/glfw/src/cocoa_joystick.m deleted file mode 100644 index 3d306777a..000000000 --- a/libs/zglfw/libs/glfw/src/cocoa_joystick.m +++ /dev/null @@ -1,488 +0,0 @@ -//======================================================================== -// GLFW 3.3 Cocoa - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2009-2019 Camilla Löwy -// Copyright (c) 2012 Torsten Walluhn -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include -#include -#include - -#include -#include - -#include -#include - - -// Joystick element information -// -typedef struct _GLFWjoyelementNS -{ - IOHIDElementRef native; - uint32_t usage; - int index; - long minimum; - long maximum; - -} _GLFWjoyelementNS; - - -// Returns the value of the specified element of the specified joystick -// -static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element) -{ - IOHIDValueRef valueRef; - long value = 0; - - if (js->ns.device) - { - if (IOHIDDeviceGetValue(js->ns.device, - element->native, - &valueRef) == kIOReturnSuccess) - { - value = IOHIDValueGetIntegerValue(valueRef); - } - } - - return value; -} - -// Comparison function for matching the SDL element order -// -static CFComparisonResult compareElements(const void* fp, - const void* sp, - void* user) -{ - const _GLFWjoyelementNS* fe = fp; - const _GLFWjoyelementNS* se = sp; - if (fe->usage < se->usage) - return kCFCompareLessThan; - if (fe->usage > se->usage) - return kCFCompareGreaterThan; - if (fe->index < se->index) - return kCFCompareLessThan; - if (fe->index > se->index) - return kCFCompareGreaterThan; - return kCFCompareEqualTo; -} - -// Removes the specified joystick -// -static void closeJoystick(_GLFWjoystick* js) -{ - int i; - - _glfwInputJoystick(js, GLFW_DISCONNECTED); - - for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) - free((void*) CFArrayGetValueAtIndex(js->ns.axes, i)); - CFRelease(js->ns.axes); - - for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) - free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i)); - CFRelease(js->ns.buttons); - - for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) - free((void*) CFArrayGetValueAtIndex(js->ns.hats, i)); - CFRelease(js->ns.hats); - - _glfwFreeJoystick(js); -} - -// Callback for user-initiated joystick addition -// -static void matchCallback(void* context, - IOReturn result, - void* sender, - IOHIDDeviceRef device) -{ - int jid; - char name[256]; - char guid[33]; - CFIndex i; - CFTypeRef property; - uint32_t vendor = 0, product = 0, version = 0; - _GLFWjoystick* js; - CFMutableArrayRef axes, buttons, hats; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - if (_glfw.joysticks[jid].ns.device == device) - return; - } - - axes = CFArrayCreateMutable(NULL, 0, NULL); - buttons = CFArrayCreateMutable(NULL, 0, NULL); - hats = CFArrayCreateMutable(NULL, 0, NULL); - - property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); - if (property) - { - CFStringGetCString(property, - name, - sizeof(name), - kCFStringEncodingUTF8); - } - else - strncpy(name, "Unknown", sizeof(name)); - - property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)); - if (property) - CFNumberGetValue(property, kCFNumberSInt32Type, &vendor); - - property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)); - if (property) - CFNumberGetValue(property, kCFNumberSInt32Type, &product); - - property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey)); - if (property) - CFNumberGetValue(property, kCFNumberSInt32Type, &version); - - // Generate a joystick GUID that matches the SDL 2.0.5+ one - if (vendor && product) - { - sprintf(guid, "03000000%02x%02x0000%02x%02x0000%02x%02x0000", - (uint8_t) vendor, (uint8_t) (vendor >> 8), - (uint8_t) product, (uint8_t) (product >> 8), - (uint8_t) version, (uint8_t) (version >> 8)); - } - else - { - sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", - name[0], name[1], name[2], name[3], - name[4], name[5], name[6], name[7], - name[8], name[9], name[10]); - } - - CFArrayRef elements = - IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); - - for (i = 0; i < CFArrayGetCount(elements); i++) - { - IOHIDElementRef native = (IOHIDElementRef) - CFArrayGetValueAtIndex(elements, i); - if (CFGetTypeID(native) != IOHIDElementGetTypeID()) - continue; - - const IOHIDElementType type = IOHIDElementGetType(native); - if ((type != kIOHIDElementTypeInput_Axis) && - (type != kIOHIDElementTypeInput_Button) && - (type != kIOHIDElementTypeInput_Misc)) - { - continue; - } - - CFMutableArrayRef target = NULL; - - const uint32_t usage = IOHIDElementGetUsage(native); - const uint32_t page = IOHIDElementGetUsagePage(native); - if (page == kHIDPage_GenericDesktop) - { - switch (usage) - { - case kHIDUsage_GD_X: - case kHIDUsage_GD_Y: - case kHIDUsage_GD_Z: - case kHIDUsage_GD_Rx: - case kHIDUsage_GD_Ry: - case kHIDUsage_GD_Rz: - case kHIDUsage_GD_Slider: - case kHIDUsage_GD_Dial: - case kHIDUsage_GD_Wheel: - target = axes; - break; - case kHIDUsage_GD_Hatswitch: - target = hats; - break; - case kHIDUsage_GD_DPadUp: - case kHIDUsage_GD_DPadRight: - case kHIDUsage_GD_DPadDown: - case kHIDUsage_GD_DPadLeft: - case kHIDUsage_GD_SystemMainMenu: - case kHIDUsage_GD_Select: - case kHIDUsage_GD_Start: - target = buttons; - break; - } - } - else if (page == kHIDPage_Simulation) - { - switch (usage) - { - case kHIDUsage_Sim_Accelerator: - case kHIDUsage_Sim_Brake: - case kHIDUsage_Sim_Throttle: - case kHIDUsage_Sim_Rudder: - case kHIDUsage_Sim_Steering: - target = axes; - break; - } - } - else if (page == kHIDPage_Button || page == kHIDPage_Consumer) - target = buttons; - - if (target) - { - _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); - element->native = native; - element->usage = usage; - element->index = (int) CFArrayGetCount(target); - element->minimum = IOHIDElementGetLogicalMin(native); - element->maximum = IOHIDElementGetLogicalMax(native); - CFArrayAppendValue(target, element); - } - } - - CFRelease(elements); - - CFArraySortValues(axes, CFRangeMake(0, CFArrayGetCount(axes)), - compareElements, NULL); - CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), - compareElements, NULL); - CFArraySortValues(hats, CFRangeMake(0, CFArrayGetCount(hats)), - compareElements, NULL); - - js = _glfwAllocJoystick(name, guid, - (int) CFArrayGetCount(axes), - (int) CFArrayGetCount(buttons), - (int) CFArrayGetCount(hats)); - - js->ns.device = device; - js->ns.axes = axes; - js->ns.buttons = buttons; - js->ns.hats = hats; - - _glfwInputJoystick(js, GLFW_CONNECTED); -} - -// Callback for user-initiated joystick removal -// -static void removeCallback(void* context, - IOReturn result, - void* sender, - IOHIDDeviceRef device) -{ - int jid; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - if (_glfw.joysticks[jid].connected && _glfw.joysticks[jid].ns.device == device) - { - closeJoystick(&_glfw.joysticks[jid]); - break; - } - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialize joystick interface -// -void _glfwInitJoysticksNS(void) -{ - CFMutableArrayRef matching; - const long usages[] = - { - kHIDUsage_GD_Joystick, - kHIDUsage_GD_GamePad, - kHIDUsage_GD_MultiAxisController - }; - - _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, - kIOHIDOptionsTypeNone); - - matching = CFArrayCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeArrayCallBacks); - if (!matching) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array"); - return; - } - - for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++) - { - const long page = kHIDPage_GenericDesktop; - - CFMutableDictionaryRef dict = - CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (!dict) - continue; - - CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, - kCFNumberLongType, - &page); - CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, - kCFNumberLongType, - &usages[i]); - if (pageRef && usageRef) - { - CFDictionarySetValue(dict, - CFSTR(kIOHIDDeviceUsagePageKey), - pageRef); - CFDictionarySetValue(dict, - CFSTR(kIOHIDDeviceUsageKey), - usageRef); - CFArrayAppendValue(matching, dict); - } - - if (pageRef) - CFRelease(pageRef); - if (usageRef) - CFRelease(usageRef); - - CFRelease(dict); - } - - IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching); - CFRelease(matching); - - IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager, - &matchCallback, NULL); - IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager, - &removeCallback, NULL); - IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager, - CFRunLoopGetMain(), - kCFRunLoopDefaultMode); - IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone); - - // Execute the run loop once in order to register any initially-attached - // joysticks - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); -} - -// Close all opened joystick handles -// -void _glfwTerminateJoysticksNS(void) -{ - int jid; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - if (_glfw.joysticks[jid].connected) - closeJoystick(&_glfw.joysticks[jid]); - } - - CFRelease(_glfw.ns.hidManager); - _glfw.ns.hidManager = NULL; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) -{ - if (mode & _GLFW_POLL_AXES) - { - CFIndex i; - - for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) - { - _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(js->ns.axes, i); - - const long raw = getElementValue(js, axis); - // Perform auto calibration - if (raw < axis->minimum) - axis->minimum = raw; - if (raw > axis->maximum) - axis->maximum = raw; - - const long size = axis->maximum - axis->minimum; - if (size == 0) - _glfwInputJoystickAxis(js, (int) i, 0.f); - else - { - const float value = (2.f * (raw - axis->minimum) / size) - 1.f; - _glfwInputJoystickAxis(js, (int) i, value); - } - } - } - - if (mode & _GLFW_POLL_BUTTONS) - { - CFIndex i; - - for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) - { - _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(js->ns.buttons, i); - const char value = getElementValue(js, button) - button->minimum; - const int state = (value > 0) ? GLFW_PRESS : GLFW_RELEASE; - _glfwInputJoystickButton(js, (int) i, state); - } - - for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) - { - const int states[9] = - { - GLFW_HAT_UP, - GLFW_HAT_RIGHT_UP, - GLFW_HAT_RIGHT, - GLFW_HAT_RIGHT_DOWN, - GLFW_HAT_DOWN, - GLFW_HAT_LEFT_DOWN, - GLFW_HAT_LEFT, - GLFW_HAT_LEFT_UP, - GLFW_HAT_CENTERED - }; - - _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(js->ns.hats, i); - long state = getElementValue(js, hat) - hat->minimum; - if (state < 0 || state > 8) - state = 8; - - _glfwInputJoystickHat(js, (int) i, states[state]); - } - } - - return js->connected; -} - -void _glfwPlatformUpdateGamepadGUID(char* guid) -{ - if ((strncmp(guid + 4, "000000000000", 12) == 0) && - (strncmp(guid + 20, "000000000000", 12) == 0)) - { - char original[33]; - strncpy(original, guid, sizeof(original) - 1); - sprintf(guid, "03000000%.4s0000%.4s000000000000", - original, original + 16); - } -} - diff --git a/libs/zglfw/libs/glfw/src/cocoa_monitor.m b/libs/zglfw/libs/glfw/src/cocoa_monitor.m deleted file mode 100644 index 7769bb7e9..000000000 --- a/libs/zglfw/libs/glfw/src/cocoa_monitor.m +++ /dev/null @@ -1,627 +0,0 @@ -//======================================================================== -// GLFW 3.3 macOS - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include -#include -#include - -#include -#include - - -// Get the name of the specified display, or NULL -// -static char* getMonitorName(CGDirectDisplayID displayID, NSScreen* screen) -{ - // IOKit doesn't work on Apple Silicon anymore - // Luckily, 10.15 introduced -[NSScreen localizedName]. - // Use it if available, and fall back to IOKit otherwise. - if (screen) - { - if ([screen respondsToSelector:@selector(localizedName)]) - { - NSString* name = [screen valueForKey:@"localizedName"]; - if (name) - return _glfw_strdup([name UTF8String]); - } - } - - io_iterator_t it; - io_service_t service; - CFDictionaryRef info; - - if (IOServiceGetMatchingServices(MACH_PORT_NULL, - IOServiceMatching("IODisplayConnect"), - &it) != 0) - { - // This may happen if a desktop Mac is running headless - return _glfw_strdup("Display"); - } - - while ((service = IOIteratorNext(it)) != 0) - { - info = IODisplayCreateInfoDictionary(service, - kIODisplayOnlyPreferredName); - - CFNumberRef vendorIDRef = - CFDictionaryGetValue(info, CFSTR(kDisplayVendorID)); - CFNumberRef productIDRef = - CFDictionaryGetValue(info, CFSTR(kDisplayProductID)); - if (!vendorIDRef || !productIDRef) - { - CFRelease(info); - continue; - } - - unsigned int vendorID, productID; - CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID); - CFNumberGetValue(productIDRef, kCFNumberIntType, &productID); - - if (CGDisplayVendorNumber(displayID) == vendorID && - CGDisplayModelNumber(displayID) == productID) - { - // Info dictionary is used and freed below - break; - } - - CFRelease(info); - } - - IOObjectRelease(it); - - if (!service) - return _glfw_strdup("Display"); - - CFDictionaryRef names = - CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); - - CFStringRef nameRef; - - if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), - (const void**) &nameRef)) - { - // This may happen if a desktop Mac is running headless - CFRelease(info); - return _glfw_strdup("Display"); - } - - const CFIndex size = - CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), - kCFStringEncodingUTF8); - char* name = calloc(size + 1, 1); - CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8); - - CFRelease(info); - return name; -} - -// Check whether the display mode should be included in enumeration -// -static GLFWbool modeIsGood(CGDisplayModeRef mode) -{ - uint32_t flags = CGDisplayModeGetIOFlags(mode); - - if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag)) - return GLFW_FALSE; - if (flags & kDisplayModeInterlacedFlag) - return GLFW_FALSE; - if (flags & kDisplayModeStretchedFlag) - return GLFW_FALSE; - -#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 - CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); - if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) && - CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0)) - { - CFRelease(format); - return GLFW_FALSE; - } - - CFRelease(format); -#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ - return GLFW_TRUE; -} - -// Convert Core Graphics display mode to GLFW video mode -// -static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, - double fallbackRefreshRate) -{ - GLFWvidmode result; - result.width = (int) CGDisplayModeGetWidth(mode); - result.height = (int) CGDisplayModeGetHeight(mode); - result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode)); - - if (result.refreshRate == 0) - result.refreshRate = (int) round(fallbackRefreshRate); - -#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 - CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); - if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0) - { - result.redBits = 5; - result.greenBits = 5; - result.blueBits = 5; - } - else -#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ - { - result.redBits = 8; - result.greenBits = 8; - result.blueBits = 8; - } - -#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 - CFRelease(format); -#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ - return result; -} - -// Starts reservation for display fading -// -static CGDisplayFadeReservationToken beginFadeReservation(void) -{ - CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; - - if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess) - { - CGDisplayFade(token, 0.3, - kCGDisplayBlendNormal, - kCGDisplayBlendSolidColor, - 0.0, 0.0, 0.0, - TRUE); - } - - return token; -} - -// Ends reservation for display fading -// -static void endFadeReservation(CGDisplayFadeReservationToken token) -{ - if (token != kCGDisplayFadeReservationInvalidToken) - { - CGDisplayFade(token, 0.5, - kCGDisplayBlendSolidColor, - kCGDisplayBlendNormal, - 0.0, 0.0, 0.0, - FALSE); - CGReleaseDisplayFadeReservation(token); - } -} - -// Returns the display refresh rate queried from the I/O registry -// -static double getFallbackRefreshRate(CGDirectDisplayID displayID) -{ - double refreshRate = 60.0; - - io_iterator_t it; - io_service_t service; - - if (IOServiceGetMatchingServices(MACH_PORT_NULL, - IOServiceMatching("IOFramebuffer"), - &it) != 0) - { - return refreshRate; - } - - while ((service = IOIteratorNext(it)) != 0) - { - const CFNumberRef indexRef = - IORegistryEntryCreateCFProperty(service, - CFSTR("IOFramebufferOpenGLIndex"), - kCFAllocatorDefault, - kNilOptions); - if (!indexRef) - continue; - - uint32_t index = 0; - CFNumberGetValue(indexRef, kCFNumberIntType, &index); - CFRelease(indexRef); - - if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID) - continue; - - const CFNumberRef clockRef = - IORegistryEntryCreateCFProperty(service, - CFSTR("IOFBCurrentPixelClock"), - kCFAllocatorDefault, - kNilOptions); - const CFNumberRef countRef = - IORegistryEntryCreateCFProperty(service, - CFSTR("IOFBCurrentPixelCount"), - kCFAllocatorDefault, - kNilOptions); - - uint32_t clock = 0, count = 0; - - if (clockRef) - { - CFNumberGetValue(clockRef, kCFNumberIntType, &clock); - CFRelease(clockRef); - } - - if (countRef) - { - CFNumberGetValue(countRef, kCFNumberIntType, &count); - CFRelease(countRef); - } - - if (clock > 0 && count > 0) - refreshRate = clock / (double) count; - - break; - } - - IOObjectRelease(it); - return refreshRate; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Poll for changes in the set of connected monitors -// -void _glfwPollMonitorsNS(void) -{ - uint32_t displayCount; - CGGetOnlineDisplayList(0, NULL, &displayCount); - CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID)); - CGGetOnlineDisplayList(displayCount, displays, &displayCount); - - for (int i = 0; i < _glfw.monitorCount; i++) - _glfw.monitors[i]->ns.screen = nil; - - _GLFWmonitor** disconnected = NULL; - uint32_t disconnectedCount = _glfw.monitorCount; - if (disconnectedCount) - { - disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); - memcpy(disconnected, - _glfw.monitors, - _glfw.monitorCount * sizeof(_GLFWmonitor*)); - } - - for (uint32_t i = 0; i < displayCount; i++) - { - if (CGDisplayIsAsleep(displays[i])) - continue; - - const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]); - NSScreen* screen = nil; - - for (screen in [NSScreen screens]) - { - NSNumber* screenNumber = [screen deviceDescription][@"NSScreenNumber"]; - - // HACK: Compare unit numbers instead of display IDs to work around - // display replacement on machines with automatic graphics - // switching - if (CGDisplayUnitNumber([screenNumber unsignedIntValue]) == unitNumber) - break; - } - - // HACK: Compare unit numbers instead of display IDs to work around - // display replacement on machines with automatic graphics - // switching - uint32_t j; - for (j = 0; j < disconnectedCount; j++) - { - if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber) - { - disconnected[j]->ns.screen = screen; - disconnected[j] = NULL; - break; - } - } - - if (j < disconnectedCount) - continue; - - const CGSize size = CGDisplayScreenSize(displays[i]); - char* name = getMonitorName(displays[i], screen); - if (!name) - continue; - - _GLFWmonitor* monitor = _glfwAllocMonitor(name, size.width, size.height); - monitor->ns.displayID = displays[i]; - monitor->ns.unitNumber = unitNumber; - monitor->ns.screen = screen; - - free(name); - - CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]); - if (CGDisplayModeGetRefreshRate(mode) == 0.0) - monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]); - CGDisplayModeRelease(mode); - - _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); - } - - for (uint32_t i = 0; i < disconnectedCount; i++) - { - if (disconnected[i]) - _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); - } - - free(disconnected); - free(displays); -} - -// Change the current video mode -// -void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) -{ - GLFWvidmode current; - _glfwPlatformGetVideoMode(monitor, ¤t); - - const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired); - if (_glfwCompareVideoModes(¤t, best) == 0) - return; - - CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); - const CFIndex count = CFArrayGetCount(modes); - CGDisplayModeRef native = NULL; - - for (CFIndex i = 0; i < count; i++) - { - CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); - if (!modeIsGood(dm)) - continue; - - const GLFWvidmode mode = - vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate); - if (_glfwCompareVideoModes(best, &mode) == 0) - { - native = dm; - break; - } - } - - if (native) - { - if (monitor->ns.previousMode == NULL) - monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID); - - CGDisplayFadeReservationToken token = beginFadeReservation(); - CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL); - endFadeReservation(token); - } - - CFRelease(modes); -} - -// Restore the previously saved (original) video mode -// -void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor) -{ - if (monitor->ns.previousMode) - { - CGDisplayFadeReservationToken token = beginFadeReservation(); - CGDisplaySetDisplayMode(monitor->ns.displayID, - monitor->ns.previousMode, NULL); - endFadeReservation(token); - - CGDisplayModeRelease(monitor->ns.previousMode); - monitor->ns.previousMode = NULL; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) -{ -} - -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) -{ - @autoreleasepool { - - const CGRect bounds = CGDisplayBounds(monitor->ns.displayID); - - if (xpos) - *xpos = (int) bounds.origin.x; - if (ypos) - *ypos = (int) bounds.origin.y; - - } // autoreleasepool -} - -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) -{ - @autoreleasepool { - - if (!monitor->ns.screen) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Cannot query content scale without screen"); - } - - const NSRect points = [monitor->ns.screen frame]; - const NSRect pixels = [monitor->ns.screen convertRectToBacking:points]; - - if (xscale) - *xscale = (float) (pixels.size.width / points.size.width); - if (yscale) - *yscale = (float) (pixels.size.height / points.size.height); - - } // autoreleasepool -} - -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, - int* xpos, int* ypos, - int* width, int* height) -{ - @autoreleasepool { - - if (!monitor->ns.screen) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Cannot query workarea without screen"); - } - - const NSRect frameRect = [monitor->ns.screen visibleFrame]; - - if (xpos) - *xpos = frameRect.origin.x; - if (ypos) - *ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1); - if (width) - *width = frameRect.size.width; - if (height) - *height = frameRect.size.height; - - } // autoreleasepool -} - -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) -{ - @autoreleasepool { - - *count = 0; - - CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); - const CFIndex found = CFArrayGetCount(modes); - GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode)); - - for (CFIndex i = 0; i < found; i++) - { - CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); - if (!modeIsGood(dm)) - continue; - - const GLFWvidmode mode = - vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate); - CFIndex j; - - for (j = 0; j < *count; j++) - { - if (_glfwCompareVideoModes(result + j, &mode) == 0) - break; - } - - // Skip duplicate modes - if (j < *count) - continue; - - (*count)++; - result[*count - 1] = mode; - } - - CFRelease(modes); - return result; - - } // autoreleasepool -} - -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) -{ - @autoreleasepool { - - CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID); - *mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate); - CGDisplayModeRelease(native); - - } // autoreleasepool -} - -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) -{ - @autoreleasepool { - - uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID); - CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue)); - - CGGetDisplayTransferByTable(monitor->ns.displayID, - size, - values, - values + size, - values + size * 2, - &size); - - _glfwAllocGammaArrays(ramp, size); - - for (uint32_t i = 0; i < size; i++) - { - ramp->red[i] = (unsigned short) (values[i] * 65535); - ramp->green[i] = (unsigned short) (values[i + size] * 65535); - ramp->blue[i] = (unsigned short) (values[i + size * 2] * 65535); - } - - free(values); - return GLFW_TRUE; - - } // autoreleasepool -} - -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) -{ - @autoreleasepool { - - CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue)); - - for (unsigned int i = 0; i < ramp->size; i++) - { - values[i] = ramp->red[i] / 65535.f; - values[i + ramp->size] = ramp->green[i] / 65535.f; - values[i + ramp->size * 2] = ramp->blue[i] / 65535.f; - } - - CGSetDisplayTransferByTable(monitor->ns.displayID, - ramp->size, - values, - values + ramp->size, - values + ramp->size * 2); - - free(values); - - } // autoreleasepool -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay); - return monitor->ns.displayID; -} - diff --git a/libs/zglfw/libs/glfw/src/cocoa_platform.h b/libs/zglfw/libs/glfw/src/cocoa_platform.h deleted file mode 100644 index bb677033a..000000000 --- a/libs/zglfw/libs/glfw/src/cocoa_platform.h +++ /dev/null @@ -1,220 +0,0 @@ -//======================================================================== -// GLFW 3.3 macOS - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2009-2019 Camilla Löwy -// -// 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. -// -//======================================================================== - -#include -#include - -#include - -// NOTE: All of NSGL was deprecated in the 10.14 SDK -// This disables the pointless warnings for every symbol we use -#ifndef GL_SILENCE_DEPRECATION -#define GL_SILENCE_DEPRECATION -#endif - -#if defined(__OBJC__) -#import -#else -typedef void* id; -#endif - -// NOTE: Many Cocoa enum values have been renamed and we need to build across -// SDK versions where one is unavailable or deprecated. -// We use the newer names in code and replace them with the older names if -// the base SDK does not provide the newer names. - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 - #define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat - #define NSEventMaskAny NSAnyEventMask - #define NSEventMaskKeyUp NSKeyUpMask - #define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask - #define NSEventModifierFlagCommand NSCommandKeyMask - #define NSEventModifierFlagControl NSControlKeyMask - #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask - #define NSEventModifierFlagOption NSAlternateKeyMask - #define NSEventModifierFlagShift NSShiftKeyMask - #define NSEventTypeApplicationDefined NSApplicationDefined - #define NSWindowStyleMaskBorderless NSBorderlessWindowMask - #define NSWindowStyleMaskClosable NSClosableWindowMask - #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask - #define NSWindowStyleMaskResizable NSResizableWindowMask - #define NSWindowStyleMaskTitled NSTitledWindowMask -#endif - -// NOTE: Many Cocoa dynamically linked constants have been renamed and we need -// to build across SDK versions where one is unavailable or deprecated. -// We use the newer names in code and replace them with the older names if -// the deployment target is older than the newer names. - -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101300 - #define NSPasteboardTypeURL NSURLPboardType -#endif - -typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; -typedef VkFlags VkMetalSurfaceCreateFlagsEXT; - -typedef struct VkMacOSSurfaceCreateInfoMVK -{ - VkStructureType sType; - const void* pNext; - VkMacOSSurfaceCreateFlagsMVK flags; - const void* pView; -} VkMacOSSurfaceCreateInfoMVK; - -typedef struct VkMetalSurfaceCreateInfoEXT -{ - VkStructureType sType; - const void* pNext; - VkMetalSurfaceCreateFlagsEXT flags; - const void* pLayer; -} VkMetalSurfaceCreateInfoEXT; - -typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*); -typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMetalSurfaceCreateInfoEXT*,const VkAllocationCallbacks*,VkSurfaceKHR*); - -#include "posix_thread.h" -#include "cocoa_joystick.h" -#include "nsgl_context.h" -#include "egl_context.h" -#include "osmesa_context.h" - -#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) -#define _glfw_dlclose(handle) dlclose(handle) -#define _glfw_dlsym(handle, name) dlsym(handle, name) - -#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->ns.layer) -#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY - -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns -#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns - -// HIToolbox.framework pointer typedefs -#define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData -typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void); -#define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource -typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef); -#define TISGetInputSourceProperty _glfw.ns.tis.GetInputSourceProperty -typedef UInt8 (*PFN_LMGetKbdType)(void); -#define LMGetKbdType _glfw.ns.tis.GetKbdType - - -// Cocoa-specific per-window data -// -typedef struct _GLFWwindowNS -{ - id object; - id delegate; - id view; - id layer; - - GLFWbool maximized; - GLFWbool occluded; - GLFWbool retina; - - // Cached window properties to filter out duplicate events - int width, height; - int fbWidth, fbHeight; - float xscale, yscale; - - // The total sum of the distances the cursor has been warped - // since the last cursor motion event was processed - // This is kept to counteract Cocoa doing the same internally - double cursorWarpDeltaX, cursorWarpDeltaY; -} _GLFWwindowNS; - -// Cocoa-specific global data -// -typedef struct _GLFWlibraryNS -{ - CGEventSourceRef eventSource; - id delegate; - GLFWbool finishedLaunching; - GLFWbool cursorHidden; - TISInputSourceRef inputSource; - IOHIDManagerRef hidManager; - id unicodeData; - id helper; - id keyUpMonitor; - id nibObjects; - - char keynames[GLFW_KEY_LAST + 1][17]; - short int keycodes[256]; - short int scancodes[GLFW_KEY_LAST + 1]; - char* clipboardString; - CGPoint cascadePoint; - // Where to place the cursor when re-enabled - double restoreCursorPosX, restoreCursorPosY; - // The window whose disabled cursor mode is active - _GLFWwindow* disabledCursorWindow; - - struct { - CFBundleRef bundle; - PFN_TISCopyCurrentKeyboardLayoutInputSource CopyCurrentKeyboardLayoutInputSource; - PFN_TISGetInputSourceProperty GetInputSourceProperty; - PFN_LMGetKbdType GetKbdType; - CFStringRef kPropertyUnicodeKeyLayoutData; - } tis; -} _GLFWlibraryNS; - -// Cocoa-specific per-monitor data -// -typedef struct _GLFWmonitorNS -{ - CGDirectDisplayID displayID; - CGDisplayModeRef previousMode; - uint32_t unitNumber; - id screen; - double fallbackRefreshRate; -} _GLFWmonitorNS; - -// Cocoa-specific per-cursor data -// -typedef struct _GLFWcursorNS -{ - id object; -} _GLFWcursorNS; - -// Cocoa-specific global timer data -// -typedef struct _GLFWtimerNS -{ - uint64_t frequency; -} _GLFWtimerNS; - - -void _glfwInitTimerNS(void); - -void _glfwPollMonitorsNS(void); -void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); -void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); - -float _glfwTransformYNS(float y); - -void* _glfwLoadLocalVulkanLoaderNS(void); - diff --git a/libs/zglfw/libs/glfw/src/cocoa_time.c b/libs/zglfw/libs/glfw/src/cocoa_time.c deleted file mode 100644 index d390cdc16..000000000 --- a/libs/zglfw/libs/glfw/src/cocoa_time.c +++ /dev/null @@ -1,62 +0,0 @@ -//======================================================================== -// GLFW 3.3 macOS - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialise timer -// -void _glfwInitTimerNS(void) -{ - mach_timebase_info_data_t info; - mach_timebase_info(&info); - - _glfw.timer.ns.frequency = (info.denom * 1e9) / info.numer; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -uint64_t _glfwPlatformGetTimerValue(void) -{ - return mach_absolute_time(); -} - -uint64_t _glfwPlatformGetTimerFrequency(void) -{ - return _glfw.timer.ns.frequency; -} - diff --git a/libs/zglfw/libs/glfw/src/cocoa_window.m b/libs/zglfw/libs/glfw/src/cocoa_window.m deleted file mode 100644 index bbab6c4f0..000000000 --- a/libs/zglfw/libs/glfw/src/cocoa_window.m +++ /dev/null @@ -1,1934 +0,0 @@ -//======================================================================== -// GLFW 3.3 macOS - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2009-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include -#include - -// HACK: This enum value is missing from framework headers on OS X 10.11 despite -// having been (according to documentation) added in Mac OS X 10.7 -#define NSWindowCollectionBehaviorFullScreenNone (1 << 9) - -// Returns whether the cursor is in the content area of the specified window -// -static GLFWbool cursorInContentArea(_GLFWwindow* window) -{ - const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; - return [window->ns.view mouse:pos inRect:[window->ns.view frame]]; -} - -// Hides the cursor if not already hidden -// -static void hideCursor(_GLFWwindow* window) -{ - if (!_glfw.ns.cursorHidden) - { - [NSCursor hide]; - _glfw.ns.cursorHidden = GLFW_TRUE; - } -} - -// Shows the cursor if not already shown -// -static void showCursor(_GLFWwindow* window) -{ - if (_glfw.ns.cursorHidden) - { - [NSCursor unhide]; - _glfw.ns.cursorHidden = GLFW_FALSE; - } -} - -// Updates the cursor image according to its cursor mode -// -static void updateCursorImage(_GLFWwindow* window) -{ - if (window->cursorMode == GLFW_CURSOR_NORMAL) - { - showCursor(window); - - if (window->cursor) - [(NSCursor*) window->cursor->ns.object set]; - else - [[NSCursor arrowCursor] set]; - } - else - hideCursor(window); -} - -// Apply chosen cursor mode to a focused window -// -static void updateCursorMode(_GLFWwindow* window) -{ - if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - _glfw.ns.disabledCursorWindow = window; - _glfwPlatformGetCursorPos(window, - &_glfw.ns.restoreCursorPosX, - &_glfw.ns.restoreCursorPosY); - _glfwCenterCursorInContentArea(window); - CGAssociateMouseAndMouseCursorPosition(false); - } - else if (_glfw.ns.disabledCursorWindow == window) - { - _glfw.ns.disabledCursorWindow = NULL; - _glfwPlatformSetCursorPos(window, - _glfw.ns.restoreCursorPosX, - _glfw.ns.restoreCursorPosY); - // NOTE: The matching CGAssociateMouseAndMouseCursorPosition call is - // made in _glfwPlatformSetCursorPos as part of a workaround - } - - if (cursorInContentArea(window)) - updateCursorImage(window); -} - -// Make the specified window and its video mode active on its monitor -// -static void acquireMonitor(_GLFWwindow* window) -{ - _glfwSetVideoModeNS(window->monitor, &window->videoMode); - const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID); - const NSRect frame = NSMakeRect(bounds.origin.x, - _glfwTransformYNS(bounds.origin.y + bounds.size.height - 1), - bounds.size.width, - bounds.size.height); - - [window->ns.object setFrame:frame display:YES]; - - _glfwInputMonitorWindow(window->monitor, window); -} - -// Remove the window and restore the original video mode -// -static void releaseMonitor(_GLFWwindow* window) -{ - if (window->monitor->window != window) - return; - - _glfwInputMonitorWindow(window->monitor, NULL); - _glfwRestoreVideoModeNS(window->monitor); -} - -// Translates macOS key modifiers into GLFW ones -// -static int translateFlags(NSUInteger flags) -{ - int mods = 0; - - if (flags & NSEventModifierFlagShift) - mods |= GLFW_MOD_SHIFT; - if (flags & NSEventModifierFlagControl) - mods |= GLFW_MOD_CONTROL; - if (flags & NSEventModifierFlagOption) - mods |= GLFW_MOD_ALT; - if (flags & NSEventModifierFlagCommand) - mods |= GLFW_MOD_SUPER; - if (flags & NSEventModifierFlagCapsLock) - mods |= GLFW_MOD_CAPS_LOCK; - - return mods; -} - -// Translates a macOS keycode to a GLFW keycode -// -static int translateKey(unsigned int key) -{ - if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0])) - return GLFW_KEY_UNKNOWN; - - return _glfw.ns.keycodes[key]; -} - -// Translate a GLFW keycode to a Cocoa modifier flag -// -static NSUInteger translateKeyToModifierFlag(int key) -{ - switch (key) - { - case GLFW_KEY_LEFT_SHIFT: - case GLFW_KEY_RIGHT_SHIFT: - return NSEventModifierFlagShift; - case GLFW_KEY_LEFT_CONTROL: - case GLFW_KEY_RIGHT_CONTROL: - return NSEventModifierFlagControl; - case GLFW_KEY_LEFT_ALT: - case GLFW_KEY_RIGHT_ALT: - return NSEventModifierFlagOption; - case GLFW_KEY_LEFT_SUPER: - case GLFW_KEY_RIGHT_SUPER: - return NSEventModifierFlagCommand; - case GLFW_KEY_CAPS_LOCK: - return NSEventModifierFlagCapsLock; - } - - return 0; -} - -// Defines a constant for empty ranges in NSTextInputClient -// -static const NSRange kEmptyRange = { NSNotFound, 0 }; - - -//------------------------------------------------------------------------ -// Delegate for window related notifications -//------------------------------------------------------------------------ - -@interface GLFWWindowDelegate : NSObject -{ - _GLFWwindow* window; -} - -- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; - -@end - -@implementation GLFWWindowDelegate - -- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow -{ - self = [super init]; - if (self != nil) - window = initWindow; - - return self; -} - -- (BOOL)windowShouldClose:(id)sender -{ - _glfwInputWindowCloseRequest(window); - return NO; -} - -- (void)windowDidResize:(NSNotification *)notification -{ - if (window->context.source == GLFW_NATIVE_CONTEXT_API) - [window->context.nsgl.object update]; - - if (_glfw.ns.disabledCursorWindow == window) - _glfwCenterCursorInContentArea(window); - - const int maximized = [window->ns.object isZoomed]; - if (window->ns.maximized != maximized) - { - window->ns.maximized = maximized; - _glfwInputWindowMaximize(window, maximized); - } - - const NSRect contentRect = [window->ns.view frame]; - const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; - - if (fbRect.size.width != window->ns.fbWidth || - fbRect.size.height != window->ns.fbHeight) - { - window->ns.fbWidth = fbRect.size.width; - window->ns.fbHeight = fbRect.size.height; - _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); - } - - if (contentRect.size.width != window->ns.width || - contentRect.size.height != window->ns.height) - { - window->ns.width = contentRect.size.width; - window->ns.height = contentRect.size.height; - _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height); - } -} - -- (void)windowDidMove:(NSNotification *)notification -{ - if (window->context.source == GLFW_NATIVE_CONTEXT_API) - [window->context.nsgl.object update]; - - if (_glfw.ns.disabledCursorWindow == window) - _glfwCenterCursorInContentArea(window); - - int x, y; - _glfwPlatformGetWindowPos(window, &x, &y); - _glfwInputWindowPos(window, x, y); -} - -- (void)windowDidMiniaturize:(NSNotification *)notification -{ - if (window->monitor) - releaseMonitor(window); - - _glfwInputWindowIconify(window, GLFW_TRUE); -} - -- (void)windowDidDeminiaturize:(NSNotification *)notification -{ - if (window->monitor) - acquireMonitor(window); - - _glfwInputWindowIconify(window, GLFW_FALSE); -} - -- (void)windowDidBecomeKey:(NSNotification *)notification -{ - if (_glfw.ns.disabledCursorWindow == window) - _glfwCenterCursorInContentArea(window); - - _glfwInputWindowFocus(window, GLFW_TRUE); - updateCursorMode(window); -} - -- (void)windowDidResignKey:(NSNotification *)notification -{ - if (window->monitor && window->autoIconify) - _glfwPlatformIconifyWindow(window); - - _glfwInputWindowFocus(window, GLFW_FALSE); -} - -- (void)windowDidChangeOcclusionState:(NSNotification* )notification -{ - if ([window->ns.object occlusionState] & NSWindowOcclusionStateVisible) - window->ns.occluded = GLFW_FALSE; - else - window->ns.occluded = GLFW_TRUE; -} - -@end - - -//------------------------------------------------------------------------ -// Content view class for the GLFW window -//------------------------------------------------------------------------ - -@interface GLFWContentView : NSView -{ - _GLFWwindow* window; - NSTrackingArea* trackingArea; - NSMutableAttributedString* markedText; -} - -- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; - -@end - -@implementation GLFWContentView - -- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow -{ - self = [super init]; - if (self != nil) - { - window = initWindow; - trackingArea = nil; - markedText = [[NSMutableAttributedString alloc] init]; - - [self updateTrackingAreas]; - [self registerForDraggedTypes:@[NSPasteboardTypeURL]]; - } - - return self; -} - -- (void)dealloc -{ - [trackingArea release]; - [markedText release]; - [super dealloc]; -} - -- (BOOL)isOpaque -{ - return [window->ns.object isOpaque]; -} - -- (BOOL)canBecomeKeyView -{ - return YES; -} - -- (BOOL)acceptsFirstResponder -{ - return YES; -} - -- (BOOL)wantsUpdateLayer -{ - return YES; -} - -- (void)updateLayer -{ - if (window->context.source == GLFW_NATIVE_CONTEXT_API) - [window->context.nsgl.object update]; - - _glfwInputWindowDamage(window); -} - -- (void)cursorUpdate:(NSEvent *)event -{ - updateCursorImage(window); -} - -- (BOOL)acceptsFirstMouse:(NSEvent *)event -{ - return YES; -} - -- (void)mouseDown:(NSEvent *)event -{ - _glfwInputMouseClick(window, - GLFW_MOUSE_BUTTON_LEFT, - GLFW_PRESS, - translateFlags([event modifierFlags])); -} - -- (void)mouseDragged:(NSEvent *)event -{ - [self mouseMoved:event]; -} - -- (void)mouseUp:(NSEvent *)event -{ - _glfwInputMouseClick(window, - GLFW_MOUSE_BUTTON_LEFT, - GLFW_RELEASE, - translateFlags([event modifierFlags])); -} - -- (void)mouseMoved:(NSEvent *)event -{ - if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - const double dx = [event deltaX] - window->ns.cursorWarpDeltaX; - const double dy = [event deltaY] - window->ns.cursorWarpDeltaY; - - _glfwInputCursorPos(window, - window->virtualCursorPosX + dx, - window->virtualCursorPosY + dy); - } - else - { - const NSRect contentRect = [window->ns.view frame]; - // NOTE: The returned location uses base 0,1 not 0,0 - const NSPoint pos = [event locationInWindow]; - - _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y); - } - - window->ns.cursorWarpDeltaX = 0; - window->ns.cursorWarpDeltaY = 0; -} - -- (void)rightMouseDown:(NSEvent *)event -{ - _glfwInputMouseClick(window, - GLFW_MOUSE_BUTTON_RIGHT, - GLFW_PRESS, - translateFlags([event modifierFlags])); -} - -- (void)rightMouseDragged:(NSEvent *)event -{ - [self mouseMoved:event]; -} - -- (void)rightMouseUp:(NSEvent *)event -{ - _glfwInputMouseClick(window, - GLFW_MOUSE_BUTTON_RIGHT, - GLFW_RELEASE, - translateFlags([event modifierFlags])); -} - -- (void)otherMouseDown:(NSEvent *)event -{ - _glfwInputMouseClick(window, - (int) [event buttonNumber], - GLFW_PRESS, - translateFlags([event modifierFlags])); -} - -- (void)otherMouseDragged:(NSEvent *)event -{ - [self mouseMoved:event]; -} - -- (void)otherMouseUp:(NSEvent *)event -{ - _glfwInputMouseClick(window, - (int) [event buttonNumber], - GLFW_RELEASE, - translateFlags([event modifierFlags])); -} - -- (void)mouseExited:(NSEvent *)event -{ - if (window->cursorMode == GLFW_CURSOR_HIDDEN) - showCursor(window); - - _glfwInputCursorEnter(window, GLFW_FALSE); -} - -- (void)mouseEntered:(NSEvent *)event -{ - if (window->cursorMode == GLFW_CURSOR_HIDDEN) - hideCursor(window); - - _glfwInputCursorEnter(window, GLFW_TRUE); -} - -- (void)viewDidChangeBackingProperties -{ - const NSRect contentRect = [window->ns.view frame]; - const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; - const float xscale = fbRect.size.width / contentRect.size.width; - const float yscale = fbRect.size.height / contentRect.size.height; - - if (xscale != window->ns.xscale || yscale != window->ns.yscale) - { - if (window->ns.retina && window->ns.layer) - [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; - - window->ns.xscale = xscale; - window->ns.yscale = yscale; - _glfwInputWindowContentScale(window, xscale, yscale); - } - - if (fbRect.size.width != window->ns.fbWidth || - fbRect.size.height != window->ns.fbHeight) - { - window->ns.fbWidth = fbRect.size.width; - window->ns.fbHeight = fbRect.size.height; - _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); - } -} - -- (void)drawRect:(NSRect)rect -{ - _glfwInputWindowDamage(window); -} - -- (void)updateTrackingAreas -{ - if (trackingArea != nil) - { - [self removeTrackingArea:trackingArea]; - [trackingArea release]; - } - - const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | - NSTrackingActiveInKeyWindow | - NSTrackingEnabledDuringMouseDrag | - NSTrackingCursorUpdate | - NSTrackingInVisibleRect | - NSTrackingAssumeInside; - - trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] - options:options - owner:self - userInfo:nil]; - - [self addTrackingArea:trackingArea]; - [super updateTrackingAreas]; -} - -- (void)keyDown:(NSEvent *)event -{ - const int key = translateKey([event keyCode]); - const int mods = translateFlags([event modifierFlags]); - - _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods); - - [self interpretKeyEvents:@[event]]; -} - -- (void)flagsChanged:(NSEvent *)event -{ - int action; - const unsigned int modifierFlags = - [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; - const int key = translateKey([event keyCode]); - const int mods = translateFlags(modifierFlags); - const NSUInteger keyFlag = translateKeyToModifierFlag(key); - - if (keyFlag & modifierFlags) - { - if (window->keys[key] == GLFW_PRESS) - action = GLFW_RELEASE; - else - action = GLFW_PRESS; - } - else - action = GLFW_RELEASE; - - _glfwInputKey(window, key, [event keyCode], action, mods); -} - -- (void)keyUp:(NSEvent *)event -{ - const int key = translateKey([event keyCode]); - const int mods = translateFlags([event modifierFlags]); - _glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods); -} - -- (void)scrollWheel:(NSEvent *)event -{ - double deltaX = [event scrollingDeltaX]; - double deltaY = [event scrollingDeltaY]; - - if ([event hasPreciseScrollingDeltas]) - { - deltaX *= 0.1; - deltaY *= 0.1; - } - - if (fabs(deltaX) > 0.0 || fabs(deltaY) > 0.0) - _glfwInputScroll(window, deltaX, deltaY); -} - -- (NSDragOperation)draggingEntered:(id )sender -{ - // HACK: We don't know what to say here because we don't know what the - // application wants to do with the paths - return NSDragOperationGeneric; -} - -- (BOOL)performDragOperation:(id )sender -{ - const NSRect contentRect = [window->ns.view frame]; - // NOTE: The returned location uses base 0,1 not 0,0 - const NSPoint pos = [sender draggingLocation]; - _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y); - - NSPasteboard* pasteboard = [sender draggingPasteboard]; - NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES}; - NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]] - options:options]; - const NSUInteger count = [urls count]; - if (count) - { - char** paths = calloc(count, sizeof(char*)); - - for (NSUInteger i = 0; i < count; i++) - paths[i] = _glfw_strdup([urls[i] fileSystemRepresentation]); - - _glfwInputDrop(window, (int) count, (const char**) paths); - - for (NSUInteger i = 0; i < count; i++) - free(paths[i]); - free(paths); - } - - return YES; -} - -- (BOOL)hasMarkedText -{ - return [markedText length] > 0; -} - -- (NSRange)markedRange -{ - if ([markedText length] > 0) - return NSMakeRange(0, [markedText length] - 1); - else - return kEmptyRange; -} - -- (NSRange)selectedRange -{ - return kEmptyRange; -} - -- (void)setMarkedText:(id)string - selectedRange:(NSRange)selectedRange - replacementRange:(NSRange)replacementRange -{ - [markedText release]; - if ([string isKindOfClass:[NSAttributedString class]]) - markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string]; - else - markedText = [[NSMutableAttributedString alloc] initWithString:string]; -} - -- (void)unmarkText -{ - [[markedText mutableString] setString:@""]; -} - -- (NSArray*)validAttributesForMarkedText -{ - return [NSArray array]; -} - -- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range - actualRange:(NSRangePointer)actualRange -{ - return nil; -} - -- (NSUInteger)characterIndexForPoint:(NSPoint)point -{ - return 0; -} - -- (NSRect)firstRectForCharacterRange:(NSRange)range - actualRange:(NSRangePointer)actualRange -{ - const NSRect frame = [window->ns.view frame]; - return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0); -} - -- (void)insertText:(id)string replacementRange:(NSRange)replacementRange -{ - NSString* characters; - NSEvent* event = [NSApp currentEvent]; - const int mods = translateFlags([event modifierFlags]); - const int plain = !(mods & GLFW_MOD_SUPER); - - if ([string isKindOfClass:[NSAttributedString class]]) - characters = [string string]; - else - characters = (NSString*) string; - - NSRange range = NSMakeRange(0, [characters length]); - while (range.length) - { - uint32_t codepoint = 0; - - if ([characters getBytes:&codepoint - maxLength:sizeof(codepoint) - usedLength:NULL - encoding:NSUTF32StringEncoding - options:0 - range:range - remainingRange:&range]) - { - if (codepoint >= 0xf700 && codepoint <= 0xf7ff) - continue; - - _glfwInputChar(window, codepoint, mods, plain); - } - } -} - -- (void)doCommandBySelector:(SEL)selector -{ -} - -@end - - -//------------------------------------------------------------------------ -// GLFW window class -//------------------------------------------------------------------------ - -@interface GLFWWindow : NSWindow {} -@end - -@implementation GLFWWindow - -- (BOOL)canBecomeKeyWindow -{ - // Required for NSWindowStyleMaskBorderless windows - return YES; -} - -- (BOOL)canBecomeMainWindow -{ - return YES; -} - -@end - - -// Create the Cocoa window -// -static GLFWbool createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWfbconfig* fbconfig) -{ - window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window]; - if (window->ns.delegate == nil) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to create window delegate"); - return GLFW_FALSE; - } - - NSRect contentRect; - - if (window->monitor) - { - GLFWvidmode mode; - int xpos, ypos; - - _glfwPlatformGetVideoMode(window->monitor, &mode); - _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); - - contentRect = NSMakeRect(xpos, ypos, mode.width, mode.height); - } - else - contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height); - - NSUInteger styleMask = NSWindowStyleMaskMiniaturizable; - - if (window->monitor || !window->decorated) - styleMask |= NSWindowStyleMaskBorderless; - else - { - styleMask |= (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); - - if (window->resizable) - styleMask |= NSWindowStyleMaskResizable; - } - - window->ns.object = [[GLFWWindow alloc] - initWithContentRect:contentRect - styleMask:styleMask - backing:NSBackingStoreBuffered - defer:NO]; - - if (window->ns.object == nil) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create window"); - return GLFW_FALSE; - } - - if (window->monitor) - [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; - else - { - [(NSWindow*) window->ns.object center]; - _glfw.ns.cascadePoint = - NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint: - NSPointFromCGPoint(_glfw.ns.cascadePoint)]); - - if (wndconfig->resizable) - { - const NSWindowCollectionBehavior behavior = - NSWindowCollectionBehaviorFullScreenPrimary | - NSWindowCollectionBehaviorManaged; - [window->ns.object setCollectionBehavior:behavior]; - } - else - { - const NSWindowCollectionBehavior behavior = - NSWindowCollectionBehaviorFullScreenNone; - [window->ns.object setCollectionBehavior:behavior]; - } - - if (wndconfig->floating) - [window->ns.object setLevel:NSFloatingWindowLevel]; - - if (wndconfig->maximized) - [window->ns.object zoom:nil]; - } - - if (strlen(wndconfig->ns.frameName)) - [window->ns.object setFrameAutosaveName:@(wndconfig->ns.frameName)]; - - window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window]; - window->ns.retina = wndconfig->ns.retina; - - if (fbconfig->transparent) - { - [window->ns.object setOpaque:NO]; - [window->ns.object setHasShadow:NO]; - [window->ns.object setBackgroundColor:[NSColor clearColor]]; - } - - [window->ns.object setContentView:window->ns.view]; - [window->ns.object makeFirstResponder:window->ns.view]; - [window->ns.object setTitle:@(wndconfig->title)]; - [window->ns.object setDelegate:window->ns.delegate]; - [window->ns.object setAcceptsMouseMovedEvents:YES]; - [window->ns.object setRestorable:NO]; - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 - if ([window->ns.object respondsToSelector:@selector(setTabbingMode:)]) - [window->ns.object setTabbingMode:NSWindowTabbingModeDisallowed]; -#endif - - _glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height); - _glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight); - - return GLFW_TRUE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Transforms a y-coordinate between the CG display and NS screen spaces -// -float _glfwTransformYNS(float y) -{ - return CGDisplayBounds(CGMainDisplayID()).size.height - y - 1; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - @autoreleasepool { - - if (!_glfw.ns.finishedLaunching) - [NSApp run]; - - if (!createNativeWindow(window, wndconfig, fbconfig)) - return GLFW_FALSE; - - if (ctxconfig->client != GLFW_NO_API) - { - if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) - { - if (!_glfwInitNSGL()) - return GLFW_FALSE; - if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) - { - // EGL implementation on macOS use CALayer* EGLNativeWindowType so we - // need to get the layer for EGL window surface creation. - [window->ns.view setWantsLayer:YES]; - window->ns.layer = [window->ns.view layer]; - - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) - { - if (!_glfwInitOSMesa()) - return GLFW_FALSE; - if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - - if (!_glfwRefreshContextAttribs(window, ctxconfig)) - return GLFW_FALSE; - } - - if (window->monitor) - { - _glfwPlatformShowWindow(window); - _glfwPlatformFocusWindow(window); - acquireMonitor(window); - - if (wndconfig->centerCursor) - _glfwCenterCursorInContentArea(window); - } - else - { - if (wndconfig->visible) - { - _glfwPlatformShowWindow(window); - if (wndconfig->focused) - _glfwPlatformFocusWindow(window); - } - } - - return GLFW_TRUE; - - } // autoreleasepool -} - -void _glfwPlatformDestroyWindow(_GLFWwindow* window) -{ - @autoreleasepool { - - if (_glfw.ns.disabledCursorWindow == window) - _glfw.ns.disabledCursorWindow = NULL; - - [window->ns.object orderOut:nil]; - - if (window->monitor) - releaseMonitor(window); - - if (window->context.destroy) - window->context.destroy(window); - - [window->ns.object setDelegate:nil]; - [window->ns.delegate release]; - window->ns.delegate = nil; - - [window->ns.view release]; - window->ns.view = nil; - - [window->ns.object close]; - window->ns.object = nil; - - // HACK: Allow Cocoa to catch up before returning - _glfwPlatformPollEvents(); - - } // autoreleasepool -} - -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) -{ - @autoreleasepool { - NSString* string = @(title); - [window->ns.object setTitle:string]; - // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it - // if the window lacks NSWindowStyleMaskTitled - [window->ns.object setMiniwindowTitle:string]; - } // autoreleasepool -} - -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images) -{ - // Regular windows do not have icons -} - -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) -{ - @autoreleasepool { - - const NSRect contentRect = - [window->ns.object contentRectForFrameRect:[window->ns.object frame]]; - - if (xpos) - *xpos = contentRect.origin.x; - if (ypos) - *ypos = _glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1); - - } // autoreleasepool -} - -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y) -{ - @autoreleasepool { - - const NSRect contentRect = [window->ns.view frame]; - const NSRect dummyRect = NSMakeRect(x, _glfwTransformYNS(y + contentRect.size.height - 1), 0, 0); - const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect]; - [window->ns.object setFrameOrigin:frameRect.origin]; - - } // autoreleasepool -} - -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) -{ - @autoreleasepool { - - const NSRect contentRect = [window->ns.view frame]; - - if (width) - *width = contentRect.size.width; - if (height) - *height = contentRect.size.height; - - } // autoreleasepool -} - -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) -{ - @autoreleasepool { - - if (window->monitor) - { - if (window->monitor->window == window) - acquireMonitor(window); - } - else - { - NSRect contentRect = - [window->ns.object contentRectForFrameRect:[window->ns.object frame]]; - contentRect.origin.y += contentRect.size.height - height; - contentRect.size = NSMakeSize(width, height); - [window->ns.object setFrame:[window->ns.object frameRectForContentRect:contentRect] - display:YES]; - } - - } // autoreleasepool -} - -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) -{ - @autoreleasepool { - - if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) - [window->ns.object setContentMinSize:NSMakeSize(0, 0)]; - else - [window->ns.object setContentMinSize:NSMakeSize(minwidth, minheight)]; - - if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) - [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)]; - else - [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)]; - - } // autoreleasepool -} - -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) -{ - @autoreleasepool { - if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) - [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)]; - else - [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)]; - } // autoreleasepool -} - -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) -{ - @autoreleasepool { - - const NSRect contentRect = [window->ns.view frame]; - const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; - - if (width) - *width = (int) fbRect.size.width; - if (height) - *height = (int) fbRect.size.height; - - } // autoreleasepool -} - -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) -{ - @autoreleasepool { - - const NSRect contentRect = [window->ns.view frame]; - const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect]; - - if (left) - *left = contentRect.origin.x - frameRect.origin.x; - if (top) - *top = frameRect.origin.y + frameRect.size.height - - contentRect.origin.y - contentRect.size.height; - if (right) - *right = frameRect.origin.x + frameRect.size.width - - contentRect.origin.x - contentRect.size.width; - if (bottom) - *bottom = contentRect.origin.y - frameRect.origin.y; - - } // autoreleasepool -} - -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) -{ - @autoreleasepool { - - const NSRect points = [window->ns.view frame]; - const NSRect pixels = [window->ns.view convertRectToBacking:points]; - - if (xscale) - *xscale = (float) (pixels.size.width / points.size.width); - if (yscale) - *yscale = (float) (pixels.size.height / points.size.height); - - } // autoreleasepool -} - -void _glfwPlatformIconifyWindow(_GLFWwindow* window) -{ - @autoreleasepool { - [window->ns.object miniaturize:nil]; - } // autoreleasepool -} - -void _glfwPlatformRestoreWindow(_GLFWwindow* window) -{ - @autoreleasepool { - if ([window->ns.object isMiniaturized]) - [window->ns.object deminiaturize:nil]; - else if ([window->ns.object isZoomed]) - [window->ns.object zoom:nil]; - } // autoreleasepool -} - -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) -{ - @autoreleasepool { - if (![window->ns.object isZoomed]) - [window->ns.object zoom:nil]; - } // autoreleasepool -} - -void _glfwPlatformShowWindow(_GLFWwindow* window) -{ - @autoreleasepool { - [window->ns.object orderFront:nil]; - } // autoreleasepool -} - -void _glfwPlatformHideWindow(_GLFWwindow* window) -{ - @autoreleasepool { - [window->ns.object orderOut:nil]; - } // autoreleasepool -} - -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) -{ - @autoreleasepool { - [NSApp requestUserAttention:NSInformationalRequest]; - } // autoreleasepool -} - -void _glfwPlatformFocusWindow(_GLFWwindow* window) -{ - @autoreleasepool { - // Make us the active application - // HACK: This is here to prevent applications using only hidden windows from - // being activated, but should probably not be done every time any - // window is shown - [NSApp activateIgnoringOtherApps:YES]; - [window->ns.object makeKeyAndOrderFront:nil]; - } // autoreleasepool -} - -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) -{ - @autoreleasepool { - - if (window->monitor == monitor) - { - if (monitor) - { - if (monitor->window == window) - acquireMonitor(window); - } - else - { - const NSRect contentRect = - NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), width, height); - const NSUInteger styleMask = [window->ns.object styleMask]; - const NSRect frameRect = - [window->ns.object frameRectForContentRect:contentRect - styleMask:styleMask]; - - [window->ns.object setFrame:frameRect display:YES]; - } - - return; - } - - if (window->monitor) - releaseMonitor(window); - - _glfwInputWindowMonitor(window, monitor); - - // HACK: Allow the state cached in Cocoa to catch up to reality - // TODO: Solve this in a less terrible way - _glfwPlatformPollEvents(); - - NSUInteger styleMask = [window->ns.object styleMask]; - - if (window->monitor) - { - styleMask &= ~(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); - styleMask |= NSWindowStyleMaskBorderless; - } - else - { - if (window->decorated) - { - styleMask &= ~NSWindowStyleMaskBorderless; - styleMask |= (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); - } - - if (window->resizable) - styleMask |= NSWindowStyleMaskResizable; - else - styleMask &= ~NSWindowStyleMaskResizable; - } - - [window->ns.object setStyleMask:styleMask]; - // HACK: Changing the style mask can cause the first responder to be cleared - [window->ns.object makeFirstResponder:window->ns.view]; - - if (window->monitor) - { - [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; - [window->ns.object setHasShadow:NO]; - - acquireMonitor(window); - } - else - { - NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), - width, height); - NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect - styleMask:styleMask]; - [window->ns.object setFrame:frameRect display:YES]; - - if (window->numer != GLFW_DONT_CARE && - window->denom != GLFW_DONT_CARE) - { - [window->ns.object setContentAspectRatio:NSMakeSize(window->numer, - window->denom)]; - } - - if (window->minwidth != GLFW_DONT_CARE && - window->minheight != GLFW_DONT_CARE) - { - [window->ns.object setContentMinSize:NSMakeSize(window->minwidth, - window->minheight)]; - } - - if (window->maxwidth != GLFW_DONT_CARE && - window->maxheight != GLFW_DONT_CARE) - { - [window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth, - window->maxheight)]; - } - - if (window->floating) - [window->ns.object setLevel:NSFloatingWindowLevel]; - else - [window->ns.object setLevel:NSNormalWindowLevel]; - - if (window->resizable) - { - const NSWindowCollectionBehavior behavior = - NSWindowCollectionBehaviorFullScreenPrimary | - NSWindowCollectionBehaviorManaged; - [window->ns.object setCollectionBehavior:behavior]; - } - else - { - const NSWindowCollectionBehavior behavior = - NSWindowCollectionBehaviorFullScreenNone; - [window->ns.object setCollectionBehavior:behavior]; - } - - [window->ns.object setHasShadow:YES]; - // HACK: Clearing NSWindowStyleMaskTitled resets and disables the window - // title property but the miniwindow title property is unaffected - [window->ns.object setTitle:[window->ns.object miniwindowTitle]]; - } - - } // autoreleasepool -} - -int _glfwPlatformWindowFocused(_GLFWwindow* window) -{ - @autoreleasepool { - return [window->ns.object isKeyWindow]; - } // autoreleasepool -} - -int _glfwPlatformWindowIconified(_GLFWwindow* window) -{ - @autoreleasepool { - return [window->ns.object isMiniaturized]; - } // autoreleasepool -} - -int _glfwPlatformWindowVisible(_GLFWwindow* window) -{ - @autoreleasepool { - return [window->ns.object isVisible]; - } // autoreleasepool -} - -int _glfwPlatformWindowMaximized(_GLFWwindow* window) -{ - @autoreleasepool { - - if (window->resizable) - return [window->ns.object isZoomed]; - else - return GLFW_FALSE; - - } // autoreleasepool -} - -int _glfwPlatformWindowHovered(_GLFWwindow* window) -{ - @autoreleasepool { - - const NSPoint point = [NSEvent mouseLocation]; - - if ([NSWindow windowNumberAtPoint:point belowWindowWithWindowNumber:0] != - [window->ns.object windowNumber]) - { - return GLFW_FALSE; - } - - return NSMouseInRect(point, - [window->ns.object convertRectToScreen:[window->ns.view frame]], NO); - - } // autoreleasepool -} - -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) -{ - @autoreleasepool { - return ![window->ns.object isOpaque] && ![window->ns.view isOpaque]; - } // autoreleasepool -} - -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) -{ - @autoreleasepool { - - const NSUInteger styleMask = [window->ns.object styleMask]; - if (enabled) - { - [window->ns.object setStyleMask:(styleMask | NSWindowStyleMaskResizable)]; - const NSWindowCollectionBehavior behavior = - NSWindowCollectionBehaviorFullScreenPrimary | - NSWindowCollectionBehaviorManaged; - [window->ns.object setCollectionBehavior:behavior]; - } - else - { - [window->ns.object setStyleMask:(styleMask & ~NSWindowStyleMaskResizable)]; - const NSWindowCollectionBehavior behavior = - NSWindowCollectionBehaviorFullScreenNone; - [window->ns.object setCollectionBehavior:behavior]; - } - - } // autoreleasepool -} - -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) -{ - @autoreleasepool { - - NSUInteger styleMask = [window->ns.object styleMask]; - if (enabled) - { - styleMask |= (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); - styleMask &= ~NSWindowStyleMaskBorderless; - } - else - { - styleMask |= NSWindowStyleMaskBorderless; - styleMask &= ~(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); - } - - [window->ns.object setStyleMask:styleMask]; - [window->ns.object makeFirstResponder:window->ns.view]; - - } // autoreleasepool -} - -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) -{ - @autoreleasepool { - if (enabled) - [window->ns.object setLevel:NSFloatingWindowLevel]; - else - [window->ns.object setLevel:NSNormalWindowLevel]; - } // autoreleasepool -} - -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) -{ - @autoreleasepool { - return (float) [window->ns.object alphaValue]; - } // autoreleasepool -} - -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) -{ - @autoreleasepool { - [window->ns.object setAlphaValue:opacity]; - } // autoreleasepool -} - -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) -{ -} - -GLFWbool _glfwPlatformRawMouseMotionSupported(void) -{ - return GLFW_FALSE; -} - -void _glfwPlatformPollEvents(void) -{ - @autoreleasepool { - - if (!_glfw.ns.finishedLaunching) - [NSApp run]; - - for (;;) - { - NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate distantPast] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - if (event == nil) - break; - - [NSApp sendEvent:event]; - } - - } // autoreleasepool -} - -void _glfwPlatformWaitEvents(void) -{ - @autoreleasepool { - - if (!_glfw.ns.finishedLaunching) - [NSApp run]; - - // I wanted to pass NO to dequeue:, and rely on PollEvents to - // dequeue and send. For reasons not at all clear to me, passing - // NO to dequeue: causes this method never to return. - NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate distantFuture] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - [NSApp sendEvent:event]; - - _glfwPlatformPollEvents(); - - } // autoreleasepool -} - -void _glfwPlatformWaitEventsTimeout(double timeout) -{ - @autoreleasepool { - - if (!_glfw.ns.finishedLaunching) - [NSApp run]; - - NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout]; - NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:date - inMode:NSDefaultRunLoopMode - dequeue:YES]; - if (event) - [NSApp sendEvent:event]; - - _glfwPlatformPollEvents(); - - } // autoreleasepool -} - -void _glfwPlatformPostEmptyEvent(void) -{ - @autoreleasepool { - - if (!_glfw.ns.finishedLaunching) - [NSApp run]; - - NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined - location:NSMakePoint(0, 0) - modifierFlags:0 - timestamp:0 - windowNumber:0 - context:nil - subtype:0 - data1:0 - data2:0]; - [NSApp postEvent:event atStart:YES]; - - } // autoreleasepool -} - -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) -{ - @autoreleasepool { - - const NSRect contentRect = [window->ns.view frame]; - // NOTE: The returned location uses base 0,1 not 0,0 - const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; - - if (xpos) - *xpos = pos.x; - if (ypos) - *ypos = contentRect.size.height - pos.y; - - } // autoreleasepool -} - -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) -{ - @autoreleasepool { - - updateCursorImage(window); - - const NSRect contentRect = [window->ns.view frame]; - // NOTE: The returned location uses base 0,1 not 0,0 - const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; - - window->ns.cursorWarpDeltaX += x - pos.x; - window->ns.cursorWarpDeltaY += y - contentRect.size.height + pos.y; - - if (window->monitor) - { - CGDisplayMoveCursorToPoint(window->monitor->ns.displayID, - CGPointMake(x, y)); - } - else - { - const NSRect localRect = NSMakeRect(x, contentRect.size.height - y - 1, 0, 0); - const NSRect globalRect = [window->ns.object convertRectToScreen:localRect]; - const NSPoint globalPoint = globalRect.origin; - - CGWarpMouseCursorPosition(CGPointMake(globalPoint.x, - _glfwTransformYNS(globalPoint.y))); - } - - // HACK: Calling this right after setting the cursor position prevents macOS - // from freezing the cursor for a fraction of a second afterwards - if (window->cursorMode != GLFW_CURSOR_DISABLED) - CGAssociateMouseAndMouseCursorPosition(true); - - } // autoreleasepool -} - -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) -{ - @autoreleasepool { - if (_glfwPlatformWindowFocused(window)) - updateCursorMode(window); - } // autoreleasepool -} - -const char* _glfwPlatformGetScancodeName(int scancode) -{ - @autoreleasepool { - - if (scancode < 0 || scancode > 0xff || - _glfw.ns.keycodes[scancode] == GLFW_KEY_UNKNOWN) - { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); - return NULL; - } - - const int key = _glfw.ns.keycodes[scancode]; - - UInt32 deadKeyState = 0; - UniChar characters[4]; - UniCharCount characterCount = 0; - - if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes], - scancode, - kUCKeyActionDisplay, - 0, - LMGetKbdType(), - kUCKeyTranslateNoDeadKeysBit, - &deadKeyState, - sizeof(characters) / sizeof(characters[0]), - &characterCount, - characters) != noErr) - { - return NULL; - } - - if (!characterCount) - return NULL; - - CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, - characters, - characterCount, - kCFAllocatorNull); - CFStringGetCString(string, - _glfw.ns.keynames[key], - sizeof(_glfw.ns.keynames[key]), - kCFStringEncodingUTF8); - CFRelease(string); - - return _glfw.ns.keynames[key]; - - } // autoreleasepool -} - -int _glfwPlatformGetKeyScancode(int key) -{ - return _glfw.ns.scancodes[key]; -} - -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) -{ - @autoreleasepool { - - NSImage* native; - NSBitmapImageRep* rep; - - rep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:NULL - pixelsWide:image->width - pixelsHigh:image->height - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSCalibratedRGBColorSpace - bitmapFormat:NSBitmapFormatAlphaNonpremultiplied - bytesPerRow:image->width * 4 - bitsPerPixel:32]; - - if (rep == nil) - return GLFW_FALSE; - - memcpy([rep bitmapData], image->pixels, image->width * image->height * 4); - - native = [[NSImage alloc] initWithSize:NSMakeSize(image->width, image->height)]; - [native addRepresentation:rep]; - - cursor->ns.object = [[NSCursor alloc] initWithImage:native - hotSpot:NSMakePoint(xhot, yhot)]; - - [native release]; - [rep release]; - - if (cursor->ns.object == nil) - return GLFW_FALSE; - - return GLFW_TRUE; - - } // autoreleasepool -} - -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) -{ - @autoreleasepool { - - if (shape == GLFW_ARROW_CURSOR) - cursor->ns.object = [NSCursor arrowCursor]; - else if (shape == GLFW_IBEAM_CURSOR) - cursor->ns.object = [NSCursor IBeamCursor]; - else if (shape == GLFW_CROSSHAIR_CURSOR) - cursor->ns.object = [NSCursor crosshairCursor]; - else if (shape == GLFW_HAND_CURSOR) - cursor->ns.object = [NSCursor pointingHandCursor]; - else if (shape == GLFW_HRESIZE_CURSOR) - cursor->ns.object = [NSCursor resizeLeftRightCursor]; - else if (shape == GLFW_VRESIZE_CURSOR) - cursor->ns.object = [NSCursor resizeUpDownCursor]; - - if (!cursor->ns.object) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to retrieve standard cursor"); - return GLFW_FALSE; - } - - [cursor->ns.object retain]; - return GLFW_TRUE; - - } // autoreleasepool -} - -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) -{ - @autoreleasepool { - if (cursor->ns.object) - [(NSCursor*) cursor->ns.object release]; - } // autoreleasepool -} - -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) -{ - @autoreleasepool { - if (cursorInContentArea(window)) - updateCursorImage(window); - } // autoreleasepool -} - -void _glfwPlatformSetClipboardString(const char* string) -{ - @autoreleasepool { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; - [pasteboard setString:@(string) forType:NSPasteboardTypeString]; - } // autoreleasepool -} - -const char* _glfwPlatformGetClipboardString(void) -{ - @autoreleasepool { - - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - - if (![[pasteboard types] containsObject:NSPasteboardTypeString]) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "Cocoa: Failed to retrieve string from pasteboard"); - return NULL; - } - - NSString* object = [pasteboard stringForType:NSPasteboardTypeString]; - if (!object) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to retrieve object from pasteboard"); - return NULL; - } - - free(_glfw.ns.clipboardString); - _glfw.ns.clipboardString = _glfw_strdup([object UTF8String]); - - return _glfw.ns.clipboardString; - - } // autoreleasepool -} - -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) -{ - if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface) - { - extensions[0] = "VK_KHR_surface"; - extensions[1] = "VK_EXT_metal_surface"; - } - else if (_glfw.vk.KHR_surface && _glfw.vk.MVK_macos_surface) - { - extensions[0] = "VK_KHR_surface"; - extensions[1] = "VK_MVK_macos_surface"; - } -} - -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) -{ - return GLFW_TRUE; -} - -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) -{ - @autoreleasepool { - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 - // HACK: Dynamically load Core Animation to avoid adding an extra - // dependency for the majority who don't use MoltenVK - NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"]; - if (!bundle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to find QuartzCore.framework"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - // NOTE: Create the layer here as makeBackingLayer should not return nil - window->ns.layer = [[bundle classNamed:@"CAMetalLayer"] layer]; - if (!window->ns.layer) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to create layer for view"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - if (window->ns.retina) - [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; - - [window->ns.view setLayer:window->ns.layer]; - [window->ns.view setWantsLayer:YES]; - - VkResult err; - - if (_glfw.vk.EXT_metal_surface) - { - VkMetalSurfaceCreateInfoEXT sci; - - PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT; - vkCreateMetalSurfaceEXT = (PFN_vkCreateMetalSurfaceEXT) - vkGetInstanceProcAddr(instance, "vkCreateMetalSurfaceEXT"); - if (!vkCreateMetalSurfaceEXT) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Cocoa: Vulkan instance missing VK_EXT_metal_surface extension"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - memset(&sci, 0, sizeof(sci)); - sci.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; - sci.pLayer = window->ns.layer; - - err = vkCreateMetalSurfaceEXT(instance, &sci, allocator, surface); - } - else - { - VkMacOSSurfaceCreateInfoMVK sci; - - PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK; - vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK) - vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK"); - if (!vkCreateMacOSSurfaceMVK) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - memset(&sci, 0, sizeof(sci)); - sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; - sci.pView = window->ns.view; - - err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface); - } - - if (err) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to create Vulkan surface: %s", - _glfwGetVulkanResultString(err)); - } - - return err; -#else - return VK_ERROR_EXTENSION_NOT_PRESENT; -#endif - - } // autoreleasepool -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(nil); - return window->ns.object; -} - diff --git a/libs/zglfw/libs/glfw/src/context.c b/libs/zglfw/libs/glfw/src/context.c deleted file mode 100644 index d86e0faec..000000000 --- a/libs/zglfw/libs/glfw/src/context.c +++ /dev/null @@ -1,758 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Checks whether the desired context attributes are valid -// -// This function checks things like whether the specified client API version -// exists and whether all relevant options have supported and non-conflicting -// values -// -GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) -{ - if (ctxconfig->share) - { - if (ctxconfig->client == GLFW_NO_API || - ctxconfig->share->context.client == GLFW_NO_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return GLFW_FALSE; - } - } - - if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && - ctxconfig->source != GLFW_EGL_CONTEXT_API && - ctxconfig->source != GLFW_OSMESA_CONTEXT_API) - { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context creation API 0x%08X", - ctxconfig->source); - return GLFW_FALSE; - } - - if (ctxconfig->client != GLFW_NO_API && - ctxconfig->client != GLFW_OPENGL_API && - ctxconfig->client != GLFW_OPENGL_ES_API) - { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid client API 0x%08X", - ctxconfig->client); - return GLFW_FALSE; - } - - if (ctxconfig->client == GLFW_OPENGL_API) - { - if ((ctxconfig->major < 1 || ctxconfig->minor < 0) || - (ctxconfig->major == 1 && ctxconfig->minor > 5) || - (ctxconfig->major == 2 && ctxconfig->minor > 1) || - (ctxconfig->major == 3 && ctxconfig->minor > 3)) - { - // OpenGL 1.0 is the smallest valid version - // OpenGL 1.x series ended with version 1.5 - // OpenGL 2.x series ended with version 2.1 - // OpenGL 3.x series ended with version 3.3 - // For now, let everything else through - - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid OpenGL version %i.%i", - ctxconfig->major, ctxconfig->minor); - return GLFW_FALSE; - } - - if (ctxconfig->profile) - { - if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE && - ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) - { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid OpenGL profile 0x%08X", - ctxconfig->profile); - return GLFW_FALSE; - } - - if (ctxconfig->major <= 2 || - (ctxconfig->major == 3 && ctxconfig->minor < 2)) - { - // Desktop OpenGL context profiles are only defined for version 3.2 - // and above - - _glfwInputError(GLFW_INVALID_VALUE, - "Context profiles are only defined for OpenGL version 3.2 and above"); - return GLFW_FALSE; - } - } - - if (ctxconfig->forward && ctxconfig->major <= 2) - { - // Forward-compatible contexts are only defined for OpenGL version 3.0 and above - _glfwInputError(GLFW_INVALID_VALUE, - "Forward-compatibility is only defined for OpenGL version 3.0 and above"); - return GLFW_FALSE; - } - } - else if (ctxconfig->client == GLFW_OPENGL_ES_API) - { - if (ctxconfig->major < 1 || ctxconfig->minor < 0 || - (ctxconfig->major == 1 && ctxconfig->minor > 1) || - (ctxconfig->major == 2 && ctxconfig->minor > 0)) - { - // OpenGL ES 1.0 is the smallest valid version - // OpenGL ES 1.x series ended with version 1.1 - // OpenGL ES 2.x series ended with version 2.0 - // For now, let everything else through - - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid OpenGL ES version %i.%i", - ctxconfig->major, ctxconfig->minor); - return GLFW_FALSE; - } - } - - if (ctxconfig->robustness) - { - if (ctxconfig->robustness != GLFW_NO_RESET_NOTIFICATION && - ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) - { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context robustness mode 0x%08X", - ctxconfig->robustness); - return GLFW_FALSE; - } - } - - if (ctxconfig->release) - { - if (ctxconfig->release != GLFW_RELEASE_BEHAVIOR_NONE && - ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) - { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context release behavior 0x%08X", - ctxconfig->release); - return GLFW_FALSE; - } - } - - return GLFW_TRUE; -} - -// Chooses the framebuffer config that best matches the desired one -// -const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, - const _GLFWfbconfig* alternatives, - unsigned int count) -{ - unsigned int i; - unsigned int missing, leastMissing = UINT_MAX; - unsigned int colorDiff, leastColorDiff = UINT_MAX; - unsigned int extraDiff, leastExtraDiff = UINT_MAX; - const _GLFWfbconfig* current; - const _GLFWfbconfig* closest = NULL; - - for (i = 0; i < count; i++) - { - current = alternatives + i; - - if (desired->stereo > 0 && current->stereo == 0) - { - // Stereo is a hard constraint - continue; - } - - // Count number of missing buffers - { - missing = 0; - - if (desired->alphaBits > 0 && current->alphaBits == 0) - missing++; - - if (desired->depthBits > 0 && current->depthBits == 0) - missing++; - - if (desired->stencilBits > 0 && current->stencilBits == 0) - missing++; - - if (desired->auxBuffers > 0 && - current->auxBuffers < desired->auxBuffers) - { - missing += desired->auxBuffers - current->auxBuffers; - } - - if (desired->samples > 0 && current->samples == 0) - { - // Technically, several multisampling buffers could be - // involved, but that's a lower level implementation detail and - // not important to us here, so we count them as one - missing++; - } - - if (desired->transparent != current->transparent) - missing++; - } - - // These polynomials make many small channel size differences matter - // less than one large channel size difference - - // Calculate color channel size difference value - { - colorDiff = 0; - - if (desired->redBits != GLFW_DONT_CARE) - { - colorDiff += (desired->redBits - current->redBits) * - (desired->redBits - current->redBits); - } - - if (desired->greenBits != GLFW_DONT_CARE) - { - colorDiff += (desired->greenBits - current->greenBits) * - (desired->greenBits - current->greenBits); - } - - if (desired->blueBits != GLFW_DONT_CARE) - { - colorDiff += (desired->blueBits - current->blueBits) * - (desired->blueBits - current->blueBits); - } - } - - // Calculate non-color channel size difference value - { - extraDiff = 0; - - if (desired->alphaBits != GLFW_DONT_CARE) - { - extraDiff += (desired->alphaBits - current->alphaBits) * - (desired->alphaBits - current->alphaBits); - } - - if (desired->depthBits != GLFW_DONT_CARE) - { - extraDiff += (desired->depthBits - current->depthBits) * - (desired->depthBits - current->depthBits); - } - - if (desired->stencilBits != GLFW_DONT_CARE) - { - extraDiff += (desired->stencilBits - current->stencilBits) * - (desired->stencilBits - current->stencilBits); - } - - if (desired->accumRedBits != GLFW_DONT_CARE) - { - extraDiff += (desired->accumRedBits - current->accumRedBits) * - (desired->accumRedBits - current->accumRedBits); - } - - if (desired->accumGreenBits != GLFW_DONT_CARE) - { - extraDiff += (desired->accumGreenBits - current->accumGreenBits) * - (desired->accumGreenBits - current->accumGreenBits); - } - - if (desired->accumBlueBits != GLFW_DONT_CARE) - { - extraDiff += (desired->accumBlueBits - current->accumBlueBits) * - (desired->accumBlueBits - current->accumBlueBits); - } - - if (desired->accumAlphaBits != GLFW_DONT_CARE) - { - extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) * - (desired->accumAlphaBits - current->accumAlphaBits); - } - - if (desired->samples != GLFW_DONT_CARE) - { - extraDiff += (desired->samples - current->samples) * - (desired->samples - current->samples); - } - - if (desired->sRGB && !current->sRGB) - extraDiff++; - } - - // Figure out if the current one is better than the best one found so far - // Least number of missing buffers is the most important heuristic, - // then color buffer size match and lastly size match for other buffers - - if (missing < leastMissing) - closest = current; - else if (missing == leastMissing) - { - if ((colorDiff < leastColorDiff) || - (colorDiff == leastColorDiff && extraDiff < leastExtraDiff)) - { - closest = current; - } - } - - if (current == closest) - { - leastMissing = missing; - leastColorDiff = colorDiff; - leastExtraDiff = extraDiff; - } - } - - return closest; -} - -// Retrieves the attributes of the current context -// -GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig) -{ - int i; - _GLFWwindow* previous; - const char* version; - const char* prefixes[] = - { - "OpenGL ES-CM ", - "OpenGL ES-CL ", - "OpenGL ES ", - NULL - }; - - window->context.source = ctxconfig->source; - window->context.client = GLFW_OPENGL_API; - - previous = _glfwPlatformGetTls(&_glfw.contextSlot); - glfwMakeContextCurrent((GLFWwindow*) window); - - window->context.GetIntegerv = (PFNGLGETINTEGERVPROC) - window->context.getProcAddress("glGetIntegerv"); - window->context.GetString = (PFNGLGETSTRINGPROC) - window->context.getProcAddress("glGetString"); - if (!window->context.GetIntegerv || !window->context.GetString) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); - glfwMakeContextCurrent((GLFWwindow*) previous); - return GLFW_FALSE; - } - - version = (const char*) window->context.GetString(GL_VERSION); - if (!version) - { - if (ctxconfig->client == GLFW_OPENGL_API) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "OpenGL version string retrieval is broken"); - } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "OpenGL ES version string retrieval is broken"); - } - - glfwMakeContextCurrent((GLFWwindow*) previous); - return GLFW_FALSE; - } - - for (i = 0; prefixes[i]; i++) - { - const size_t length = strlen(prefixes[i]); - - if (strncmp(version, prefixes[i], length) == 0) - { - version += length; - window->context.client = GLFW_OPENGL_ES_API; - break; - } - } - - if (!sscanf(version, "%d.%d.%d", - &window->context.major, - &window->context.minor, - &window->context.revision)) - { - if (window->context.client == GLFW_OPENGL_API) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "No version found in OpenGL version string"); - } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "No version found in OpenGL ES version string"); - } - - glfwMakeContextCurrent((GLFWwindow*) previous); - return GLFW_FALSE; - } - - if (window->context.major < ctxconfig->major || - (window->context.major == ctxconfig->major && - window->context.minor < ctxconfig->minor)) - { - // The desired OpenGL version is greater than the actual version - // This only happens if the machine lacks {GLX|WGL}_ARB_create_context - // /and/ the user has requested an OpenGL version greater than 1.0 - - // For API consistency, we emulate the behavior of the - // {GLX|WGL}_ARB_create_context extension and fail here - - if (window->context.client == GLFW_OPENGL_API) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "Requested OpenGL version %i.%i, got version %i.%i", - ctxconfig->major, ctxconfig->minor, - window->context.major, window->context.minor); - } - else - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "Requested OpenGL ES version %i.%i, got version %i.%i", - ctxconfig->major, ctxconfig->minor, - window->context.major, window->context.minor); - } - - glfwMakeContextCurrent((GLFWwindow*) previous); - return GLFW_FALSE; - } - - if (window->context.major >= 3) - { - // OpenGL 3.0+ uses a different function for extension string retrieval - // We cache it here instead of in glfwExtensionSupported mostly to alert - // users as early as possible that their build may be broken - - window->context.GetStringi = (PFNGLGETSTRINGIPROC) - window->context.getProcAddress("glGetStringi"); - if (!window->context.GetStringi) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Entry point retrieval is broken"); - glfwMakeContextCurrent((GLFWwindow*) previous); - return GLFW_FALSE; - } - } - - if (window->context.client == GLFW_OPENGL_API) - { - // Read back context flags (OpenGL 3.0 and above) - if (window->context.major >= 3) - { - GLint flags; - window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags); - - if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) - window->context.forward = GLFW_TRUE; - - if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) - window->context.debug = GLFW_TRUE; - else if (glfwExtensionSupported("GL_ARB_debug_output") && - ctxconfig->debug) - { - // HACK: This is a workaround for older drivers (pre KHR_debug) - // not setting the debug bit in the context flags for - // debug contexts - window->context.debug = GLFW_TRUE; - } - - if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR) - window->context.noerror = GLFW_TRUE; - } - - // Read back OpenGL context profile (OpenGL 3.2 and above) - if (window->context.major >= 4 || - (window->context.major == 3 && window->context.minor >= 2)) - { - GLint mask; - window->context.GetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); - - if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) - window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; - else if (mask & GL_CONTEXT_CORE_PROFILE_BIT) - window->context.profile = GLFW_OPENGL_CORE_PROFILE; - else if (glfwExtensionSupported("GL_ARB_compatibility")) - { - // HACK: This is a workaround for the compatibility profile bit - // not being set in the context flags if an OpenGL 3.2+ - // context was created without having requested a specific - // version - window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; - } - } - - // Read back robustness strategy - if (glfwExtensionSupported("GL_ARB_robustness")) - { - // NOTE: We avoid using the context flags for detection, as they are - // only present from 3.0 while the extension applies from 1.1 - - GLint strategy; - window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, - &strategy); - - if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) - window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; - else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) - window->context.robustness = GLFW_NO_RESET_NOTIFICATION; - } - } - else - { - // Read back robustness strategy - if (glfwExtensionSupported("GL_EXT_robustness")) - { - // NOTE: The values of these constants match those of the OpenGL ARB - // one, so we can reuse them here - - GLint strategy; - window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, - &strategy); - - if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) - window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; - else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) - window->context.robustness = GLFW_NO_RESET_NOTIFICATION; - } - } - - if (glfwExtensionSupported("GL_KHR_context_flush_control")) - { - GLint behavior; - window->context.GetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &behavior); - - if (behavior == GL_NONE) - window->context.release = GLFW_RELEASE_BEHAVIOR_NONE; - else if (behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH) - window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH; - } - - // Clearing the front buffer to black to avoid garbage pixels left over from - // previous uses of our bit of VRAM - { - PFNGLCLEARPROC glClear = (PFNGLCLEARPROC) - window->context.getProcAddress("glClear"); - glClear(GL_COLOR_BUFFER_BIT); - - if (window->doublebuffer) - window->context.swapBuffers(window); - } - - glfwMakeContextCurrent((GLFWwindow*) previous); - return GLFW_TRUE; -} - -// Searches an extension string for the specified extension -// -GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions) -{ - const char* start = extensions; - - for (;;) - { - const char* where; - const char* terminator; - - where = strstr(start, string); - if (!where) - return GLFW_FALSE; - - terminator = where + strlen(string); - if (where == start || *(where - 1) == ' ') - { - if (*terminator == ' ' || *terminator == '\0') - break; - } - - start = terminator; - } - - return GLFW_TRUE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW public API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFWwindow* previous; - - _GLFW_REQUIRE_INIT(); - - previous = _glfwPlatformGetTls(&_glfw.contextSlot); - - if (window && window->context.client == GLFW_NO_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, - "Cannot make current with a window that has no OpenGL or OpenGL ES context"); - return; - } - - if (previous) - { - if (!window || window->context.source != previous->context.source) - previous->context.makeCurrent(NULL); - } - - if (window) - window->context.makeCurrent(window); -} - -GLFWAPI GLFWwindow* glfwGetCurrentContext(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfwPlatformGetTls(&_glfw.contextSlot); -} - -GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - if (window->context.client == GLFW_NO_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, - "Cannot swap buffers of a window that has no OpenGL or OpenGL ES context"); - return; - } - - window->context.swapBuffers(window); -} - -GLFWAPI void glfwSwapInterval(int interval) -{ - _GLFWwindow* window; - - _GLFW_REQUIRE_INIT(); - - window = _glfwPlatformGetTls(&_glfw.contextSlot); - if (!window) - { - _glfwInputError(GLFW_NO_CURRENT_CONTEXT, - "Cannot set swap interval without a current OpenGL or OpenGL ES context"); - return; - } - - window->context.swapInterval(interval); -} - -GLFWAPI int glfwExtensionSupported(const char* extension) -{ - _GLFWwindow* window; - assert(extension != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - - window = _glfwPlatformGetTls(&_glfw.contextSlot); - if (!window) - { - _glfwInputError(GLFW_NO_CURRENT_CONTEXT, - "Cannot query extension without a current OpenGL or OpenGL ES context"); - return GLFW_FALSE; - } - - if (*extension == '\0') - { - _glfwInputError(GLFW_INVALID_VALUE, "Extension name cannot be an empty string"); - return GLFW_FALSE; - } - - if (window->context.major >= 3) - { - int i; - GLint count; - - // Check if extension is in the modern OpenGL extensions string list - - window->context.GetIntegerv(GL_NUM_EXTENSIONS, &count); - - for (i = 0; i < count; i++) - { - const char* en = (const char*) - window->context.GetStringi(GL_EXTENSIONS, i); - if (!en) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Extension string retrieval is broken"); - return GLFW_FALSE; - } - - if (strcmp(en, extension) == 0) - return GLFW_TRUE; - } - } - else - { - // Check if extension is in the old style OpenGL extensions string - - const char* extensions = (const char*) - window->context.GetString(GL_EXTENSIONS); - if (!extensions) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Extension string retrieval is broken"); - return GLFW_FALSE; - } - - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - - // Check if extension is in the platform-specific string - return window->context.extensionSupported(extension); -} - -GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) -{ - _GLFWwindow* window; - assert(procname != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - window = _glfwPlatformGetTls(&_glfw.contextSlot); - if (!window) - { - _glfwInputError(GLFW_NO_CURRENT_CONTEXT, - "Cannot query entry point without a current OpenGL or OpenGL ES context"); - return NULL; - } - - return window->context.getProcAddress(procname); -} - diff --git a/libs/zglfw/libs/glfw/src/egl_context.c b/libs/zglfw/libs/glfw/src/egl_context.c deleted file mode 100644 index 58d9557b0..000000000 --- a/libs/zglfw/libs/glfw/src/egl_context.c +++ /dev/null @@ -1,809 +0,0 @@ -//======================================================================== -// GLFW 3.3 EGL - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include - - -// Return a description of the specified EGL error -// -static const char* getEGLErrorString(EGLint error) -{ - switch (error) - { - case EGL_SUCCESS: - return "Success"; - case EGL_NOT_INITIALIZED: - return "EGL is not or could not be initialized"; - case EGL_BAD_ACCESS: - return "EGL cannot access a requested resource"; - case EGL_BAD_ALLOC: - return "EGL failed to allocate resources for the requested operation"; - case EGL_BAD_ATTRIBUTE: - return "An unrecognized attribute or attribute value was passed in the attribute list"; - case EGL_BAD_CONTEXT: - return "An EGLContext argument does not name a valid EGL rendering context"; - case EGL_BAD_CONFIG: - return "An EGLConfig argument does not name a valid EGL frame buffer configuration"; - case EGL_BAD_CURRENT_SURFACE: - return "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid"; - case EGL_BAD_DISPLAY: - return "An EGLDisplay argument does not name a valid EGL display connection"; - case EGL_BAD_SURFACE: - return "An EGLSurface argument does not name a valid surface configured for GL rendering"; - case EGL_BAD_MATCH: - return "Arguments are inconsistent"; - case EGL_BAD_PARAMETER: - return "One or more argument values are invalid"; - case EGL_BAD_NATIVE_PIXMAP: - return "A NativePixmapType argument does not refer to a valid native pixmap"; - case EGL_BAD_NATIVE_WINDOW: - return "A NativeWindowType argument does not refer to a valid native window"; - case EGL_CONTEXT_LOST: - return "The application must destroy all contexts and reinitialise"; - default: - return "ERROR: UNKNOWN EGL ERROR"; - } -} - -// Returns the specified attribute of the specified EGLConfig -// -static int getEGLConfigAttrib(EGLConfig config, int attrib) -{ - int value; - eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value); - return value; -} - -// Return the EGLConfig most closely matching the specified hints -// -static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* desired, - EGLConfig* result) -{ - EGLConfig* nativeConfigs; - _GLFWfbconfig* usableConfigs; - const _GLFWfbconfig* closest; - int i, nativeCount, usableCount; - - eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); - if (!nativeCount) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned"); - return GLFW_FALSE; - } - - nativeConfigs = calloc(nativeCount, sizeof(EGLConfig)); - eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount); - - usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); - usableCount = 0; - - for (i = 0; i < nativeCount; i++) - { - const EGLConfig n = nativeConfigs[i]; - _GLFWfbconfig* u = usableConfigs + usableCount; - - // Only consider RGB(A) EGLConfigs - if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER) - continue; - - // Only consider window EGLConfigs - if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT)) - continue; - -#if defined(_GLFW_X11) - { - XVisualInfo vi = {0}; - - // Only consider EGLConfigs with associated Visuals - vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); - if (!vi.visualid) - continue; - - if (desired->transparent) - { - int count; - XVisualInfo* vis = - XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count); - if (vis) - { - u->transparent = _glfwIsVisualTransparentX11(vis[0].visual); - XFree(vis); - } - } - } -#endif // _GLFW_X11 - - if (ctxconfig->client == GLFW_OPENGL_ES_API) - { - if (ctxconfig->major == 1) - { - if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT)) - continue; - } - else - { - if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT)) - continue; - } - } - else if (ctxconfig->client == GLFW_OPENGL_API) - { - if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT)) - continue; - } - - u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE); - u->greenBits = getEGLConfigAttrib(n, EGL_GREEN_SIZE); - u->blueBits = getEGLConfigAttrib(n, EGL_BLUE_SIZE); - - u->alphaBits = getEGLConfigAttrib(n, EGL_ALPHA_SIZE); - u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE); - u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE); - - u->samples = getEGLConfigAttrib(n, EGL_SAMPLES); - u->doublebuffer = desired->doublebuffer; - - u->handle = (uintptr_t) n; - usableCount++; - } - - closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); - if (closest) - *result = (EGLConfig) closest->handle; - - free(nativeConfigs); - free(usableConfigs); - - return closest != NULL; -} - -static void makeContextCurrentEGL(_GLFWwindow* window) -{ - if (window) - { - if (!eglMakeCurrent(_glfw.egl.display, - window->context.egl.surface, - window->context.egl.surface, - window->context.egl.handle)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "EGL: Failed to make context current: %s", - getEGLErrorString(eglGetError())); - return; - } - } - else - { - if (!eglMakeCurrent(_glfw.egl.display, - EGL_NO_SURFACE, - EGL_NO_SURFACE, - EGL_NO_CONTEXT)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "EGL: Failed to clear current context: %s", - getEGLErrorString(eglGetError())); - return; - } - } - - _glfwPlatformSetTls(&_glfw.contextSlot, window); -} - -static void swapBuffersEGL(_GLFWwindow* window) -{ - if (window != _glfwPlatformGetTls(&_glfw.contextSlot)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "EGL: The context must be current on the calling thread when swapping buffers"); - return; - } - -#if defined(_GLFW_WAYLAND) - // NOTE: Swapping buffers on a hidden window on Wayland makes it visible - if (!window->wl.visible) - return; -#endif - - eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); -} - -static void swapIntervalEGL(int interval) -{ - eglSwapInterval(_glfw.egl.display, interval); -} - -static int extensionSupportedEGL(const char* extension) -{ - const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS); - if (extensions) - { - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - - return GLFW_FALSE; -} - -static GLFWglproc getProcAddressEGL(const char* procname) -{ - _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); - - if (window->context.egl.client) - { - GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client, - procname); - if (proc) - return proc; - } - - return eglGetProcAddress(procname); -} - -static void destroyContextEGL(_GLFWwindow* window) -{ -#if defined(_GLFW_X11) - // NOTE: Do not unload libGL.so.1 while the X11 display is still open, - // as it will make XCloseDisplay segfault - if (window->context.client != GLFW_OPENGL_API) -#endif // _GLFW_X11 - { - if (window->context.egl.client) - { - _glfw_dlclose(window->context.egl.client); - window->context.egl.client = NULL; - } - } - - if (window->context.egl.surface) - { - eglDestroySurface(_glfw.egl.display, window->context.egl.surface); - window->context.egl.surface = EGL_NO_SURFACE; - } - - if (window->context.egl.handle) - { - eglDestroyContext(_glfw.egl.display, window->context.egl.handle); - window->context.egl.handle = EGL_NO_CONTEXT; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialize EGL -// -GLFWbool _glfwInitEGL(void) -{ - int i; - const char* sonames[] = - { -#if defined(_GLFW_EGL_LIBRARY) - _GLFW_EGL_LIBRARY, -#elif defined(_GLFW_WIN32) - "libEGL.dll", - "EGL.dll", -#elif defined(_GLFW_COCOA) - "libEGL.dylib", -#elif defined(__CYGWIN__) - "libEGL-1.so", -#elif defined(__OpenBSD__) || defined(__NetBSD__) - "libEGL.so", -#else - "libEGL.so.1", -#endif - NULL - }; - - if (_glfw.egl.handle) - return GLFW_TRUE; - - for (i = 0; sonames[i]; i++) - { - _glfw.egl.handle = _glfw_dlopen(sonames[i]); - if (_glfw.egl.handle) - break; - } - - if (!_glfw.egl.handle) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Library not found"); - return GLFW_FALSE; - } - - _glfw.egl.prefix = (strncmp(sonames[i], "lib", 3) == 0); - - _glfw.egl.GetConfigAttrib = (PFN_eglGetConfigAttrib) - _glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib"); - _glfw.egl.GetConfigs = (PFN_eglGetConfigs) - _glfw_dlsym(_glfw.egl.handle, "eglGetConfigs"); - _glfw.egl.GetDisplay = (PFN_eglGetDisplay) - _glfw_dlsym(_glfw.egl.handle, "eglGetDisplay"); - _glfw.egl.GetError = (PFN_eglGetError) - _glfw_dlsym(_glfw.egl.handle, "eglGetError"); - _glfw.egl.Initialize = (PFN_eglInitialize) - _glfw_dlsym(_glfw.egl.handle, "eglInitialize"); - _glfw.egl.Terminate = (PFN_eglTerminate) - _glfw_dlsym(_glfw.egl.handle, "eglTerminate"); - _glfw.egl.BindAPI = (PFN_eglBindAPI) - _glfw_dlsym(_glfw.egl.handle, "eglBindAPI"); - _glfw.egl.CreateContext = (PFN_eglCreateContext) - _glfw_dlsym(_glfw.egl.handle, "eglCreateContext"); - _glfw.egl.DestroySurface = (PFN_eglDestroySurface) - _glfw_dlsym(_glfw.egl.handle, "eglDestroySurface"); - _glfw.egl.DestroyContext = (PFN_eglDestroyContext) - _glfw_dlsym(_glfw.egl.handle, "eglDestroyContext"); - _glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface) - _glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface"); - _glfw.egl.MakeCurrent = (PFN_eglMakeCurrent) - _glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent"); - _glfw.egl.SwapBuffers = (PFN_eglSwapBuffers) - _glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers"); - _glfw.egl.SwapInterval = (PFN_eglSwapInterval) - _glfw_dlsym(_glfw.egl.handle, "eglSwapInterval"); - _glfw.egl.QueryString = (PFN_eglQueryString) - _glfw_dlsym(_glfw.egl.handle, "eglQueryString"); - _glfw.egl.GetProcAddress = (PFN_eglGetProcAddress) - _glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress"); - - if (!_glfw.egl.GetConfigAttrib || - !_glfw.egl.GetConfigs || - !_glfw.egl.GetDisplay || - !_glfw.egl.GetError || - !_glfw.egl.Initialize || - !_glfw.egl.Terminate || - !_glfw.egl.BindAPI || - !_glfw.egl.CreateContext || - !_glfw.egl.DestroySurface || - !_glfw.egl.DestroyContext || - !_glfw.egl.CreateWindowSurface || - !_glfw.egl.MakeCurrent || - !_glfw.egl.SwapBuffers || - !_glfw.egl.SwapInterval || - !_glfw.egl.QueryString || - !_glfw.egl.GetProcAddress) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "EGL: Failed to load required entry points"); - - _glfwTerminateEGL(); - return GLFW_FALSE; - } - - _glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY); - if (_glfw.egl.display == EGL_NO_DISPLAY) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "EGL: Failed to get EGL display: %s", - getEGLErrorString(eglGetError())); - - _glfwTerminateEGL(); - return GLFW_FALSE; - } - - if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor)) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "EGL: Failed to initialize EGL: %s", - getEGLErrorString(eglGetError())); - - _glfwTerminateEGL(); - return GLFW_FALSE; - } - - _glfw.egl.KHR_create_context = - extensionSupportedEGL("EGL_KHR_create_context"); - _glfw.egl.KHR_create_context_no_error = - extensionSupportedEGL("EGL_KHR_create_context_no_error"); - _glfw.egl.KHR_gl_colorspace = - extensionSupportedEGL("EGL_KHR_gl_colorspace"); - _glfw.egl.KHR_get_all_proc_addresses = - extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); - _glfw.egl.KHR_context_flush_control = - extensionSupportedEGL("EGL_KHR_context_flush_control"); - _glfw.egl.EXT_present_opaque = - extensionSupportedEGL("EGL_EXT_present_opaque"); - - return GLFW_TRUE; -} - -// Terminate EGL -// -void _glfwTerminateEGL(void) -{ - if (_glfw.egl.display) - { - eglTerminate(_glfw.egl.display); - _glfw.egl.display = EGL_NO_DISPLAY; - } - - if (_glfw.egl.handle) - { - _glfw_dlclose(_glfw.egl.handle); - _glfw.egl.handle = NULL; - } -} - -#define setAttrib(a, v) \ -{ \ - assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ - attribs[index++] = a; \ - attribs[index++] = v; \ -} - -// Create the OpenGL or OpenGL ES context -// -GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - EGLint attribs[40]; - EGLConfig config; - EGLContext share = NULL; - int index = 0; - - if (!_glfw.egl.display) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: API not available"); - return GLFW_FALSE; - } - - if (ctxconfig->share) - share = ctxconfig->share->context.egl.handle; - - if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "EGL: Failed to find a suitable EGLConfig"); - return GLFW_FALSE; - } - - if (ctxconfig->client == GLFW_OPENGL_ES_API) - { - if (!eglBindAPI(EGL_OPENGL_ES_API)) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "EGL: Failed to bind OpenGL ES: %s", - getEGLErrorString(eglGetError())); - return GLFW_FALSE; - } - } - else - { - if (!eglBindAPI(EGL_OPENGL_API)) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "EGL: Failed to bind OpenGL: %s", - getEGLErrorString(eglGetError())); - return GLFW_FALSE; - } - } - - if (_glfw.egl.KHR_create_context) - { - int mask = 0, flags = 0; - - if (ctxconfig->client == GLFW_OPENGL_API) - { - if (ctxconfig->forward) - flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; - - if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) - mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; - else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) - mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; - } - - if (ctxconfig->debug) - flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; - - if (ctxconfig->robustness) - { - if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) - { - setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, - EGL_NO_RESET_NOTIFICATION_KHR); - } - else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) - { - setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, - EGL_LOSE_CONTEXT_ON_RESET_KHR); - } - - flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; - } - - if (ctxconfig->noerror) - { - if (_glfw.egl.KHR_create_context_no_error) - setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); - } - - if (ctxconfig->major != 1 || ctxconfig->minor != 0) - { - setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); - setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); - } - - if (mask) - setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); - - if (flags) - setAttrib(EGL_CONTEXT_FLAGS_KHR, flags); - } - else - { - if (ctxconfig->client == GLFW_OPENGL_ES_API) - setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); - } - - if (_glfw.egl.KHR_context_flush_control) - { - if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) - { - setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, - EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); - } - else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) - { - setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, - EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); - } - } - - setAttrib(EGL_NONE, EGL_NONE); - - window->context.egl.handle = eglCreateContext(_glfw.egl.display, - config, share, attribs); - - if (window->context.egl.handle == EGL_NO_CONTEXT) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "EGL: Failed to create context: %s", - getEGLErrorString(eglGetError())); - return GLFW_FALSE; - } - - // Set up attributes for surface creation - index = 0; - - if (fbconfig->sRGB) - { - if (_glfw.egl.KHR_gl_colorspace) - setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); - } - - if (!fbconfig->doublebuffer) - setAttrib(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); - - if (_glfw.egl.EXT_present_opaque) - setAttrib(EGL_PRESENT_OPAQUE_EXT, !fbconfig->transparent); - - setAttrib(EGL_NONE, EGL_NONE); - - window->context.egl.surface = - eglCreateWindowSurface(_glfw.egl.display, - config, - _GLFW_EGL_NATIVE_WINDOW, - attribs); - if (window->context.egl.surface == EGL_NO_SURFACE) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "EGL: Failed to create window surface: %s", - getEGLErrorString(eglGetError())); - return GLFW_FALSE; - } - - window->context.egl.config = config; - - // Load the appropriate client library - if (!_glfw.egl.KHR_get_all_proc_addresses) - { - int i; - const char** sonames; - const char* es1sonames[] = - { -#if defined(_GLFW_GLESV1_LIBRARY) - _GLFW_GLESV1_LIBRARY, -#elif defined(_GLFW_WIN32) - "GLESv1_CM.dll", - "libGLES_CM.dll", -#elif defined(_GLFW_COCOA) - "libGLESv1_CM.dylib", -#elif defined(__OpenBSD__) || defined(__NetBSD__) - "libGLESv1_CM.so", -#else - "libGLESv1_CM.so.1", - "libGLES_CM.so.1", -#endif - NULL - }; - const char* es2sonames[] = - { -#if defined(_GLFW_GLESV2_LIBRARY) - _GLFW_GLESV2_LIBRARY, -#elif defined(_GLFW_WIN32) - "GLESv2.dll", - "libGLESv2.dll", -#elif defined(_GLFW_COCOA) - "libGLESv2.dylib", -#elif defined(__CYGWIN__) - "libGLESv2-2.so", -#elif defined(__OpenBSD__) || defined(__NetBSD__) - "libGLESv2.so", -#else - "libGLESv2.so.2", -#endif - NULL - }; - const char* glsonames[] = - { -#if defined(_GLFW_OPENGL_LIBRARY) - _GLFW_OPENGL_LIBRARY, -#elif defined(_GLFW_WIN32) -#elif defined(_GLFW_COCOA) -#elif defined(__OpenBSD__) || defined(__NetBSD__) - "libGL.so", -#else - "libGL.so.1", -#endif - NULL - }; - - if (ctxconfig->client == GLFW_OPENGL_ES_API) - { - if (ctxconfig->major == 1) - sonames = es1sonames; - else - sonames = es2sonames; - } - else - sonames = glsonames; - - for (i = 0; sonames[i]; i++) - { - // HACK: Match presence of lib prefix to increase chance of finding - // a matching pair in the jungle that is Win32 EGL/GLES - if (_glfw.egl.prefix != (strncmp(sonames[i], "lib", 3) == 0)) - continue; - - window->context.egl.client = _glfw_dlopen(sonames[i]); - if (window->context.egl.client) - break; - } - - if (!window->context.egl.client) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "EGL: Failed to load client library"); - return GLFW_FALSE; - } - } - - window->context.makeCurrent = makeContextCurrentEGL; - window->context.swapBuffers = swapBuffersEGL; - window->context.swapInterval = swapIntervalEGL; - window->context.extensionSupported = extensionSupportedEGL; - window->context.getProcAddress = getProcAddressEGL; - window->context.destroy = destroyContextEGL; - - return GLFW_TRUE; -} - -#undef setAttrib - -// Returns the Visual and depth of the chosen EGLConfig -// -#if defined(_GLFW_X11) -GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig, - Visual** visual, int* depth) -{ - XVisualInfo* result; - XVisualInfo desired; - EGLConfig native; - EGLint visualID = 0, count = 0; - const long vimask = VisualScreenMask | VisualIDMask; - - if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "EGL: Failed to find a suitable EGLConfig"); - return GLFW_FALSE; - } - - eglGetConfigAttrib(_glfw.egl.display, native, - EGL_NATIVE_VISUAL_ID, &visualID); - - desired.screen = _glfw.x11.screen; - desired.visualid = visualID; - - result = XGetVisualInfo(_glfw.x11.display, vimask, &desired, &count); - if (!result) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "EGL: Failed to retrieve Visual for EGLConfig"); - return GLFW_FALSE; - } - - *visual = result->visual; - *depth = result->depth; - - XFree(result); - return GLFW_TRUE; -} -#endif // _GLFW_X11 - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI EGLDisplay glfwGetEGLDisplay(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_DISPLAY); - return _glfw.egl.display; -} - -GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT); - - if (window->context.source != GLFW_EGL_CONTEXT_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return EGL_NO_CONTEXT; - } - - return window->context.egl.handle; -} - -GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE); - - if (window->context.source != GLFW_EGL_CONTEXT_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return EGL_NO_SURFACE; - } - - return window->context.egl.surface; -} - diff --git a/libs/zglfw/libs/glfw/src/egl_context.h b/libs/zglfw/libs/glfw/src/egl_context.h deleted file mode 100644 index 47493a6fa..000000000 --- a/libs/zglfw/libs/glfw/src/egl_context.h +++ /dev/null @@ -1,217 +0,0 @@ -//======================================================================== -// GLFW 3.3 EGL - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#if defined(_GLFW_USE_EGLPLATFORM_H) - #include -#elif defined(_GLFW_WIN32) - #define EGLAPIENTRY __stdcall -typedef HDC EGLNativeDisplayType; -typedef HWND EGLNativeWindowType; -#elif defined(_GLFW_COCOA) - #define EGLAPIENTRY -typedef void* EGLNativeDisplayType; -typedef id EGLNativeWindowType; -#elif defined(_GLFW_X11) - #define EGLAPIENTRY -typedef Display* EGLNativeDisplayType; -typedef Window EGLNativeWindowType; -#elif defined(_GLFW_WAYLAND) - #define EGLAPIENTRY -typedef struct wl_display* EGLNativeDisplayType; -typedef struct wl_egl_window* EGLNativeWindowType; -#else - #error "No supported EGL platform selected" -#endif - -#define EGL_SUCCESS 0x3000 -#define EGL_NOT_INITIALIZED 0x3001 -#define EGL_BAD_ACCESS 0x3002 -#define EGL_BAD_ALLOC 0x3003 -#define EGL_BAD_ATTRIBUTE 0x3004 -#define EGL_BAD_CONFIG 0x3005 -#define EGL_BAD_CONTEXT 0x3006 -#define EGL_BAD_CURRENT_SURFACE 0x3007 -#define EGL_BAD_DISPLAY 0x3008 -#define EGL_BAD_MATCH 0x3009 -#define EGL_BAD_NATIVE_PIXMAP 0x300a -#define EGL_BAD_NATIVE_WINDOW 0x300b -#define EGL_BAD_PARAMETER 0x300c -#define EGL_BAD_SURFACE 0x300d -#define EGL_CONTEXT_LOST 0x300e -#define EGL_COLOR_BUFFER_TYPE 0x303f -#define EGL_RGB_BUFFER 0x308e -#define EGL_SURFACE_TYPE 0x3033 -#define EGL_WINDOW_BIT 0x0004 -#define EGL_RENDERABLE_TYPE 0x3040 -#define EGL_OPENGL_ES_BIT 0x0001 -#define EGL_OPENGL_ES2_BIT 0x0004 -#define EGL_OPENGL_BIT 0x0008 -#define EGL_ALPHA_SIZE 0x3021 -#define EGL_BLUE_SIZE 0x3022 -#define EGL_GREEN_SIZE 0x3023 -#define EGL_RED_SIZE 0x3024 -#define EGL_DEPTH_SIZE 0x3025 -#define EGL_STENCIL_SIZE 0x3026 -#define EGL_SAMPLES 0x3031 -#define EGL_OPENGL_ES_API 0x30a0 -#define EGL_OPENGL_API 0x30a2 -#define EGL_NONE 0x3038 -#define EGL_RENDER_BUFFER 0x3086 -#define EGL_SINGLE_BUFFER 0x3085 -#define EGL_EXTENSIONS 0x3055 -#define EGL_CONTEXT_CLIENT_VERSION 0x3098 -#define EGL_NATIVE_VISUAL_ID 0x302e -#define EGL_NO_SURFACE ((EGLSurface) 0) -#define EGL_NO_DISPLAY ((EGLDisplay) 0) -#define EGL_NO_CONTEXT ((EGLContext) 0) -#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0) - -#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 -#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 -#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 -#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 -#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd -#define EGL_NO_RESET_NOTIFICATION_KHR 0x31be -#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf -#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 -#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 -#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb -#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd -#define EGL_CONTEXT_FLAGS_KHR 0x30fc -#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 -#define EGL_GL_COLORSPACE_KHR 0x309d -#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 -#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 -#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 -#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 -#define EGL_PRESENT_OPAQUE_EXT 0x31df - -typedef int EGLint; -typedef unsigned int EGLBoolean; -typedef unsigned int EGLenum; -typedef void* EGLConfig; -typedef void* EGLContext; -typedef void* EGLDisplay; -typedef void* EGLSurface; - -// EGL function pointer typedefs -typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); -typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); -typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); -typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); -typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); -typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); -typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); -#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib -#define eglGetConfigs _glfw.egl.GetConfigs -#define eglGetDisplay _glfw.egl.GetDisplay -#define eglGetError _glfw.egl.GetError -#define eglInitialize _glfw.egl.Initialize -#define eglTerminate _glfw.egl.Terminate -#define eglBindAPI _glfw.egl.BindAPI -#define eglCreateContext _glfw.egl.CreateContext -#define eglDestroySurface _glfw.egl.DestroySurface -#define eglDestroyContext _glfw.egl.DestroyContext -#define eglCreateWindowSurface _glfw.egl.CreateWindowSurface -#define eglMakeCurrent _glfw.egl.MakeCurrent -#define eglSwapBuffers _glfw.egl.SwapBuffers -#define eglSwapInterval _glfw.egl.SwapInterval -#define eglQueryString _glfw.egl.QueryString -#define eglGetProcAddress _glfw.egl.GetProcAddress - -#define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl -#define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl - - -// EGL-specific per-context data -// -typedef struct _GLFWcontextEGL -{ - EGLConfig config; - EGLContext handle; - EGLSurface surface; - - void* client; -} _GLFWcontextEGL; - -// EGL-specific global data -// -typedef struct _GLFWlibraryEGL -{ - EGLDisplay display; - EGLint major, minor; - GLFWbool prefix; - - GLFWbool KHR_create_context; - GLFWbool KHR_create_context_no_error; - GLFWbool KHR_gl_colorspace; - GLFWbool KHR_get_all_proc_addresses; - GLFWbool KHR_context_flush_control; - GLFWbool EXT_present_opaque; - - void* handle; - - PFN_eglGetConfigAttrib GetConfigAttrib; - PFN_eglGetConfigs GetConfigs; - PFN_eglGetDisplay GetDisplay; - PFN_eglGetError GetError; - PFN_eglInitialize Initialize; - PFN_eglTerminate Terminate; - PFN_eglBindAPI BindAPI; - PFN_eglCreateContext CreateContext; - PFN_eglDestroySurface DestroySurface; - PFN_eglDestroyContext DestroyContext; - PFN_eglCreateWindowSurface CreateWindowSurface; - PFN_eglMakeCurrent MakeCurrent; - PFN_eglSwapBuffers SwapBuffers; - PFN_eglSwapInterval SwapInterval; - PFN_eglQueryString QueryString; - PFN_eglGetProcAddress GetProcAddress; -} _GLFWlibraryEGL; - - -GLFWbool _glfwInitEGL(void); -void _glfwTerminateEGL(void); -GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); -#if defined(_GLFW_X11) -GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig, - Visual** visual, int* depth); -#endif /*_GLFW_X11*/ - diff --git a/libs/zglfw/libs/glfw/src/glx_context.c b/libs/zglfw/libs/glfw/src/glx_context.c deleted file mode 100644 index 1b1b3f90d..000000000 --- a/libs/zglfw/libs/glfw/src/glx_context.c +++ /dev/null @@ -1,701 +0,0 @@ -//======================================================================== -// GLFW 3.3 GLX - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include -#include -#include - -#ifndef GLXBadProfileARB - #define GLXBadProfileARB 13 -#endif - - -// Returns the specified attribute of the specified GLXFBConfig -// -static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib) -{ - int value; - glXGetFBConfigAttrib(_glfw.x11.display, fbconfig, attrib, &value); - return value; -} - -// Return the GLXFBConfig most closely matching the specified hints -// -static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, - GLXFBConfig* result) -{ - GLXFBConfig* nativeConfigs; - _GLFWfbconfig* usableConfigs; - const _GLFWfbconfig* closest; - int i, nativeCount, usableCount; - const char* vendor; - GLFWbool trustWindowBit = GLFW_TRUE; - - // HACK: This is a (hopefully temporary) workaround for Chromium - // (VirtualBox GL) not setting the window bit on any GLXFBConfigs - vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR); - if (vendor && strcmp(vendor, "Chromium") == 0) - trustWindowBit = GLFW_FALSE; - - nativeConfigs = - glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount); - if (!nativeConfigs || !nativeCount) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned"); - return GLFW_FALSE; - } - - usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); - usableCount = 0; - - for (i = 0; i < nativeCount; i++) - { - const GLXFBConfig n = nativeConfigs[i]; - _GLFWfbconfig* u = usableConfigs + usableCount; - - // Only consider RGBA GLXFBConfigs - if (!(getGLXFBConfigAttrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) - continue; - - // Only consider window GLXFBConfigs - if (!(getGLXFBConfigAttrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) - { - if (trustWindowBit) - continue; - } - - if (getGLXFBConfigAttrib(n, GLX_DOUBLEBUFFER) != desired->doublebuffer) - continue; - - if (desired->transparent) - { - XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n); - if (vi) - { - u->transparent = _glfwIsVisualTransparentX11(vi->visual); - XFree(vi); - } - } - - u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); - u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE); - u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE); - - u->alphaBits = getGLXFBConfigAttrib(n, GLX_ALPHA_SIZE); - u->depthBits = getGLXFBConfigAttrib(n, GLX_DEPTH_SIZE); - u->stencilBits = getGLXFBConfigAttrib(n, GLX_STENCIL_SIZE); - - u->accumRedBits = getGLXFBConfigAttrib(n, GLX_ACCUM_RED_SIZE); - u->accumGreenBits = getGLXFBConfigAttrib(n, GLX_ACCUM_GREEN_SIZE); - u->accumBlueBits = getGLXFBConfigAttrib(n, GLX_ACCUM_BLUE_SIZE); - u->accumAlphaBits = getGLXFBConfigAttrib(n, GLX_ACCUM_ALPHA_SIZE); - - u->auxBuffers = getGLXFBConfigAttrib(n, GLX_AUX_BUFFERS); - - if (getGLXFBConfigAttrib(n, GLX_STEREO)) - u->stereo = GLFW_TRUE; - - if (_glfw.glx.ARB_multisample) - u->samples = getGLXFBConfigAttrib(n, GLX_SAMPLES); - - if (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB) - u->sRGB = getGLXFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB); - - u->handle = (uintptr_t) n; - usableCount++; - } - - closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); - if (closest) - *result = (GLXFBConfig) closest->handle; - - XFree(nativeConfigs); - free(usableConfigs); - - return closest != NULL; -} - -// Create the OpenGL context using legacy API -// -static GLXContext createLegacyContextGLX(_GLFWwindow* window, - GLXFBConfig fbconfig, - GLXContext share) -{ - return glXCreateNewContext(_glfw.x11.display, - fbconfig, - GLX_RGBA_TYPE, - share, - True); -} - -static void makeContextCurrentGLX(_GLFWwindow* window) -{ - if (window) - { - if (!glXMakeCurrent(_glfw.x11.display, - window->context.glx.window, - window->context.glx.handle)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "GLX: Failed to make context current"); - return; - } - } - else - { - if (!glXMakeCurrent(_glfw.x11.display, None, NULL)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "GLX: Failed to clear current context"); - return; - } - } - - _glfwPlatformSetTls(&_glfw.contextSlot, window); -} - -static void swapBuffersGLX(_GLFWwindow* window) -{ - glXSwapBuffers(_glfw.x11.display, window->context.glx.window); -} - -static void swapIntervalGLX(int interval) -{ - _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); - - if (_glfw.glx.EXT_swap_control) - { - _glfw.glx.SwapIntervalEXT(_glfw.x11.display, - window->context.glx.window, - interval); - } - else if (_glfw.glx.MESA_swap_control) - _glfw.glx.SwapIntervalMESA(interval); - else if (_glfw.glx.SGI_swap_control) - { - if (interval > 0) - _glfw.glx.SwapIntervalSGI(interval); - } -} - -static int extensionSupportedGLX(const char* extension) -{ - const char* extensions = - glXQueryExtensionsString(_glfw.x11.display, _glfw.x11.screen); - if (extensions) - { - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - - return GLFW_FALSE; -} - -static GLFWglproc getProcAddressGLX(const char* procname) -{ - if (_glfw.glx.GetProcAddress) - return _glfw.glx.GetProcAddress((const GLubyte*) procname); - else if (_glfw.glx.GetProcAddressARB) - return _glfw.glx.GetProcAddressARB((const GLubyte*) procname); - else - return _glfw_dlsym(_glfw.glx.handle, procname); -} - -static void destroyContextGLX(_GLFWwindow* window) -{ - if (window->context.glx.window) - { - glXDestroyWindow(_glfw.x11.display, window->context.glx.window); - window->context.glx.window = None; - } - - if (window->context.glx.handle) - { - glXDestroyContext(_glfw.x11.display, window->context.glx.handle); - window->context.glx.handle = NULL; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialize GLX -// -GLFWbool _glfwInitGLX(void) -{ - int i; - const char* sonames[] = - { -#if defined(_GLFW_GLX_LIBRARY) - _GLFW_GLX_LIBRARY, -#elif defined(__CYGWIN__) - "libGL-1.so", -#elif defined(__OpenBSD__) || defined(__NetBSD__) - "libGL.so", -#else - "libGL.so.1", - "libGL.so", -#endif - NULL - }; - - if (_glfw.glx.handle) - return GLFW_TRUE; - - for (i = 0; sonames[i]; i++) - { - _glfw.glx.handle = _glfw_dlopen(sonames[i]); - if (_glfw.glx.handle) - break; - } - - if (!_glfw.glx.handle) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: Failed to load GLX"); - return GLFW_FALSE; - } - - _glfw.glx.GetFBConfigs = - _glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigs"); - _glfw.glx.GetFBConfigAttrib = - _glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib"); - _glfw.glx.GetClientString = - _glfw_dlsym(_glfw.glx.handle, "glXGetClientString"); - _glfw.glx.QueryExtension = - _glfw_dlsym(_glfw.glx.handle, "glXQueryExtension"); - _glfw.glx.QueryVersion = - _glfw_dlsym(_glfw.glx.handle, "glXQueryVersion"); - _glfw.glx.DestroyContext = - _glfw_dlsym(_glfw.glx.handle, "glXDestroyContext"); - _glfw.glx.MakeCurrent = - _glfw_dlsym(_glfw.glx.handle, "glXMakeCurrent"); - _glfw.glx.SwapBuffers = - _glfw_dlsym(_glfw.glx.handle, "glXSwapBuffers"); - _glfw.glx.QueryExtensionsString = - _glfw_dlsym(_glfw.glx.handle, "glXQueryExtensionsString"); - _glfw.glx.CreateNewContext = - _glfw_dlsym(_glfw.glx.handle, "glXCreateNewContext"); - _glfw.glx.CreateWindow = - _glfw_dlsym(_glfw.glx.handle, "glXCreateWindow"); - _glfw.glx.DestroyWindow = - _glfw_dlsym(_glfw.glx.handle, "glXDestroyWindow"); - _glfw.glx.GetVisualFromFBConfig = - _glfw_dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig"); - - if (!_glfw.glx.GetFBConfigs || - !_glfw.glx.GetFBConfigAttrib || - !_glfw.glx.GetClientString || - !_glfw.glx.QueryExtension || - !_glfw.glx.QueryVersion || - !_glfw.glx.DestroyContext || - !_glfw.glx.MakeCurrent || - !_glfw.glx.SwapBuffers || - !_glfw.glx.QueryExtensionsString || - !_glfw.glx.CreateNewContext || - !_glfw.glx.CreateWindow || - !_glfw.glx.DestroyWindow || - !_glfw.glx.GetVisualFromFBConfig) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "GLX: Failed to load required entry points"); - return GLFW_FALSE; - } - - // NOTE: Unlike GLX 1.3 entry points these are not required to be present - _glfw.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) - _glfw_dlsym(_glfw.glx.handle, "glXGetProcAddress"); - _glfw.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) - _glfw_dlsym(_glfw.glx.handle, "glXGetProcAddressARB"); - - if (!glXQueryExtension(_glfw.x11.display, - &_glfw.glx.errorBase, - &_glfw.glx.eventBase)) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: GLX extension not found"); - return GLFW_FALSE; - } - - if (!glXQueryVersion(_glfw.x11.display, &_glfw.glx.major, &_glfw.glx.minor)) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "GLX: Failed to query GLX version"); - return GLFW_FALSE; - } - - if (_glfw.glx.major == 1 && _glfw.glx.minor < 3) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "GLX: GLX version 1.3 is required"); - return GLFW_FALSE; - } - - if (extensionSupportedGLX("GLX_EXT_swap_control")) - { - _glfw.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) - getProcAddressGLX("glXSwapIntervalEXT"); - - if (_glfw.glx.SwapIntervalEXT) - _glfw.glx.EXT_swap_control = GLFW_TRUE; - } - - if (extensionSupportedGLX("GLX_SGI_swap_control")) - { - _glfw.glx.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) - getProcAddressGLX("glXSwapIntervalSGI"); - - if (_glfw.glx.SwapIntervalSGI) - _glfw.glx.SGI_swap_control = GLFW_TRUE; - } - - if (extensionSupportedGLX("GLX_MESA_swap_control")) - { - _glfw.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) - getProcAddressGLX("glXSwapIntervalMESA"); - - if (_glfw.glx.SwapIntervalMESA) - _glfw.glx.MESA_swap_control = GLFW_TRUE; - } - - if (extensionSupportedGLX("GLX_ARB_multisample")) - _glfw.glx.ARB_multisample = GLFW_TRUE; - - if (extensionSupportedGLX("GLX_ARB_framebuffer_sRGB")) - _glfw.glx.ARB_framebuffer_sRGB = GLFW_TRUE; - - if (extensionSupportedGLX("GLX_EXT_framebuffer_sRGB")) - _glfw.glx.EXT_framebuffer_sRGB = GLFW_TRUE; - - if (extensionSupportedGLX("GLX_ARB_create_context")) - { - _glfw.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) - getProcAddressGLX("glXCreateContextAttribsARB"); - - if (_glfw.glx.CreateContextAttribsARB) - _glfw.glx.ARB_create_context = GLFW_TRUE; - } - - if (extensionSupportedGLX("GLX_ARB_create_context_robustness")) - _glfw.glx.ARB_create_context_robustness = GLFW_TRUE; - - if (extensionSupportedGLX("GLX_ARB_create_context_profile")) - _glfw.glx.ARB_create_context_profile = GLFW_TRUE; - - if (extensionSupportedGLX("GLX_EXT_create_context_es2_profile")) - _glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE; - - if (extensionSupportedGLX("GLX_ARB_create_context_no_error")) - _glfw.glx.ARB_create_context_no_error = GLFW_TRUE; - - if (extensionSupportedGLX("GLX_ARB_context_flush_control")) - _glfw.glx.ARB_context_flush_control = GLFW_TRUE; - - return GLFW_TRUE; -} - -// Terminate GLX -// -void _glfwTerminateGLX(void) -{ - // NOTE: This function must not call any X11 functions, as it is called - // after XCloseDisplay (see _glfwPlatformTerminate for details) - - if (_glfw.glx.handle) - { - _glfw_dlclose(_glfw.glx.handle); - _glfw.glx.handle = NULL; - } -} - -#define setAttrib(a, v) \ -{ \ - assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ - attribs[index++] = a; \ - attribs[index++] = v; \ -} - -// Create the OpenGL or OpenGL ES context -// -GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - int attribs[40]; - GLXFBConfig native = NULL; - GLXContext share = NULL; - - if (ctxconfig->share) - share = ctxconfig->share->context.glx.handle; - - if (!chooseGLXFBConfig(fbconfig, &native)) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "GLX: Failed to find a suitable GLXFBConfig"); - return GLFW_FALSE; - } - - if (ctxconfig->client == GLFW_OPENGL_ES_API) - { - if (!_glfw.glx.ARB_create_context || - !_glfw.glx.ARB_create_context_profile || - !_glfw.glx.EXT_create_context_es2_profile) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "GLX: OpenGL ES requested but GLX_EXT_create_context_es2_profile is unavailable"); - return GLFW_FALSE; - } - } - - if (ctxconfig->forward) - { - if (!_glfw.glx.ARB_create_context) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "GLX: Forward compatibility requested but GLX_ARB_create_context_profile is unavailable"); - return GLFW_FALSE; - } - } - - if (ctxconfig->profile) - { - if (!_glfw.glx.ARB_create_context || - !_glfw.glx.ARB_create_context_profile) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "GLX: An OpenGL profile requested but GLX_ARB_create_context_profile is unavailable"); - return GLFW_FALSE; - } - } - - _glfwGrabErrorHandlerX11(); - - if (_glfw.glx.ARB_create_context) - { - int index = 0, mask = 0, flags = 0; - - if (ctxconfig->client == GLFW_OPENGL_API) - { - if (ctxconfig->forward) - flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; - - if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) - mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; - else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) - mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; - } - else - mask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT; - - if (ctxconfig->debug) - flags |= GLX_CONTEXT_DEBUG_BIT_ARB; - - if (ctxconfig->robustness) - { - if (_glfw.glx.ARB_create_context_robustness) - { - if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) - { - setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - GLX_NO_RESET_NOTIFICATION_ARB); - } - else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) - { - setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - GLX_LOSE_CONTEXT_ON_RESET_ARB); - } - - flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; - } - } - - if (ctxconfig->release) - { - if (_glfw.glx.ARB_context_flush_control) - { - if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) - { - setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, - GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); - } - else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) - { - setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, - GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); - } - } - } - - if (ctxconfig->noerror) - { - if (_glfw.glx.ARB_create_context_no_error) - setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); - } - - // NOTE: Only request an explicitly versioned context when necessary, as - // explicitly requesting version 1.0 does not always return the - // highest version supported by the driver - if (ctxconfig->major != 1 || ctxconfig->minor != 0) - { - setAttrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); - setAttrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); - } - - if (mask) - setAttrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); - - if (flags) - setAttrib(GLX_CONTEXT_FLAGS_ARB, flags); - - setAttrib(None, None); - - window->context.glx.handle = - _glfw.glx.CreateContextAttribsARB(_glfw.x11.display, - native, - share, - True, - attribs); - - // HACK: This is a fallback for broken versions of the Mesa - // implementation of GLX_ARB_create_context_profile that fail - // default 1.0 context creation with a GLXBadProfileARB error in - // violation of the extension spec - if (!window->context.glx.handle) - { - if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB && - ctxconfig->client == GLFW_OPENGL_API && - ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE && - ctxconfig->forward == GLFW_FALSE) - { - window->context.glx.handle = - createLegacyContextGLX(window, native, share); - } - } - } - else - { - window->context.glx.handle = - createLegacyContextGLX(window, native, share); - } - - _glfwReleaseErrorHandlerX11(); - - if (!window->context.glx.handle) - { - _glfwInputErrorX11(GLFW_VERSION_UNAVAILABLE, "GLX: Failed to create context"); - return GLFW_FALSE; - } - - window->context.glx.window = - glXCreateWindow(_glfw.x11.display, native, window->x11.handle, NULL); - if (!window->context.glx.window) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to create window"); - return GLFW_FALSE; - } - - window->context.makeCurrent = makeContextCurrentGLX; - window->context.swapBuffers = swapBuffersGLX; - window->context.swapInterval = swapIntervalGLX; - window->context.extensionSupported = extensionSupportedGLX; - window->context.getProcAddress = getProcAddressGLX; - window->context.destroy = destroyContextGLX; - - return GLFW_TRUE; -} - -#undef setAttrib - -// Returns the Visual and depth of the chosen GLXFBConfig -// -GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig, - Visual** visual, int* depth) -{ - GLXFBConfig native; - XVisualInfo* result; - - if (!chooseGLXFBConfig(fbconfig, &native)) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "GLX: Failed to find a suitable GLXFBConfig"); - return GLFW_FALSE; - } - - result = glXGetVisualFromFBConfig(_glfw.x11.display, native); - if (!result) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "GLX: Failed to retrieve Visual for GLXFBConfig"); - return GLFW_FALSE; - } - - *visual = result->visual; - *depth = result->depth; - - XFree(result); - return GLFW_TRUE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (window->context.source != GLFW_NATIVE_CONTEXT_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return NULL; - } - - return window->context.glx.handle; -} - -GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(None); - - if (window->context.source != GLFW_NATIVE_CONTEXT_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return None; - } - - return window->context.glx.window; -} - diff --git a/libs/zglfw/libs/glfw/src/glx_context.h b/libs/zglfw/libs/glfw/src/glx_context.h deleted file mode 100644 index df0233eba..000000000 --- a/libs/zglfw/libs/glfw/src/glx_context.h +++ /dev/null @@ -1,179 +0,0 @@ -//======================================================================== -// GLFW 3.3 GLX - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#define GLX_VENDOR 1 -#define GLX_RGBA_BIT 0x00000001 -#define GLX_WINDOW_BIT 0x00000001 -#define GLX_DRAWABLE_TYPE 0x8010 -#define GLX_RENDER_TYPE 0x8011 -#define GLX_RGBA_TYPE 0x8014 -#define GLX_DOUBLEBUFFER 5 -#define GLX_STEREO 6 -#define GLX_AUX_BUFFERS 7 -#define GLX_RED_SIZE 8 -#define GLX_GREEN_SIZE 9 -#define GLX_BLUE_SIZE 10 -#define GLX_ALPHA_SIZE 11 -#define GLX_DEPTH_SIZE 12 -#define GLX_STENCIL_SIZE 13 -#define GLX_ACCUM_RED_SIZE 14 -#define GLX_ACCUM_GREEN_SIZE 15 -#define GLX_ACCUM_BLUE_SIZE 16 -#define GLX_ACCUM_ALPHA_SIZE 17 -#define GLX_SAMPLES 0x186a1 -#define GLX_VISUAL_ID 0x800b - -#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2 -#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 -#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 -#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 -#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 -#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define GLX_CONTEXT_FLAGS_ARB 0x2094 -#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 -#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 -#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 -#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 -#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 -#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 -#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 -#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 -#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 - -typedef XID GLXWindow; -typedef XID GLXDrawable; -typedef struct __GLXFBConfig* GLXFBConfig; -typedef struct __GLXcontext* GLXContext; -typedef void (*__GLXextproc)(void); - -typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); -typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); -typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); -typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); -typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); -typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); -typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); -typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); -typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); -typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); -typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); -typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); -typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); -typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); -typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); - -typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); -typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); -typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); - -// libGL.so function pointer typedefs -#define glXGetFBConfigs _glfw.glx.GetFBConfigs -#define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib -#define glXGetClientString _glfw.glx.GetClientString -#define glXQueryExtension _glfw.glx.QueryExtension -#define glXQueryVersion _glfw.glx.QueryVersion -#define glXDestroyContext _glfw.glx.DestroyContext -#define glXMakeCurrent _glfw.glx.MakeCurrent -#define glXSwapBuffers _glfw.glx.SwapBuffers -#define glXQueryExtensionsString _glfw.glx.QueryExtensionsString -#define glXCreateNewContext _glfw.glx.CreateNewContext -#define glXGetVisualFromFBConfig _glfw.glx.GetVisualFromFBConfig -#define glXCreateWindow _glfw.glx.CreateWindow -#define glXDestroyWindow _glfw.glx.DestroyWindow - -#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX glx -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx - - -// GLX-specific per-context data -// -typedef struct _GLFWcontextGLX -{ - GLXContext handle; - GLXWindow window; -} _GLFWcontextGLX; - -// GLX-specific global data -// -typedef struct _GLFWlibraryGLX -{ - int major, minor; - int eventBase; - int errorBase; - - // dlopen handle for libGL.so.1 - void* handle; - - // GLX 1.3 functions - PFNGLXGETFBCONFIGSPROC GetFBConfigs; - PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; - PFNGLXGETCLIENTSTRINGPROC GetClientString; - PFNGLXQUERYEXTENSIONPROC QueryExtension; - PFNGLXQUERYVERSIONPROC QueryVersion; - PFNGLXDESTROYCONTEXTPROC DestroyContext; - PFNGLXMAKECURRENTPROC MakeCurrent; - PFNGLXSWAPBUFFERSPROC SwapBuffers; - PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; - PFNGLXCREATENEWCONTEXTPROC CreateNewContext; - PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; - PFNGLXCREATEWINDOWPROC CreateWindow; - PFNGLXDESTROYWINDOWPROC DestroyWindow; - - // GLX 1.4 and extension functions - PFNGLXGETPROCADDRESSPROC GetProcAddress; - PFNGLXGETPROCADDRESSPROC GetProcAddressARB; - PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI; - PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; - PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; - PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; - GLFWbool SGI_swap_control; - GLFWbool EXT_swap_control; - GLFWbool MESA_swap_control; - GLFWbool ARB_multisample; - GLFWbool ARB_framebuffer_sRGB; - GLFWbool EXT_framebuffer_sRGB; - GLFWbool ARB_create_context; - GLFWbool ARB_create_context_profile; - GLFWbool ARB_create_context_robustness; - GLFWbool EXT_create_context_es2_profile; - GLFWbool ARB_create_context_no_error; - GLFWbool ARB_context_flush_control; -} _GLFWlibraryGLX; - -GLFWbool _glfwInitGLX(void); -void _glfwTerminateGLX(void); -GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); -void _glfwDestroyContextGLX(_GLFWwindow* window); -GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig, - Visual** visual, int* depth); - diff --git a/libs/zglfw/libs/glfw/src/init.c b/libs/zglfw/libs/glfw/src/init.c deleted file mode 100644 index cfdd512d8..000000000 --- a/libs/zglfw/libs/glfw/src/init.c +++ /dev/null @@ -1,420 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2018 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include -#include - - -// NOTE: The global variables below comprise all mutable global data in GLFW -// Any other mutable global variable is a bug - -// This contains all mutable state shared between compilation units of GLFW -// -_GLFWlibrary _glfw = { GLFW_FALSE }; - -// These are outside of _glfw so they can be used before initialization and -// after termination without special handling when _glfw is cleared to zero -// -static _GLFWerror _glfwMainThreadError; -static GLFWerrorfun _glfwErrorCallback; -static _GLFWinitconfig _glfwInitHints = -{ - GLFW_TRUE, // hat buttons - { - GLFW_TRUE, // macOS menu bar - GLFW_TRUE // macOS bundle chdir - } -}; - -// Terminate the library -// -static void terminate(void) -{ - int i; - - memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks)); - - while (_glfw.windowListHead) - glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead); - - while (_glfw.cursorListHead) - glfwDestroyCursor((GLFWcursor*) _glfw.cursorListHead); - - for (i = 0; i < _glfw.monitorCount; i++) - { - _GLFWmonitor* monitor = _glfw.monitors[i]; - if (monitor->originalRamp.size) - _glfwPlatformSetGammaRamp(monitor, &monitor->originalRamp); - _glfwFreeMonitor(monitor); - } - - free(_glfw.monitors); - _glfw.monitors = NULL; - _glfw.monitorCount = 0; - - free(_glfw.mappings); - _glfw.mappings = NULL; - _glfw.mappingCount = 0; - - _glfwTerminateVulkan(); - _glfwPlatformTerminate(); - - _glfw.initialized = GLFW_FALSE; - - while (_glfw.errorListHead) - { - _GLFWerror* error = _glfw.errorListHead; - _glfw.errorListHead = error->next; - free(error); - } - - _glfwPlatformDestroyTls(&_glfw.contextSlot); - _glfwPlatformDestroyTls(&_glfw.errorSlot); - _glfwPlatformDestroyMutex(&_glfw.errorLock); - - memset(&_glfw, 0, sizeof(_glfw)); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Encode a Unicode code point to a UTF-8 stream -// Based on cutef8 by Jeff Bezanson (Public Domain) -// -size_t _glfwEncodeUTF8(char* s, uint32_t codepoint) -{ - size_t count = 0; - - if (codepoint < 0x80) - s[count++] = (char) codepoint; - else if (codepoint < 0x800) - { - s[count++] = (codepoint >> 6) | 0xc0; - s[count++] = (codepoint & 0x3f) | 0x80; - } - else if (codepoint < 0x10000) - { - s[count++] = (codepoint >> 12) | 0xe0; - s[count++] = ((codepoint >> 6) & 0x3f) | 0x80; - s[count++] = (codepoint & 0x3f) | 0x80; - } - else if (codepoint < 0x110000) - { - s[count++] = (codepoint >> 18) | 0xf0; - s[count++] = ((codepoint >> 12) & 0x3f) | 0x80; - s[count++] = ((codepoint >> 6) & 0x3f) | 0x80; - s[count++] = (codepoint & 0x3f) | 0x80; - } - - return count; -} - -// Splits and translates a text/uri-list into separate file paths -// NOTE: This function destroys the provided string -// -char** _glfwParseUriList(char* text, int* count) -{ - const char* prefix = "file://"; - char** paths = NULL; - char* line; - - *count = 0; - - while ((line = strtok(text, "\r\n"))) - { - char* path; - - text = NULL; - - if (line[0] == '#') - continue; - - if (strncmp(line, prefix, strlen(prefix)) == 0) - { - line += strlen(prefix); - // TODO: Validate hostname - while (*line != '/') - line++; - } - - (*count)++; - - path = calloc(strlen(line) + 1, 1); - paths = realloc(paths, *count * sizeof(char*)); - paths[*count - 1] = path; - - while (*line) - { - if (line[0] == '%' && line[1] && line[2]) - { - const char digits[3] = { line[1], line[2], '\0' }; - *path = (char) strtol(digits, NULL, 16); - line += 2; - } - else - *path = *line; - - path++; - line++; - } - } - - return paths; -} - -char* _glfw_strdup(const char* source) -{ - const size_t length = strlen(source); - char* result = calloc(length + 1, 1); - strcpy(result, source); - return result; -} - -int _glfw_min(int a, int b) -{ - return a < b ? a : b; -} - -int _glfw_max(int a, int b) -{ - return a > b ? a : b; -} - -float _glfw_fminf(float a, float b) -{ - if (a != a) - return b; - else if (b != b) - return a; - else if (a < b) - return a; - else - return b; -} - -float _glfw_fmaxf(float a, float b) -{ - if (a != a) - return b; - else if (b != b) - return a; - else if (a > b) - return a; - else - return b; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW event API ////// -////////////////////////////////////////////////////////////////////////// - -// Notifies shared code of an error -// -void _glfwInputError(int code, const char* format, ...) -{ - _GLFWerror* error; - char description[_GLFW_MESSAGE_SIZE]; - - if (format) - { - va_list vl; - - va_start(vl, format); - vsnprintf(description, sizeof(description), format, vl); - va_end(vl); - - description[sizeof(description) - 1] = '\0'; - } - else - { - if (code == GLFW_NOT_INITIALIZED) - strcpy(description, "The GLFW library is not initialized"); - else if (code == GLFW_NO_CURRENT_CONTEXT) - strcpy(description, "There is no current context"); - else if (code == GLFW_INVALID_ENUM) - strcpy(description, "Invalid argument for enum parameter"); - else if (code == GLFW_INVALID_VALUE) - strcpy(description, "Invalid value for parameter"); - else if (code == GLFW_OUT_OF_MEMORY) - strcpy(description, "Out of memory"); - else if (code == GLFW_API_UNAVAILABLE) - strcpy(description, "The requested API is unavailable"); - else if (code == GLFW_VERSION_UNAVAILABLE) - strcpy(description, "The requested API version is unavailable"); - else if (code == GLFW_PLATFORM_ERROR) - strcpy(description, "A platform-specific error occurred"); - else if (code == GLFW_FORMAT_UNAVAILABLE) - strcpy(description, "The requested format is unavailable"); - else if (code == GLFW_NO_WINDOW_CONTEXT) - strcpy(description, "The specified window has no context"); - else - strcpy(description, "ERROR: UNKNOWN GLFW ERROR"); - } - - if (_glfw.initialized) - { - error = _glfwPlatformGetTls(&_glfw.errorSlot); - if (!error) - { - error = calloc(1, sizeof(_GLFWerror)); - _glfwPlatformSetTls(&_glfw.errorSlot, error); - _glfwPlatformLockMutex(&_glfw.errorLock); - error->next = _glfw.errorListHead; - _glfw.errorListHead = error; - _glfwPlatformUnlockMutex(&_glfw.errorLock); - } - } - else - error = &_glfwMainThreadError; - - error->code = code; - strcpy(error->description, description); - - if (_glfwErrorCallback) - _glfwErrorCallback(code, description); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW public API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI int glfwInit(void) -{ - if (_glfw.initialized) - return GLFW_TRUE; - - memset(&_glfw, 0, sizeof(_glfw)); - _glfw.hints.init = _glfwInitHints; - - if (!_glfwPlatformInit()) - { - terminate(); - return GLFW_FALSE; - } - - if (!_glfwPlatformCreateMutex(&_glfw.errorLock) || - !_glfwPlatformCreateTls(&_glfw.errorSlot) || - !_glfwPlatformCreateTls(&_glfw.contextSlot)) - { - terminate(); - return GLFW_FALSE; - } - - _glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError); - - _glfwInitGamepadMappings(); - - _glfw.initialized = GLFW_TRUE; - _glfw.timer.offset = _glfwPlatformGetTimerValue(); - - glfwDefaultWindowHints(); - return GLFW_TRUE; -} - -GLFWAPI void glfwTerminate(void) -{ - if (!_glfw.initialized) - return; - - terminate(); -} - -GLFWAPI void glfwInitHint(int hint, int value) -{ - switch (hint) - { - case GLFW_JOYSTICK_HAT_BUTTONS: - _glfwInitHints.hatButtons = value; - return; - case GLFW_COCOA_CHDIR_RESOURCES: - _glfwInitHints.ns.chdir = value; - return; - case GLFW_COCOA_MENUBAR: - _glfwInitHints.ns.menubar = value; - return; - } - - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid init hint 0x%08X", hint); -} - -GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) -{ - if (major != NULL) - *major = GLFW_VERSION_MAJOR; - if (minor != NULL) - *minor = GLFW_VERSION_MINOR; - if (rev != NULL) - *rev = GLFW_VERSION_REVISION; -} - -GLFWAPI const char* glfwGetVersionString(void) -{ - return _glfwPlatformGetVersionString(); -} - -GLFWAPI int glfwGetError(const char** description) -{ - _GLFWerror* error; - int code = GLFW_NO_ERROR; - - if (description) - *description = NULL; - - if (_glfw.initialized) - error = _glfwPlatformGetTls(&_glfw.errorSlot); - else - error = &_glfwMainThreadError; - - if (error) - { - code = error->code; - error->code = GLFW_NO_ERROR; - if (description && code) - *description = error->description; - } - - return code; -} - -GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) -{ - _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); - return cbfun; -} - diff --git a/libs/zglfw/libs/glfw/src/input.c b/libs/zglfw/libs/glfw/src/input.c deleted file mode 100644 index 7ea1222c5..000000000 --- a/libs/zglfw/libs/glfw/src/input.c +++ /dev/null @@ -1,1380 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" -#include "mappings.h" - -#include -#include -#include -#include -#include - -// Internal key state used for sticky keys -#define _GLFW_STICK 3 - -// Internal constants for gamepad mapping source types -#define _GLFW_JOYSTICK_AXIS 1 -#define _GLFW_JOYSTICK_BUTTON 2 -#define _GLFW_JOYSTICK_HATBIT 3 - -// Finds a mapping based on joystick GUID -// -static _GLFWmapping* findMapping(const char* guid) -{ - int i; - - for (i = 0; i < _glfw.mappingCount; i++) - { - if (strcmp(_glfw.mappings[i].guid, guid) == 0) - return _glfw.mappings + i; - } - - return NULL; -} - -// Checks whether a gamepad mapping element is present in the hardware -// -static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e, - const _GLFWjoystick* js) -{ - if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount) - return GLFW_FALSE; - else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount) - return GLFW_FALSE; - else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount) - return GLFW_FALSE; - - return GLFW_TRUE; -} - -// Finds a mapping based on joystick GUID and verifies element indices -// -static _GLFWmapping* findValidMapping(const _GLFWjoystick* js) -{ - _GLFWmapping* mapping = findMapping(js->guid); - if (mapping) - { - int i; - - for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) - { - if (!isValidElementForJoystick(mapping->buttons + i, js)) - return NULL; - } - - for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) - { - if (!isValidElementForJoystick(mapping->axes + i, js)) - return NULL; - } - } - - return mapping; -} - -// Parses an SDL_GameControllerDB line and adds it to the mapping list -// -static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) -{ - const char* c = string; - size_t i, length; - struct - { - const char* name; - _GLFWmapelement* element; - } fields[] = - { - { "platform", NULL }, - { "a", mapping->buttons + GLFW_GAMEPAD_BUTTON_A }, - { "b", mapping->buttons + GLFW_GAMEPAD_BUTTON_B }, - { "x", mapping->buttons + GLFW_GAMEPAD_BUTTON_X }, - { "y", mapping->buttons + GLFW_GAMEPAD_BUTTON_Y }, - { "back", mapping->buttons + GLFW_GAMEPAD_BUTTON_BACK }, - { "start", mapping->buttons + GLFW_GAMEPAD_BUTTON_START }, - { "guide", mapping->buttons + GLFW_GAMEPAD_BUTTON_GUIDE }, - { "leftshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_BUMPER }, - { "rightshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER }, - { "leftstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_THUMB }, - { "rightstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB }, - { "dpup", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_UP }, - { "dpright", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT }, - { "dpdown", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_DOWN }, - { "dpleft", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_LEFT }, - { "lefttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER }, - { "righttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER }, - { "leftx", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_X }, - { "lefty", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_Y }, - { "rightx", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_X }, - { "righty", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_Y } - }; - - length = strcspn(c, ","); - if (length != 32 || c[length] != ',') - { - _glfwInputError(GLFW_INVALID_VALUE, NULL); - return GLFW_FALSE; - } - - memcpy(mapping->guid, c, length); - c += length + 1; - - length = strcspn(c, ","); - if (length >= sizeof(mapping->name) || c[length] != ',') - { - _glfwInputError(GLFW_INVALID_VALUE, NULL); - return GLFW_FALSE; - } - - memcpy(mapping->name, c, length); - c += length + 1; - - while (*c) - { - // TODO: Implement output modifiers - if (*c == '+' || *c == '-') - return GLFW_FALSE; - - for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) - { - length = strlen(fields[i].name); - if (strncmp(c, fields[i].name, length) != 0 || c[length] != ':') - continue; - - c += length + 1; - - if (fields[i].element) - { - _GLFWmapelement* e = fields[i].element; - int8_t minimum = -1; - int8_t maximum = 1; - - if (*c == '+') - { - minimum = 0; - c += 1; - } - else if (*c == '-') - { - maximum = 0; - c += 1; - } - - if (*c == 'a') - e->type = _GLFW_JOYSTICK_AXIS; - else if (*c == 'b') - e->type = _GLFW_JOYSTICK_BUTTON; - else if (*c == 'h') - e->type = _GLFW_JOYSTICK_HATBIT; - else - break; - - if (e->type == _GLFW_JOYSTICK_HATBIT) - { - const unsigned long hat = strtoul(c + 1, (char**) &c, 10); - const unsigned long bit = strtoul(c + 1, (char**) &c, 10); - e->index = (uint8_t) ((hat << 4) | bit); - } - else - e->index = (uint8_t) strtoul(c + 1, (char**) &c, 10); - - if (e->type == _GLFW_JOYSTICK_AXIS) - { - e->axisScale = 2 / (maximum - minimum); - e->axisOffset = -(maximum + minimum); - - if (*c == '~') - { - e->axisScale = -e->axisScale; - e->axisOffset = -e->axisOffset; - } - } - } - else - { - length = strlen(_GLFW_PLATFORM_MAPPING_NAME); - if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0) - return GLFW_FALSE; - } - - break; - } - - c += strcspn(c, ","); - c += strspn(c, ","); - } - - for (i = 0; i < 32; i++) - { - if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F') - mapping->guid[i] += 'a' - 'A'; - } - - _glfwPlatformUpdateGamepadGUID(mapping->guid); - return GLFW_TRUE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW event API ////// -////////////////////////////////////////////////////////////////////////// - -// Notifies shared code of a physical key event -// -void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods) -{ - if (key >= 0 && key <= GLFW_KEY_LAST) - { - GLFWbool repeated = GLFW_FALSE; - - if (action == GLFW_RELEASE && window->keys[key] == GLFW_RELEASE) - return; - - if (action == GLFW_PRESS && window->keys[key] == GLFW_PRESS) - repeated = GLFW_TRUE; - - if (action == GLFW_RELEASE && window->stickyKeys) - window->keys[key] = _GLFW_STICK; - else - window->keys[key] = (char) action; - - if (repeated) - action = GLFW_REPEAT; - } - - if (!window->lockKeyMods) - mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); - - if (window->callbacks.key) - window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods); -} - -// Notifies shared code of a Unicode codepoint input event -// The 'plain' parameter determines whether to emit a regular character event -// -void _glfwInputChar(_GLFWwindow* window, uint32_t codepoint, int mods, GLFWbool plain) -{ - if (codepoint < 32 || (codepoint > 126 && codepoint < 160)) - return; - - if (!window->lockKeyMods) - mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); - - if (window->callbacks.charmods) - window->callbacks.charmods((GLFWwindow*) window, codepoint, mods); - - if (plain) - { - if (window->callbacks.character) - window->callbacks.character((GLFWwindow*) window, codepoint); - } -} - -// Notifies shared code of a scroll event -// -void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset) -{ - if (window->callbacks.scroll) - window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset); -} - -// Notifies shared code of a mouse button click event -// -void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) -{ - if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST) - return; - - if (!window->lockKeyMods) - mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); - - if (action == GLFW_RELEASE && window->stickyMouseButtons) - window->mouseButtons[button] = _GLFW_STICK; - else - window->mouseButtons[button] = (char) action; - - if (window->callbacks.mouseButton) - window->callbacks.mouseButton((GLFWwindow*) window, button, action, mods); -} - -// Notifies shared code of a cursor motion event -// The position is specified in content area relative screen coordinates -// -void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos) -{ - if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos) - return; - - window->virtualCursorPosX = xpos; - window->virtualCursorPosY = ypos; - - if (window->callbacks.cursorPos) - window->callbacks.cursorPos((GLFWwindow*) window, xpos, ypos); -} - -// Notifies shared code of a cursor enter/leave event -// -void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered) -{ - if (window->callbacks.cursorEnter) - window->callbacks.cursorEnter((GLFWwindow*) window, entered); -} - -// Notifies shared code of files or directories dropped on a window -// -void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) -{ - if (window->callbacks.drop) - window->callbacks.drop((GLFWwindow*) window, count, paths); -} - -// Notifies shared code of a joystick connection or disconnection -// -void _glfwInputJoystick(_GLFWjoystick* js, int event) -{ - const int jid = (int) (js - _glfw.joysticks); - - if (event == GLFW_CONNECTED) - js->connected = GLFW_TRUE; - else if (event == GLFW_DISCONNECTED) - js->connected = GLFW_FALSE; - - if (_glfw.callbacks.joystick) - _glfw.callbacks.joystick(jid, event); -} - -// Notifies shared code of the new value of a joystick axis -// -void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value) -{ - js->axes[axis] = value; -} - -// Notifies shared code of the new value of a joystick button -// -void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value) -{ - js->buttons[button] = value; -} - -// Notifies shared code of the new value of a joystick hat -// -void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) -{ - const int base = js->buttonCount + hat * 4; - - js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE; - js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE; - js->buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE; - js->buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE; - - js->hats[hat] = value; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Adds the built-in set of gamepad mappings -// -void _glfwInitGamepadMappings(void) -{ - int jid; - size_t i; - const size_t count = sizeof(_glfwDefaultMappings) / sizeof(char*); - _glfw.mappings = calloc(count, sizeof(_GLFWmapping)); - - for (i = 0; i < count; i++) - { - if (parseMapping(&_glfw.mappings[_glfw.mappingCount], _glfwDefaultMappings[i])) - _glfw.mappingCount++; - } - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - _GLFWjoystick* js = _glfw.joysticks + jid; - if (js->connected) - js->mapping = findValidMapping(js); - } -} - -// Returns an available joystick object with arrays and name allocated -// -_GLFWjoystick* _glfwAllocJoystick(const char* name, - const char* guid, - int axisCount, - int buttonCount, - int hatCount) -{ - int jid; - _GLFWjoystick* js; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - if (!_glfw.joysticks[jid].allocated) - break; - } - - if (jid > GLFW_JOYSTICK_LAST) - return NULL; - - js = _glfw.joysticks + jid; - js->allocated = GLFW_TRUE; - js->axes = calloc(axisCount, sizeof(float)); - js->buttons = calloc(buttonCount + (size_t) hatCount * 4, 1); - js->hats = calloc(hatCount, 1); - js->axisCount = axisCount; - js->buttonCount = buttonCount; - js->hatCount = hatCount; - - strncpy(js->name, name, sizeof(js->name) - 1); - strncpy(js->guid, guid, sizeof(js->guid) - 1); - js->mapping = findValidMapping(js); - - return js; -} - -// Frees arrays and name and flags the joystick object as unused -// -void _glfwFreeJoystick(_GLFWjoystick* js) -{ - free(js->axes); - free(js->buttons); - free(js->hats); - memset(js, 0, sizeof(_GLFWjoystick)); -} - -// Center the cursor in the content area of the specified window -// -void _glfwCenterCursorInContentArea(_GLFWwindow* window) -{ - int width, height; - - _glfwPlatformGetWindowSize(window, &width, &height); - _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW public API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(0); - - switch (mode) - { - case GLFW_CURSOR: - return window->cursorMode; - case GLFW_STICKY_KEYS: - return window->stickyKeys; - case GLFW_STICKY_MOUSE_BUTTONS: - return window->stickyMouseButtons; - case GLFW_LOCK_KEY_MODS: - return window->lockKeyMods; - case GLFW_RAW_MOUSE_MOTION: - return window->rawMouseMotion; - } - - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); - return 0; -} - -GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - if (mode == GLFW_CURSOR) - { - if (value != GLFW_CURSOR_NORMAL && - value != GLFW_CURSOR_HIDDEN && - value != GLFW_CURSOR_DISABLED) - { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid cursor mode 0x%08X", - value); - return; - } - - if (window->cursorMode == value) - return; - - window->cursorMode = value; - - _glfwPlatformGetCursorPos(window, - &window->virtualCursorPosX, - &window->virtualCursorPosY); - _glfwPlatformSetCursorMode(window, value); - } - else if (mode == GLFW_STICKY_KEYS) - { - value = value ? GLFW_TRUE : GLFW_FALSE; - if (window->stickyKeys == value) - return; - - if (!value) - { - int i; - - // Release all sticky keys - for (i = 0; i <= GLFW_KEY_LAST; i++) - { - if (window->keys[i] == _GLFW_STICK) - window->keys[i] = GLFW_RELEASE; - } - } - - window->stickyKeys = value; - } - else if (mode == GLFW_STICKY_MOUSE_BUTTONS) - { - value = value ? GLFW_TRUE : GLFW_FALSE; - if (window->stickyMouseButtons == value) - return; - - if (!value) - { - int i; - - // Release all sticky mouse buttons - for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) - { - if (window->mouseButtons[i] == _GLFW_STICK) - window->mouseButtons[i] = GLFW_RELEASE; - } - } - - window->stickyMouseButtons = value; - } - else if (mode == GLFW_LOCK_KEY_MODS) - { - window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE; - } - else if (mode == GLFW_RAW_MOUSE_MOTION) - { - if (!_glfwPlatformRawMouseMotionSupported()) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Raw mouse motion is not supported on this system"); - return; - } - - value = value ? GLFW_TRUE : GLFW_FALSE; - if (window->rawMouseMotion == value) - return; - - window->rawMouseMotion = value; - _glfwPlatformSetRawMouseMotion(window, value); - } - else - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); -} - -GLFWAPI int glfwRawMouseMotionSupported(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - return _glfwPlatformRawMouseMotionSupported(); -} - -GLFWAPI const char* glfwGetKeyName(int key, int scancode) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (key != GLFW_KEY_UNKNOWN) - { - if (key != GLFW_KEY_KP_EQUAL && - (key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) && - (key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_WORLD_2)) - { - return NULL; - } - - scancode = _glfwPlatformGetKeyScancode(key); - } - - return _glfwPlatformGetScancodeName(scancode); -} - -GLFWAPI int glfwGetKeyScancode(int key) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(-1); - - if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); - return GLFW_RELEASE; - } - - return _glfwPlatformGetKeyScancode(key); -} - -GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); - - if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); - return GLFW_RELEASE; - } - - if (window->keys[key] == _GLFW_STICK) - { - // Sticky mode: release key now - window->keys[key] = GLFW_RELEASE; - return GLFW_PRESS; - } - - return (int) window->keys[key]; -} - -GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); - - if (button < GLFW_MOUSE_BUTTON_1 || button > GLFW_MOUSE_BUTTON_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid mouse button %i", button); - return GLFW_RELEASE; - } - - if (window->mouseButtons[button] == _GLFW_STICK) - { - // Sticky mode: release mouse button now - window->mouseButtons[button] = GLFW_RELEASE; - return GLFW_PRESS; - } - - return (int) window->mouseButtons[button]; -} - -GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - if (xpos) - *xpos = 0; - if (ypos) - *ypos = 0; - - _GLFW_REQUIRE_INIT(); - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - if (xpos) - *xpos = window->virtualCursorPosX; - if (ypos) - *ypos = window->virtualCursorPosY; - } - else - _glfwPlatformGetCursorPos(window, xpos, ypos); -} - -GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - if (xpos != xpos || xpos < -DBL_MAX || xpos > DBL_MAX || - ypos != ypos || ypos < -DBL_MAX || ypos > DBL_MAX) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid cursor position %f %f", - xpos, ypos); - return; - } - - if (!_glfwPlatformWindowFocused(window)) - return; - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - // Only update the accumulated position if the cursor is disabled - window->virtualCursorPosX = xpos; - window->virtualCursorPosY = ypos; - } - else - { - // Update system cursor position - _glfwPlatformSetCursorPos(window, xpos, ypos); - } -} - -GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) -{ - _GLFWcursor* cursor; - - assert(image != NULL); - assert(image->pixels != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (image->width <= 0 || image->height <= 0) - { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid image dimensions for cursor"); - return NULL; - } - - cursor = calloc(1, sizeof(_GLFWcursor)); - cursor->next = _glfw.cursorListHead; - _glfw.cursorListHead = cursor; - - if (!_glfwPlatformCreateCursor(cursor, image, xhot, yhot)) - { - glfwDestroyCursor((GLFWcursor*) cursor); - return NULL; - } - - return (GLFWcursor*) cursor; -} - -GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) -{ - _GLFWcursor* cursor; - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (shape != GLFW_ARROW_CURSOR && - shape != GLFW_IBEAM_CURSOR && - shape != GLFW_CROSSHAIR_CURSOR && - shape != GLFW_HAND_CURSOR && - shape != GLFW_HRESIZE_CURSOR && - shape != GLFW_VRESIZE_CURSOR) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape); - return NULL; - } - - cursor = calloc(1, sizeof(_GLFWcursor)); - cursor->next = _glfw.cursorListHead; - _glfw.cursorListHead = cursor; - - if (!_glfwPlatformCreateStandardCursor(cursor, shape)) - { - glfwDestroyCursor((GLFWcursor*) cursor); - return NULL; - } - - return (GLFWcursor*) cursor; -} - -GLFWAPI void glfwDestroyCursor(GLFWcursor* handle) -{ - _GLFWcursor* cursor = (_GLFWcursor*) handle; - - _GLFW_REQUIRE_INIT(); - - if (cursor == NULL) - return; - - // Make sure the cursor is not being used by any window - { - _GLFWwindow* window; - - for (window = _glfw.windowListHead; window; window = window->next) - { - if (window->cursor == cursor) - glfwSetCursor((GLFWwindow*) window, NULL); - } - } - - _glfwPlatformDestroyCursor(cursor); - - // Unlink cursor from global linked list - { - _GLFWcursor** prev = &_glfw.cursorListHead; - - while (*prev != cursor) - prev = &((*prev)->next); - - *prev = cursor->next; - } - - free(cursor); -} - -GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) -{ - _GLFWwindow* window = (_GLFWwindow*) windowHandle; - _GLFWcursor* cursor = (_GLFWcursor*) cursorHandle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - window->cursor = cursor; - - _glfwPlatformSetCursor(window, cursor); -} - -GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.key, cbfun); - return cbfun; -} - -GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.character, cbfun); - return cbfun; -} - -GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun); - return cbfun; -} - -GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle, - GLFWmousebuttonfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.mouseButton, cbfun); - return cbfun; -} - -GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle, - GLFWcursorposfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.cursorPos, cbfun); - return cbfun; -} - -GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle, - GLFWcursorenterfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.cursorEnter, cbfun); - return cbfun; -} - -GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle, - GLFWscrollfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.scroll, cbfun); - return cbfun; -} - -GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.drop, cbfun); - return cbfun; -} - -GLFWAPI int glfwJoystickPresent(int jid) -{ - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - - if (jid < 0 || jid > GLFW_JOYSTICK_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); - return GLFW_FALSE; - } - - js = _glfw.joysticks + jid; - if (!js->connected) - return GLFW_FALSE; - - return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); -} - -GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) -{ - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - assert(count != NULL); - - *count = 0; - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (jid < 0 || jid > GLFW_JOYSTICK_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); - return NULL; - } - - js = _glfw.joysticks + jid; - if (!js->connected) - return NULL; - - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_AXES)) - return NULL; - - *count = js->axisCount; - return js->axes; -} - -GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) -{ - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - assert(count != NULL); - - *count = 0; - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (jid < 0 || jid > GLFW_JOYSTICK_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); - return NULL; - } - - js = _glfw.joysticks + jid; - if (!js->connected) - return NULL; - - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) - return NULL; - - if (_glfw.hints.init.hatButtons) - *count = js->buttonCount + js->hatCount * 4; - else - *count = js->buttonCount; - - return js->buttons; -} - -GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) -{ - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - assert(count != NULL); - - *count = 0; - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (jid < 0 || jid > GLFW_JOYSTICK_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); - return NULL; - } - - js = _glfw.joysticks + jid; - if (!js->connected) - return NULL; - - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) - return NULL; - - *count = js->hatCount; - return js->hats; -} - -GLFWAPI const char* glfwGetJoystickName(int jid) -{ - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (jid < 0 || jid > GLFW_JOYSTICK_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); - return NULL; - } - - js = _glfw.joysticks + jid; - if (!js->connected) - return NULL; - - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) - return NULL; - - return js->name; -} - -GLFWAPI const char* glfwGetJoystickGUID(int jid) -{ - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (jid < 0 || jid > GLFW_JOYSTICK_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); - return NULL; - } - - js = _glfw.joysticks + jid; - if (!js->connected) - return NULL; - - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) - return NULL; - - return js->guid; -} - -GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer) -{ - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - - _GLFW_REQUIRE_INIT(); - - js = _glfw.joysticks + jid; - if (!js->allocated) - return; - - js->userPointer = pointer; -} - -GLFWAPI void* glfwGetJoystickUserPointer(int jid) -{ - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - js = _glfw.joysticks + jid; - if (!js->allocated) - return NULL; - - return js->userPointer; -} - -GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun); - return cbfun; -} - -GLFWAPI int glfwUpdateGamepadMappings(const char* string) -{ - int jid; - const char* c = string; - - assert(string != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - - while (*c) - { - if ((*c >= '0' && *c <= '9') || - (*c >= 'a' && *c <= 'f') || - (*c >= 'A' && *c <= 'F')) - { - char line[1024]; - - const size_t length = strcspn(c, "\r\n"); - if (length < sizeof(line)) - { - _GLFWmapping mapping = {{0}}; - - memcpy(line, c, length); - line[length] = '\0'; - - if (parseMapping(&mapping, line)) - { - _GLFWmapping* previous = findMapping(mapping.guid); - if (previous) - *previous = mapping; - else - { - _glfw.mappingCount++; - _glfw.mappings = - realloc(_glfw.mappings, - sizeof(_GLFWmapping) * _glfw.mappingCount); - _glfw.mappings[_glfw.mappingCount - 1] = mapping; - } - } - } - - c += length; - } - else - { - c += strcspn(c, "\r\n"); - c += strspn(c, "\r\n"); - } - } - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - _GLFWjoystick* js = _glfw.joysticks + jid; - if (js->connected) - js->mapping = findValidMapping(js); - } - - return GLFW_TRUE; -} - -GLFWAPI int glfwJoystickIsGamepad(int jid) -{ - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - - if (jid < 0 || jid > GLFW_JOYSTICK_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); - return GLFW_FALSE; - } - - js = _glfw.joysticks + jid; - if (!js->connected) - return GLFW_FALSE; - - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) - return GLFW_FALSE; - - return js->mapping != NULL; -} - -GLFWAPI const char* glfwGetGamepadName(int jid) -{ - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (jid < 0 || jid > GLFW_JOYSTICK_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); - return NULL; - } - - js = _glfw.joysticks + jid; - if (!js->connected) - return NULL; - - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) - return NULL; - - if (!js->mapping) - return NULL; - - return js->mapping->name; -} - -GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state) -{ - int i; - _GLFWjoystick* js; - - assert(jid >= GLFW_JOYSTICK_1); - assert(jid <= GLFW_JOYSTICK_LAST); - assert(state != NULL); - - memset(state, 0, sizeof(GLFWgamepadstate)); - - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - - if (jid < 0 || jid > GLFW_JOYSTICK_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); - return GLFW_FALSE; - } - - js = _glfw.joysticks + jid; - if (!js->connected) - return GLFW_FALSE; - - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL)) - return GLFW_FALSE; - - if (!js->mapping) - return GLFW_FALSE; - - for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) - { - const _GLFWmapelement* e = js->mapping->buttons + i; - if (e->type == _GLFW_JOYSTICK_AXIS) - { - const float value = js->axes[e->index] * e->axisScale + e->axisOffset; - // HACK: This should be baked into the value transform - // TODO: Bake into transform when implementing output modifiers - if (e->axisOffset < 0 || (e->axisOffset == 0 && e->axisScale > 0)) - { - if (value >= 0.f) - state->buttons[i] = GLFW_PRESS; - } - else - { - if (value <= 0.f) - state->buttons[i] = GLFW_PRESS; - } - } - else if (e->type == _GLFW_JOYSTICK_HATBIT) - { - const unsigned int hat = e->index >> 4; - const unsigned int bit = e->index & 0xf; - if (js->hats[hat] & bit) - state->buttons[i] = GLFW_PRESS; - } - else if (e->type == _GLFW_JOYSTICK_BUTTON) - state->buttons[i] = js->buttons[e->index]; - } - - for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) - { - const _GLFWmapelement* e = js->mapping->axes + i; - if (e->type == _GLFW_JOYSTICK_AXIS) - { - const float value = js->axes[e->index] * e->axisScale + e->axisOffset; - state->axes[i] = _glfw_fminf(_glfw_fmaxf(value, -1.f), 1.f); - } - else if (e->type == _GLFW_JOYSTICK_HATBIT) - { - const unsigned int hat = e->index >> 4; - const unsigned int bit = e->index & 0xf; - if (js->hats[hat] & bit) - state->axes[i] = 1.f; - else - state->axes[i] = -1.f; - } - else if (e->type == _GLFW_JOYSTICK_BUTTON) - state->axes[i] = js->buttons[e->index] * 2.f - 1.f; - } - - return GLFW_TRUE; -} - -GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) -{ - assert(string != NULL); - - _GLFW_REQUIRE_INIT(); - _glfwPlatformSetClipboardString(string); -} - -GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfwPlatformGetClipboardString(); -} - -GLFWAPI double glfwGetTime(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(0.0); - return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) / - _glfwPlatformGetTimerFrequency(); -} - -GLFWAPI void glfwSetTime(double time) -{ - _GLFW_REQUIRE_INIT(); - - if (time != time || time < 0.0 || time > 18446744073.0) - { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", time); - return; - } - - _glfw.timer.offset = _glfwPlatformGetTimerValue() - - (uint64_t) (time * _glfwPlatformGetTimerFrequency()); -} - -GLFWAPI uint64_t glfwGetTimerValue(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(0); - return _glfwPlatformGetTimerValue(); -} - -GLFWAPI uint64_t glfwGetTimerFrequency(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(0); - return _glfwPlatformGetTimerFrequency(); -} - diff --git a/libs/zglfw/libs/glfw/src/internal.h b/libs/zglfw/libs/glfw/src/internal.h deleted file mode 100644 index 7734caa3a..000000000 --- a/libs/zglfw/libs/glfw/src/internal.h +++ /dev/null @@ -1,786 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== - -#pragma once - -#if defined(_GLFW_USE_CONFIG_H) - #include "glfw_config.h" -#endif - -#if defined(GLFW_INCLUDE_GLCOREARB) || \ - defined(GLFW_INCLUDE_ES1) || \ - defined(GLFW_INCLUDE_ES2) || \ - defined(GLFW_INCLUDE_ES3) || \ - defined(GLFW_INCLUDE_ES31) || \ - defined(GLFW_INCLUDE_ES32) || \ - defined(GLFW_INCLUDE_NONE) || \ - defined(GLFW_INCLUDE_GLEXT) || \ - defined(GLFW_INCLUDE_GLU) || \ - defined(GLFW_INCLUDE_VULKAN) || \ - defined(GLFW_DLL) - #error "You must not define any header option macros when compiling GLFW" -#endif - -#define GLFW_INCLUDE_NONE -#include "../include/GLFW/glfw3.h" - -#define _GLFW_INSERT_FIRST 0 -#define _GLFW_INSERT_LAST 1 - -#define _GLFW_POLL_PRESENCE 0 -#define _GLFW_POLL_AXES 1 -#define _GLFW_POLL_BUTTONS 2 -#define _GLFW_POLL_ALL (_GLFW_POLL_AXES | _GLFW_POLL_BUTTONS) - -#define _GLFW_MESSAGE_SIZE 1024 - -typedef int GLFWbool; - -typedef struct _GLFWerror _GLFWerror; -typedef struct _GLFWinitconfig _GLFWinitconfig; -typedef struct _GLFWwndconfig _GLFWwndconfig; -typedef struct _GLFWctxconfig _GLFWctxconfig; -typedef struct _GLFWfbconfig _GLFWfbconfig; -typedef struct _GLFWcontext _GLFWcontext; -typedef struct _GLFWwindow _GLFWwindow; -typedef struct _GLFWlibrary _GLFWlibrary; -typedef struct _GLFWmonitor _GLFWmonitor; -typedef struct _GLFWcursor _GLFWcursor; -typedef struct _GLFWmapelement _GLFWmapelement; -typedef struct _GLFWmapping _GLFWmapping; -typedef struct _GLFWjoystick _GLFWjoystick; -typedef struct _GLFWtls _GLFWtls; -typedef struct _GLFWmutex _GLFWmutex; - -typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); -typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); -typedef void (* _GLFWswapintervalfun)(int); -typedef int (* _GLFWextensionsupportedfun)(const char*); -typedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*); -typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); - -#define GL_VERSION 0x1f02 -#define GL_NONE 0 -#define GL_COLOR_BUFFER_BIT 0x00004000 -#define GL_UNSIGNED_BYTE 0x1401 -#define GL_EXTENSIONS 0x1f03 -#define GL_NUM_EXTENSIONS 0x821d -#define GL_CONTEXT_FLAGS 0x821e -#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 -#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 -#define GL_CONTEXT_PROFILE_MASK 0x9126 -#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 -#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 -#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 -#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 -#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 -#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82fb -#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82fc -#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 - -typedef int GLint; -typedef unsigned int GLuint; -typedef unsigned int GLenum; -typedef unsigned int GLbitfield; -typedef unsigned char GLubyte; - -typedef void (APIENTRY * PFNGLCLEARPROC)(GLbitfield); -typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGPROC)(GLenum); -typedef void (APIENTRY * PFNGLGETINTEGERVPROC)(GLenum,GLint*); -typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGIPROC)(GLenum,GLuint); - -#define VK_NULL_HANDLE 0 - -typedef void* VkInstance; -typedef void* VkPhysicalDevice; -typedef uint64_t VkSurfaceKHR; -typedef uint32_t VkFlags; -typedef uint32_t VkBool32; - -typedef enum VkStructureType -{ - VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, - VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, - VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, - VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, - VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, - VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000, - VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF -} VkStructureType; - -typedef enum VkResult -{ - VK_SUCCESS = 0, - VK_NOT_READY = 1, - VK_TIMEOUT = 2, - VK_EVENT_SET = 3, - VK_EVENT_RESET = 4, - VK_INCOMPLETE = 5, - VK_ERROR_OUT_OF_HOST_MEMORY = -1, - VK_ERROR_OUT_OF_DEVICE_MEMORY = -2, - VK_ERROR_INITIALIZATION_FAILED = -3, - VK_ERROR_DEVICE_LOST = -4, - VK_ERROR_MEMORY_MAP_FAILED = -5, - VK_ERROR_LAYER_NOT_PRESENT = -6, - VK_ERROR_EXTENSION_NOT_PRESENT = -7, - VK_ERROR_FEATURE_NOT_PRESENT = -8, - VK_ERROR_INCOMPATIBLE_DRIVER = -9, - VK_ERROR_TOO_MANY_OBJECTS = -10, - VK_ERROR_FORMAT_NOT_SUPPORTED = -11, - VK_ERROR_SURFACE_LOST_KHR = -1000000000, - VK_SUBOPTIMAL_KHR = 1000001003, - VK_ERROR_OUT_OF_DATE_KHR = -1000001004, - VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, - VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, - VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, - VK_RESULT_MAX_ENUM = 0x7FFFFFFF -} VkResult; - -typedef struct VkAllocationCallbacks VkAllocationCallbacks; - -typedef struct VkExtensionProperties -{ - char extensionName[256]; - uint32_t specVersion; -} VkExtensionProperties; - -typedef void (APIENTRY * PFN_vkVoidFunction)(void); - -#if defined(_GLFW_VULKAN_STATIC) - PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance,const char*); - VkResult vkEnumerateInstanceExtensionProperties(const char*,uint32_t*,VkExtensionProperties*); -#else - typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,const char*); - typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*); - #define vkEnumerateInstanceExtensionProperties _glfw.vk.EnumerateInstanceExtensionProperties - #define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr -#endif - -#if defined(_GLFW_COCOA) - #include "cocoa_platform.h" -#elif defined(_GLFW_WIN32) - #include "win32_platform.h" -#elif defined(_GLFW_X11) - #include "x11_platform.h" -#elif defined(_GLFW_WAYLAND) - #include "wl_platform.h" -#elif defined(_GLFW_OSMESA) - #include "null_platform.h" -#else - #error "No supported window creation API selected" -#endif - -// Constructs a version number string from the public header macros -#define _GLFW_CONCAT_VERSION(m, n, r) #m "." #n "." #r -#define _GLFW_MAKE_VERSION(m, n, r) _GLFW_CONCAT_VERSION(m, n, r) -#define _GLFW_VERSION_NUMBER _GLFW_MAKE_VERSION(GLFW_VERSION_MAJOR, \ - GLFW_VERSION_MINOR, \ - GLFW_VERSION_REVISION) - -// Checks for whether the library has been initialized -#define _GLFW_REQUIRE_INIT() \ - if (!_glfw.initialized) \ - { \ - _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ - return; \ - } -#define _GLFW_REQUIRE_INIT_OR_RETURN(x) \ - if (!_glfw.initialized) \ - { \ - _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ - return x; \ - } - -// Swaps the provided pointers -#define _GLFW_SWAP_POINTERS(x, y) \ - { \ - void* t; \ - t = x; \ - x = y; \ - y = t; \ - } - -// Per-thread error structure -// -struct _GLFWerror -{ - _GLFWerror* next; - int code; - char description[_GLFW_MESSAGE_SIZE]; -}; - -// Initialization configuration -// -// Parameters relating to the initialization of the library -// -struct _GLFWinitconfig -{ - GLFWbool hatButtons; - struct { - GLFWbool menubar; - GLFWbool chdir; - } ns; -}; - -// Window configuration -// -// Parameters relating to the creation of the window but not directly related -// to the framebuffer. This is used to pass window creation parameters from -// shared code to the platform API. -// -struct _GLFWwndconfig -{ - int width; - int height; - const char* title; - GLFWbool resizable; - GLFWbool visible; - GLFWbool decorated; - GLFWbool focused; - GLFWbool autoIconify; - GLFWbool floating; - GLFWbool maximized; - GLFWbool centerCursor; - GLFWbool focusOnShow; - GLFWbool scaleToMonitor; - struct { - GLFWbool retina; - char frameName[256]; - } ns; - struct { - char className[256]; - char instanceName[256]; - } x11; -}; - -// Context configuration -// -// Parameters relating to the creation of the context but not directly related -// to the framebuffer. This is used to pass context creation parameters from -// shared code to the platform API. -// -struct _GLFWctxconfig -{ - int client; - int source; - int major; - int minor; - GLFWbool forward; - GLFWbool debug; - GLFWbool noerror; - int profile; - int robustness; - int release; - _GLFWwindow* share; - struct { - GLFWbool offline; - } nsgl; -}; - -// Framebuffer configuration -// -// This describes buffers and their sizes. It also contains -// a platform-specific ID used to map back to the backend API object. -// -// It is used to pass framebuffer parameters from shared code to the platform -// API and also to enumerate and select available framebuffer configs. -// -struct _GLFWfbconfig -{ - int redBits; - int greenBits; - int blueBits; - int alphaBits; - int depthBits; - int stencilBits; - int accumRedBits; - int accumGreenBits; - int accumBlueBits; - int accumAlphaBits; - int auxBuffers; - GLFWbool stereo; - int samples; - GLFWbool sRGB; - GLFWbool doublebuffer; - GLFWbool transparent; - uintptr_t handle; -}; - -// Context structure -// -struct _GLFWcontext -{ - int client; - int source; - int major, minor, revision; - GLFWbool forward, debug, noerror; - int profile; - int robustness; - int release; - - PFNGLGETSTRINGIPROC GetStringi; - PFNGLGETINTEGERVPROC GetIntegerv; - PFNGLGETSTRINGPROC GetString; - - _GLFWmakecontextcurrentfun makeCurrent; - _GLFWswapbuffersfun swapBuffers; - _GLFWswapintervalfun swapInterval; - _GLFWextensionsupportedfun extensionSupported; - _GLFWgetprocaddressfun getProcAddress; - _GLFWdestroycontextfun destroy; - - // This is defined in the context API's context.h - _GLFW_PLATFORM_CONTEXT_STATE; - // This is defined in egl_context.h - _GLFW_EGL_CONTEXT_STATE; - // This is defined in osmesa_context.h - _GLFW_OSMESA_CONTEXT_STATE; -}; - -// Window and context structure -// -struct _GLFWwindow -{ - struct _GLFWwindow* next; - - // Window settings and state - GLFWbool resizable; - GLFWbool decorated; - GLFWbool autoIconify; - GLFWbool floating; - GLFWbool focusOnShow; - GLFWbool shouldClose; - void* userPointer; - GLFWbool doublebuffer; - GLFWvidmode videoMode; - _GLFWmonitor* monitor; - _GLFWcursor* cursor; - - int minwidth, minheight; - int maxwidth, maxheight; - int numer, denom; - - GLFWbool stickyKeys; - GLFWbool stickyMouseButtons; - GLFWbool lockKeyMods; - int cursorMode; - char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; - char keys[GLFW_KEY_LAST + 1]; - // Virtual cursor position when cursor is disabled - double virtualCursorPosX, virtualCursorPosY; - GLFWbool rawMouseMotion; - - _GLFWcontext context; - - struct { - GLFWwindowposfun pos; - GLFWwindowsizefun size; - GLFWwindowclosefun close; - GLFWwindowrefreshfun refresh; - GLFWwindowfocusfun focus; - GLFWwindowiconifyfun iconify; - GLFWwindowmaximizefun maximize; - GLFWframebuffersizefun fbsize; - GLFWwindowcontentscalefun scale; - GLFWmousebuttonfun mouseButton; - GLFWcursorposfun cursorPos; - GLFWcursorenterfun cursorEnter; - GLFWscrollfun scroll; - GLFWkeyfun key; - GLFWcharfun character; - GLFWcharmodsfun charmods; - GLFWdropfun drop; - } callbacks; - - // This is defined in the window API's platform.h - _GLFW_PLATFORM_WINDOW_STATE; -}; - -// Monitor structure -// -struct _GLFWmonitor -{ - char name[128]; - void* userPointer; - - // Physical dimensions in millimeters. - int widthMM, heightMM; - - // The window whose video mode is current on this monitor - _GLFWwindow* window; - - GLFWvidmode* modes; - int modeCount; - GLFWvidmode currentMode; - - GLFWgammaramp originalRamp; - GLFWgammaramp currentRamp; - - // This is defined in the window API's platform.h - _GLFW_PLATFORM_MONITOR_STATE; -}; - -// Cursor structure -// -struct _GLFWcursor -{ - _GLFWcursor* next; - - // This is defined in the window API's platform.h - _GLFW_PLATFORM_CURSOR_STATE; -}; - -// Gamepad mapping element structure -// -struct _GLFWmapelement -{ - uint8_t type; - uint8_t index; - int8_t axisScale; - int8_t axisOffset; -}; - -// Gamepad mapping structure -// -struct _GLFWmapping -{ - char name[128]; - char guid[33]; - _GLFWmapelement buttons[15]; - _GLFWmapelement axes[6]; -}; - -// Joystick structure -// -struct _GLFWjoystick -{ - GLFWbool allocated; - GLFWbool connected; - float* axes; - int axisCount; - unsigned char* buttons; - int buttonCount; - unsigned char* hats; - int hatCount; - char name[128]; - void* userPointer; - char guid[33]; - _GLFWmapping* mapping; - - // This is defined in the joystick API's joystick.h - _GLFW_PLATFORM_JOYSTICK_STATE; -}; - -// Thread local storage structure -// -struct _GLFWtls -{ - // This is defined in the platform's thread.h - _GLFW_PLATFORM_TLS_STATE; -}; - -// Mutex structure -// -struct _GLFWmutex -{ - // This is defined in the platform's thread.h - _GLFW_PLATFORM_MUTEX_STATE; -}; - -// Library global data -// -struct _GLFWlibrary -{ - GLFWbool initialized; - - struct { - _GLFWinitconfig init; - _GLFWfbconfig framebuffer; - _GLFWwndconfig window; - _GLFWctxconfig context; - int refreshRate; - } hints; - - _GLFWerror* errorListHead; - _GLFWcursor* cursorListHead; - _GLFWwindow* windowListHead; - - _GLFWmonitor** monitors; - int monitorCount; - - _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; - _GLFWmapping* mappings; - int mappingCount; - - _GLFWtls errorSlot; - _GLFWtls contextSlot; - _GLFWmutex errorLock; - - struct { - uint64_t offset; - // This is defined in the platform's time.h - _GLFW_PLATFORM_LIBRARY_TIMER_STATE; - } timer; - - struct { - GLFWbool available; - void* handle; - char* extensions[2]; -#if !defined(_GLFW_VULKAN_STATIC) - PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; - PFN_vkGetInstanceProcAddr GetInstanceProcAddr; -#endif - GLFWbool KHR_surface; -#if defined(_GLFW_WIN32) - GLFWbool KHR_win32_surface; -#elif defined(_GLFW_COCOA) - GLFWbool MVK_macos_surface; - GLFWbool EXT_metal_surface; -#elif defined(_GLFW_X11) - GLFWbool KHR_xlib_surface; - GLFWbool KHR_xcb_surface; -#elif defined(_GLFW_WAYLAND) - GLFWbool KHR_wayland_surface; -#endif - } vk; - - struct { - GLFWmonitorfun monitor; - GLFWjoystickfun joystick; - } callbacks; - - // This is defined in the window API's platform.h - _GLFW_PLATFORM_LIBRARY_WINDOW_STATE; - // This is defined in the context API's context.h - _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE; - // This is defined in the platform's joystick.h - _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; - // This is defined in egl_context.h - _GLFW_EGL_LIBRARY_CONTEXT_STATE; - // This is defined in osmesa_context.h - _GLFW_OSMESA_LIBRARY_CONTEXT_STATE; -}; - -// Global state shared between compilation units of GLFW -// -extern _GLFWlibrary _glfw; - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformInit(void); -void _glfwPlatformTerminate(void); -const char* _glfwPlatformGetVersionString(void); - -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled); -GLFWbool _glfwPlatformRawMouseMotionSupported(void); -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, int xhot, int yhot); -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); - -const char* _glfwPlatformGetScancodeName(int scancode); -int _glfwPlatformGetKeyScancode(int key); - -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor); -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale); -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height); -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); - -void _glfwPlatformSetClipboardString(const char* string); -const char* _glfwPlatformGetClipboardString(void); - -int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode); -void _glfwPlatformUpdateGamepadGUID(char* guid); - -uint64_t _glfwPlatformGetTimerValue(void); -uint64_t _glfwPlatformGetTimerFrequency(void); - -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); -void _glfwPlatformDestroyWindow(_GLFWwindow* window); -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images); -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos); -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos); -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height); -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height); -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight); -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom); -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale); -void _glfwPlatformIconifyWindow(_GLFWwindow* window); -void _glfwPlatformRestoreWindow(_GLFWwindow* window); -void _glfwPlatformMaximizeWindow(_GLFWwindow* window); -void _glfwPlatformShowWindow(_GLFWwindow* window); -void _glfwPlatformHideWindow(_GLFWwindow* window); -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window); -void _glfwPlatformFocusWindow(_GLFWwindow* window); -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, - int xpos, int ypos, int width, int height, - int refreshRate); -int _glfwPlatformWindowFocused(_GLFWwindow* window); -int _glfwPlatformWindowIconified(_GLFWwindow* window); -int _glfwPlatformWindowVisible(_GLFWwindow* window); -int _glfwPlatformWindowMaximized(_GLFWwindow* window); -int _glfwPlatformWindowHovered(_GLFWwindow* window); -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window); -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); - -void _glfwPlatformPollEvents(void); -void _glfwPlatformWaitEvents(void); -void _glfwPlatformWaitEventsTimeout(double timeout); -void _glfwPlatformPostEmptyEvent(void); - -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions); -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily); -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface); - -GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls); -void _glfwPlatformDestroyTls(_GLFWtls* tls); -void* _glfwPlatformGetTls(_GLFWtls* tls); -void _glfwPlatformSetTls(_GLFWtls* tls, void* value); - -GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex); -void _glfwPlatformDestroyMutex(_GLFWmutex* mutex); -void _glfwPlatformLockMutex(_GLFWmutex* mutex); -void _glfwPlatformUnlockMutex(_GLFWmutex* mutex); - - -////////////////////////////////////////////////////////////////////////// -////// GLFW event API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused); -void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos); -void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); -void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height); -void _glfwInputWindowContentScale(_GLFWwindow* window, - float xscale, float yscale); -void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified); -void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized); -void _glfwInputWindowDamage(_GLFWwindow* window); -void _glfwInputWindowCloseRequest(_GLFWwindow* window); -void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor); - -void _glfwInputKey(_GLFWwindow* window, - int key, int scancode, int action, int mods); -void _glfwInputChar(_GLFWwindow* window, - uint32_t codepoint, int mods, GLFWbool plain); -void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset); -void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); -void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); -void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered); -void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); -void _glfwInputJoystick(_GLFWjoystick* js, int event); -void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); -void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); -void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); - -void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); -void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); - -#if defined(__GNUC__) -void _glfwInputError(int code, const char* format, ...) - __attribute__((format(printf, 2, 3))); -#else -void _glfwInputError(int code, const char* format, ...); -#endif - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions); -const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, - const _GLFWfbconfig* alternatives, - unsigned int count); -GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig); -GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig); - -const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, - const GLFWvidmode* desired); -int _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second); -_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM); -void _glfwFreeMonitor(_GLFWmonitor* monitor); -void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size); -void _glfwFreeGammaArrays(GLFWgammaramp* ramp); -void _glfwSplitBPP(int bpp, int* red, int* green, int* blue); - -void _glfwInitGamepadMappings(void); -_GLFWjoystick* _glfwAllocJoystick(const char* name, - const char* guid, - int axisCount, - int buttonCount, - int hatCount); -void _glfwFreeJoystick(_GLFWjoystick* js); -void _glfwCenterCursorInContentArea(_GLFWwindow* window); - -GLFWbool _glfwInitVulkan(int mode); -void _glfwTerminateVulkan(void); -const char* _glfwGetVulkanResultString(VkResult result); - -size_t _glfwEncodeUTF8(char* s, uint32_t codepoint); -char** _glfwParseUriList(char* text, int* count); - -char* _glfw_strdup(const char* source); -int _glfw_min(int a, int b); -int _glfw_max(int a, int b); -float _glfw_fminf(float a, float b); -float _glfw_fmaxf(float a, float b); - diff --git a/libs/zglfw/libs/glfw/src/linux_joystick.c b/libs/zglfw/libs/glfw/src/linux_joystick.c deleted file mode 100644 index 0894a7263..000000000 --- a/libs/zglfw/libs/glfw/src/linux_joystick.c +++ /dev/null @@ -1,433 +0,0 @@ -//======================================================================== -// GLFW 3.3 Linux - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef SYN_DROPPED // < v2.6.39 kernel headers -// Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32 -#define SYN_DROPPED 3 -#endif - -// Apply an EV_KEY event to the specified joystick -// -static void handleKeyEvent(_GLFWjoystick* js, int code, int value) -{ - _glfwInputJoystickButton(js, - js->linjs.keyMap[code - BTN_MISC], - value ? GLFW_PRESS : GLFW_RELEASE); -} - -// Apply an EV_ABS event to the specified joystick -// -static void handleAbsEvent(_GLFWjoystick* js, int code, int value) -{ - const int index = js->linjs.absMap[code]; - - if (code >= ABS_HAT0X && code <= ABS_HAT3Y) - { - static const char stateMap[3][3] = - { - { GLFW_HAT_CENTERED, GLFW_HAT_UP, GLFW_HAT_DOWN }, - { GLFW_HAT_LEFT, GLFW_HAT_LEFT_UP, GLFW_HAT_LEFT_DOWN }, - { GLFW_HAT_RIGHT, GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT_DOWN }, - }; - - const int hat = (code - ABS_HAT0X) / 2; - const int axis = (code - ABS_HAT0X) % 2; - int* state = js->linjs.hats[hat]; - - // NOTE: Looking at several input drivers, it seems all hat events use - // -1 for left / up, 0 for centered and 1 for right / down - if (value == 0) - state[axis] = 0; - else if (value < 0) - state[axis] = 1; - else if (value > 0) - state[axis] = 2; - - _glfwInputJoystickHat(js, index, stateMap[state[0]][state[1]]); - } - else - { - const struct input_absinfo* info = &js->linjs.absInfo[code]; - float normalized = value; - - const int range = info->maximum - info->minimum; - if (range) - { - // Normalize to 0.0 -> 1.0 - normalized = (normalized - info->minimum) / range; - // Normalize to -1.0 -> 1.0 - normalized = normalized * 2.0f - 1.0f; - } - - _glfwInputJoystickAxis(js, index, normalized); - } -} - -// Poll state of absolute axes -// -static void pollAbsState(_GLFWjoystick* js) -{ - for (int code = 0; code < ABS_CNT; code++) - { - if (js->linjs.absMap[code] < 0) - continue; - - struct input_absinfo* info = &js->linjs.absInfo[code]; - - if (ioctl(js->linjs.fd, EVIOCGABS(code), info) < 0) - continue; - - handleAbsEvent(js, code, info->value); - } -} - -#define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8))) - -// Attempt to open the specified joystick device -// -static GLFWbool openJoystickDevice(const char* path) -{ - for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - if (!_glfw.joysticks[jid].connected) - continue; - if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) - return GLFW_FALSE; - } - - _GLFWjoystickLinux linjs = {0}; - linjs.fd = open(path, O_RDONLY | O_NONBLOCK); - if (linjs.fd == -1) - return GLFW_FALSE; - - char evBits[(EV_CNT + 7) / 8] = {0}; - char keyBits[(KEY_CNT + 7) / 8] = {0}; - char absBits[(ABS_CNT + 7) / 8] = {0}; - struct input_id id; - - if (ioctl(linjs.fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 || - ioctl(linjs.fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 || - ioctl(linjs.fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 || - ioctl(linjs.fd, EVIOCGID, &id) < 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Linux: Failed to query input device: %s", - strerror(errno)); - close(linjs.fd); - return GLFW_FALSE; - } - - // Ensure this device supports the events expected of a joystick - if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits)) - { - close(linjs.fd); - return GLFW_FALSE; - } - - char name[256] = ""; - - if (ioctl(linjs.fd, EVIOCGNAME(sizeof(name)), name) < 0) - strncpy(name, "Unknown", sizeof(name)); - - char guid[33] = ""; - - // Generate a joystick GUID that matches the SDL 2.0.5+ one - if (id.vendor && id.product && id.version) - { - sprintf(guid, "%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000", - id.bustype & 0xff, id.bustype >> 8, - id.vendor & 0xff, id.vendor >> 8, - id.product & 0xff, id.product >> 8, - id.version & 0xff, id.version >> 8); - } - else - { - sprintf(guid, "%02x%02x0000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", - id.bustype & 0xff, id.bustype >> 8, - name[0], name[1], name[2], name[3], - name[4], name[5], name[6], name[7], - name[8], name[9], name[10]); - } - - int axisCount = 0, buttonCount = 0, hatCount = 0; - - for (int code = BTN_MISC; code < KEY_CNT; code++) - { - if (!isBitSet(code, keyBits)) - continue; - - linjs.keyMap[code - BTN_MISC] = buttonCount; - buttonCount++; - } - - for (int code = 0; code < ABS_CNT; code++) - { - linjs.absMap[code] = -1; - if (!isBitSet(code, absBits)) - continue; - - if (code >= ABS_HAT0X && code <= ABS_HAT3Y) - { - linjs.absMap[code] = hatCount; - hatCount++; - // Skip the Y axis - code++; - } - else - { - if (ioctl(linjs.fd, EVIOCGABS(code), &linjs.absInfo[code]) < 0) - continue; - - linjs.absMap[code] = axisCount; - axisCount++; - } - } - - _GLFWjoystick* js = - _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount); - if (!js) - { - close(linjs.fd); - return GLFW_FALSE; - } - - strncpy(linjs.path, path, sizeof(linjs.path) - 1); - memcpy(&js->linjs, &linjs, sizeof(linjs)); - - pollAbsState(js); - - _glfwInputJoystick(js, GLFW_CONNECTED); - return GLFW_TRUE; -} - -#undef isBitSet - -// Frees all resources associated with the specified joystick -// -static void closeJoystick(_GLFWjoystick* js) -{ - _glfwInputJoystick(js, GLFW_DISCONNECTED); - close(js->linjs.fd); - _glfwFreeJoystick(js); -} - -// Lexically compare joysticks by name; used by qsort -// -static int compareJoysticks(const void* fp, const void* sp) -{ - const _GLFWjoystick* fj = fp; - const _GLFWjoystick* sj = sp; - return strcmp(fj->linjs.path, sj->linjs.path); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialize joystick interface -// -GLFWbool _glfwInitJoysticksLinux(void) -{ - const char* dirname = "/dev/input"; - - _glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); - if (_glfw.linjs.inotify > 0) - { - // HACK: Register for IN_ATTRIB to get notified when udev is done - // This works well in practice but the true way is libudev - - _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify, - dirname, - IN_CREATE | IN_ATTRIB | IN_DELETE); - } - - // Continue without device connection notifications if inotify fails - - if (regcomp(&_glfw.linjs.regex, "^event[0-9]\\+$", 0) != 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex"); - return GLFW_FALSE; - } - - int count = 0; - - DIR* dir = opendir(dirname); - if (dir) - { - struct dirent* entry; - - while ((entry = readdir(dir))) - { - regmatch_t match; - - if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0) - continue; - - char path[PATH_MAX]; - - snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name); - - if (openJoystickDevice(path)) - count++; - } - - closedir(dir); - } - - // Continue with no joysticks if enumeration fails - - qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks); - return GLFW_TRUE; -} - -// Close all opened joystick handles -// -void _glfwTerminateJoysticksLinux(void) -{ - int jid; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - _GLFWjoystick* js = _glfw.joysticks + jid; - if (js->connected) - closeJoystick(js); - } - - regfree(&_glfw.linjs.regex); - - if (_glfw.linjs.inotify > 0) - { - if (_glfw.linjs.watch > 0) - inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch); - - close(_glfw.linjs.inotify); - } -} - -void _glfwDetectJoystickConnectionLinux(void) -{ - if (_glfw.linjs.inotify <= 0) - return; - - ssize_t offset = 0; - char buffer[16384]; - const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer)); - - while (size > offset) - { - regmatch_t match; - const struct inotify_event* e = (struct inotify_event*) (buffer + offset); - - offset += sizeof(struct inotify_event) + e->len; - - if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0) - continue; - - char path[PATH_MAX]; - snprintf(path, sizeof(path), "/dev/input/%s", e->name); - - if (e->mask & (IN_CREATE | IN_ATTRIB)) - openJoystickDevice(path); - else if (e->mask & IN_DELETE) - { - for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) - { - closeJoystick(_glfw.joysticks + jid); - break; - } - } - } - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) -{ - // Read all queued events (non-blocking) - for (;;) - { - struct input_event e; - - errno = 0; - if (read(js->linjs.fd, &e, sizeof(e)) < 0) - { - // Reset the joystick slot if the device was disconnected - if (errno == ENODEV) - closeJoystick(js); - - break; - } - - if (e.type == EV_SYN) - { - if (e.code == SYN_DROPPED) - _glfw.linjs.dropped = GLFW_TRUE; - else if (e.code == SYN_REPORT) - { - _glfw.linjs.dropped = GLFW_FALSE; - pollAbsState(js); - } - } - - if (_glfw.linjs.dropped) - continue; - - if (e.type == EV_KEY) - handleKeyEvent(js, e.code, e.value); - else if (e.type == EV_ABS) - handleAbsEvent(js, e.code, e.value); - } - - return js->connected; -} - -void _glfwPlatformUpdateGamepadGUID(char* guid) -{ -} - diff --git a/libs/zglfw/libs/glfw/src/linux_joystick.h b/libs/zglfw/libs/glfw/src/linux_joystick.h deleted file mode 100644 index 25a2a2eeb..000000000 --- a/libs/zglfw/libs/glfw/src/linux_joystick.h +++ /dev/null @@ -1,63 +0,0 @@ -//======================================================================== -// GLFW 3.3 Linux - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014 Jonas Ã…dahl -// -// 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. -// -//======================================================================== - -#include -#include -#include - -#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs - -#define _GLFW_PLATFORM_MAPPING_NAME "Linux" -#define GLFW_BUILD_LINUX_MAPPINGS - -// Linux-specific joystick data -// -typedef struct _GLFWjoystickLinux -{ - int fd; - char path[PATH_MAX]; - int keyMap[KEY_CNT - BTN_MISC]; - int absMap[ABS_CNT]; - struct input_absinfo absInfo[ABS_CNT]; - int hats[4][2]; -} _GLFWjoystickLinux; - -// Linux-specific joystick API data -// -typedef struct _GLFWlibraryLinux -{ - int inotify; - int watch; - regex_t regex; - GLFWbool dropped; -} _GLFWlibraryLinux; - - -GLFWbool _glfwInitJoysticksLinux(void); -void _glfwTerminateJoysticksLinux(void); -void _glfwDetectJoystickConnectionLinux(void); - diff --git a/libs/zglfw/libs/glfw/src/mappings.h b/libs/zglfw/libs/glfw/src/mappings.h deleted file mode 100644 index 11853a0ae..000000000 --- a/libs/zglfw/libs/glfw/src/mappings.h +++ /dev/null @@ -1,1001 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2006-2018 Camilla Löwy -// -// 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. -// -//======================================================================== -// As mappings.h.in, this file is used by CMake to produce the mappings.h -// header file. If you are adding a GLFW specific gamepad mapping, this is -// where to put it. -//======================================================================== -// As mappings.h, this provides all pre-defined gamepad mappings, including -// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad -// mappings not specific to GLFW should be submitted to SDL_GameControllerDB. -// This file can be re-generated from mappings.h.in and the upstream -// gamecontrollerdb.txt with the 'update_mappings' CMake target. -//======================================================================== - -// All gamepad mappings not labeled GLFW are copied from the -// SDL_GameControllerDB project under the following license: -// -// Simple DirectMedia Layer -// Copyright (C) 1997-2013 Sam Lantinga -// -// 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. - -const char* _glfwDefaultMappings[] = -{ -#if defined(GLFW_BUILD_WIN32_MAPPINGS) -"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,", -"03000000c82d00002038000000000000,8bitdo,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000951000000000000,8BitDo Dogbone Modkit,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,", -"03000000c82d000011ab000000000000,8BitDo F30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000151000000000000,8BitDo M30 ModKit,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00000451000000000000,8BitDo N30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,start:b11,platform:Windows,", -"03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000360000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00002867000000000000,8BitDo S30 Modkit,a:b0,b:b1,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00003028000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000351000000000000,8BitDo SN30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000121000000000000,8BitDo SN30 Pro for Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000031000000000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", -"03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", -"03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", -"030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", -"03000000c01100000355000011010000,ACRUX USB GAME PAD,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"030000006f0e00001413000000000000,Afterglow,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00001401000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000d62000001d57000000000000,Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,", -"03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,platform:Windows,", -"03000000ef0500000300000000000000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,", -"03000000d6200000e557000000000000,Batarang,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,", -"030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bc2000000055000000000000,Betop BFM Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bc2000006321000000000000,BETOP CONTROLLER,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000790000000700000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", -"03000000808300000300000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", -"030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", -"03000000120c0000210e000000000000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,", -"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000457500000401000000000000,Cobra,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"030000005e040000a102000000000000,Controller (Xbox 360 Wireless Receiver for Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"030000005e040000ff02000000000000,Controller (Xbox One For Windows) - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"030000005e040000ea02000000000000,Controller (Xbox One For Windows) - Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,", -"03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000007d0400000840000000000000,Destroyer Tiltpad,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows,", -"03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bd12000002e0000000000000,Dual USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,", -"030000008f0e00000910000000000000,DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,", -"030000006f0e00003001000000000000,EA SPORTS PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", -"03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", -"03000000120c0000f61c000000000000,Elite,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", -"03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000006f0e00008401000000000000,Faceoff Deluxe+ Audio Wired Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00008001000000000000,Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", -"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,", -"03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,", -"03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,", -"03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"03000000ac0500003d03000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000ac0500004d04000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000009b2800003200000000000000,GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,", -"030000009b2800006000000000000000,GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,", -"030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", -"03000000300f00000b01000000000000,GGE909 Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"03000000f0250000c283000000000000,Gioteck,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000f0250000c383000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000f0250000c483000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"030000007d0400000540000000000000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000001008000001e1000000000000,Havit HV-G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,", -"03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", -"03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00009200000000000000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00001600000000007803,HORI Real Arcade Pro EX-SE (Xbox 360),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,", -"030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f0000c100000000000000,Horipad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,", -"030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,", -"03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,", -"03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", -"030000006f0e00002401000000000000,INJUSTICE FightStick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"03000000ac0500002c02000000000000,IPEGA,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000491900000304000000000000,Ipega PG-9087 - Bluetooth Gamepad,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,", -"030000006e0500000a20000000000000,JC-DUX60 ELECOM MMO Gamepad,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows,", -"030000006e0500000520000000000000,JC-P301U,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", -"030000006e0500000320000000000000,JC-U3613M (DInput),a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", -"030000006e0500000720000000000000,JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", -"030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,", -"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,", -"030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", -"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", -"03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows,", -"03000000242f00002d00000000000000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000242f00008a00000000000000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", -"03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", -"030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,", -"030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006d0400001ac2000000000000,Logitech Precision Gamepad,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000006d0400000ac2000000000000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows,", -"03000000380700006652000000000000,Mad Catz C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008433000000000000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008483000000000000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700006252000000000000,Mad Catz Micro C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,", -"030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", -"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,", -"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", -"0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,", -"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", -"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000790000002418000000000000,Mega Drive,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows,", -"03000000380700006382000000000000,MLG GamePad PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000c62400002a89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c62400002b89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c62400001a89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c62400001b89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000efbe0000edfe000000000000,Monect Virtual Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", -"03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", -"030000006b140000010c000000000000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000921200004b46000000000000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,", -"03000000790000004518000000000000,NEXILUX GAMECUBE Controller Adapter,platform:Windows,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,", -"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows,", -"03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", -"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows,", -"03000000550900001472000000000000,NVIDIA Controller v01.04,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows,", -"030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows,", -"03000000d620000013a7000000000000,NSW wired controller,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,", -"03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows,", -"03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", -"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,", -"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00000901000000000000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"030000004c050000da0c000000000000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", -"030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,", -"03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", -"03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", -"03000000666600006706000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,", -"030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,", -"030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows,", -"03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", -"030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000003807000056a8000000000000,PS3 RF pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000ff000000cb01000000000000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", -"03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,", -"03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", -"03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", -"03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,", -"03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"03000000321500000204000000000000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000321500000104000000000000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000321500000011000000000000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bd12000013d0000000000000,Retrolink USB SEGA Saturn Classic,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows,", -"0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", -"0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", -"030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000006b140000020d000000000000,Revolution Pro Controller 2(1/2),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000006b140000130d000000000000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", -"03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Windows,", -"03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b0,y:b1,platform:Windows,", -"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", -"03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,", -"03000000a30600002106000000000000,Saitek PS1000,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000a306000020f6000000000000,Saitek PS2700,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000300f00001101000000000000,Saitek Rumble Pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", -"03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,", -"0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", -"030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000a30c00002500000000000000,Sega Genesis Mini 3B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,", -"03000000a30c00002400000000000000,Sega Mega Drive Mini 6B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", -"03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,", -"03000000341a00000908000000000000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000008f0e00000800000000000000,SpeedLink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000d11800000094000000000000,Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,", -"03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,", -"03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows,", -"03000000790000001c18000000000000,STK-7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,", -"03000000d620000011a7000000000000,Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000457500002211000000000000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000004f04000007d0000000000000,T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000004f0400000ab1000000000000,T.16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows,", -"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", -"030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", -"030000004f04000023b3000000000000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000004f0400000ed0000000000000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,", -"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", -"03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", -"03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows,", -"03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"030000006e0500001320000000000000,U4113,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000300f00000701000000000000,USB 4-Axis 12-Button Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"03000000341a00002308000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000005509000000b4000000000000,USB gamepad,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,platform:Windows,", -"030000006b1400000203000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000790000000a00000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", -"03000000f0250000c183000000000000,USB gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000ff1100004133000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000790000001a18000000000000,Venom,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00000302000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00000702000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows,", -"030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000ac0500005b05000000000000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", -"03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"03000000790000004f18000000000000,ZD-T Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000120c0000101e000000000000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", -"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", -"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", -"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", -"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", -"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", -"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", -#endif // GLFW_BUILD_WIN32_MAPPINGS - -#if defined(GLFW_BUILD_COCOA_MAPPINGS) -"030000008f0e00000300000009010000,2In1 USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000c82d00005106000000010000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"030000003512000012ab000001000000,8BitDo NES30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000190000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00004028000000010000,8Bitdo SN30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Mac OS X,", -"03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000260000001000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000031000001000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", -"03000000ef0500000300000000020000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,", -"03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X,", -"03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,", -"03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,", -"03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000120c0000200e000000010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000120c0000210e000000010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,", -"03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,", -"03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", -"03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006f0e00000102000000000000,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000007d0400000540000001010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000280400000140000000020000,Gravis Gamepad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000008f0e00000300000007010000,GreenAsia Inc. USB Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,", -"030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f0000ee00000000010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,", -"03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", -"03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", -"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,", -"03000000242f00002d00000007010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", -"030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000006d04000018c2000000010000,Logitech RumblePad 2 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000380700008433000000010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000380700008483000000010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,", -"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X,", -"0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,", -"03000000790000000018000000010000,Mayflash Wii U Pro Controller Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", -"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", -"03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000005e0400002700000001010000,Microsoft SideWinder Plug & Play Game Pad,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,leftx:a0,lefty:a1,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,", -"03000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,", -"03000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000c62400002b89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000632500007505000000020000,NEOGEO mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X,", -"03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X,", -"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000d620000011a7000000020000,Nintendo Switch Core (Plus) Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000d620000011a7000010050000,Nintendo Switch PowerA Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", -"030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", -"03000000550900001472000025050000,NVIDIA Controller v01.04,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,", -"030000006f0e00000901000002010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,", -"030000004c050000da0c000000010000,Playstation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"030000004c0500003713000000010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", -"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", -"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", -"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"03000000321500000204000000010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000321500000104000000010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000321500000011000000010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,", -"030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,", -"0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000c6240000fefa000000000000,Rock Candy Gamepad for PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Mac OS X,", -"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,", -"03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,", -"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", -"0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X,", -"030000004c050000e60c000000010000,Sony DualSense,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,", -"030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,", -"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", -"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", -"050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,", -"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", -"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", -"03000000457500002211000000010000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,", -"030000004f0400000ed0000000020000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,", -"03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,", -"030000006f0e00000302000025040000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006f0e00000702000003060000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000791d00000103000009010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", -"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,", -"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,", -"030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000006f0e00000104000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000050b000003090000,Xbox Elite Wireless Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000c62400003a54000000000000,Xbox One PowerA Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000120c0000101e000000010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -#endif // GLFW_BUILD_COCOA_MAPPINGS - -#if defined(GLFW_BUILD_LINUX_MAPPINGS) -"03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00001038000000010000,8Bitdo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux,", -"03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,", -"05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,", -"03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00000060000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", -"030000003512000012ab000010010000,8Bitdo SFC30 GamePad,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,", -"05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00000260000011010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000202800000900000000010000,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00000031000011010000,8BitDo Wireless Adapter (DInput),a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e0400008e02000020010000,8BitDo Wireless Adapter (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"050000005e040000e002000030110000,8BitDo Zero 2 (XInput),a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,", -"05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", -"05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", -"03000000c01100000355000011010000,ACRUX USB GAME PAD,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00003901000013020000,Afterglow Prismatic Wired Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000007c1800000006000010010000,Alienware Dual Compatible Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", -"05000000491900000204000021000000,Amazon Fire Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,", -"05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", -"03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", -"03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", -"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,", -"05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,", -"03000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,", -"05000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,", -"03000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", -"05000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", -"03000000120c00000500000010010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,", -"03000000ef0500000300000000010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,", -"03000000c62400001b89000011010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000c21100000791000011010000,Be1 GC101 Controller 1.03 mode,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000c31100000791000011010000,Be1 GC101 GAMEPAD 1.03 mode,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e0400008e02000003030000,Be1 GC101 Xbox 360 Controller mode,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"05000000bc2000000055000001000000,BETOP AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,", -"03000000120c0000200e000011010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000120c0000210e000011010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"03000000ffff0000ffff000000010000,Chinese-made Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", -"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux,", -"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,", -"03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", -"03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,", -"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,", -"030000004f04000004b3000010010000,Dual Power 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", -"030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", -"03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000008f0e00000800000010010000,Gasia Co. Ltd PS(R) Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000007d0400000540000000010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,", -"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", -"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,", -"03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"03000000632500002605000010010000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,", -"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f0000c100000011010000,HORI CO. LTD. HORIPAD S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00008500000010010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00008600000002010000,Hori Fighting Commander,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f0000aa00000011010000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000000d0f0000d800000072056800,HORI Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", -"030000000d0f00001600000000010000,Hori Real Arcade Pro.EX-SE (Xbox 360),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,", -"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f0000ee00000011010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,", -"03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,", -"050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", -"03000000d80400008200000003000000,IMS PCU#0 Gamepad Interface,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,", -"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,", -"0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000632500007505000011010000,Ipega PG-9099 - Bluetooth Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,", -"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", -"03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", -"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", -"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,", -"050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,", -"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,", -"050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,", -"03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000242f00002d00000011010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000242f00008a00000011010000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,", -"030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d0400001ec2000019200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d0400000ac2000010010000,Logitech Inc. WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux,", -"030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,", -"050000004d4f435554452d3035305800,M54-PC,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,", -"03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700008433000011010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700008483000011010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000242f0000f700000001010000,Magic-S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000120c00000500000000010000,Manta Dualshock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", -"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", -"03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", -"03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,", -"0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000d620000010a7000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", -"03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", -"030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,", -"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"050000005e040000050b000003090000,Microsoft X-Box One Elite 2 pad,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000dd02000003020000,Microsoft X-Box One pad (Firmware 2015),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", -"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", -"030000005e040000000b000008040000,Microsoft Xbox One Elite 2 pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000ea02000008040000,Microsoft Xbox One S pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c62400001a53000000010000,Mini PE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,", -"05000000d6200000e589000001000000,Moga 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", -"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", -"05000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", -"03000000c62400002b89000011010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"05000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"05000000c62400001a89000000010000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", -"030000006b140000010c000010010000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000790000004518000010010000,NEXILUX GAMECUBE Controller Adapter,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,platform:Linux,", -"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,", -"060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", -"060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", -"030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux,", -"03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux,", -"050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b9,b:b8,back:b5,leftshoulder:b2,leftstick:b6,leftx:a1,lefty:a0~,rightshoulder:b4,start:b0,x:b7,y:b10,platform:Linux,", -"030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", -"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", -"050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0~,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux,", -"050000007e0500001720000001000000,Nintendo Switch SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,", -"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", -"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,", -"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", -"03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,", -"05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,", -"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"19000000010000000100000001010000,odroidgo2_joypad,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux,", -"19000000010000000200000011000000,odroidgo2_joypad_v11,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux,", -"030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", -"03000000c0160000dc27000001010000,OnyxSoft Dual JoyDivision,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:Linux,", -"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", -"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", -"03000000830500005020000010010000,Padix Co. Ltd. Rockfire PSX/USB Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux,", -"03000000790000001c18000011010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000006f0e0000b802000001010000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e0000b802000013020000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00008001000011010000,PDP CO. LTD. Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00008701000011010000,PDP Rock Candy Wired Controller for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000006f0e00000901000011010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000006f0e0000a802000023020000,PDP Wired Controller for Xbox One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000006f0e00008501000011010000,PDP Wired Fight Pad Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"0500000049190000030400001b010000,PG-9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"05000000491900000204000000000000,PG-9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000004c050000da0c000011010000,Playstation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", -"030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", -"03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000d62000000228000001010000,PowerA Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c62400001a58000001010000,PowerA Xbox One Cabled,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c62400001a54000001010000,PowerA Xbox One Mini Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", -"03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", -"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", -"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", -"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", -"050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", -"060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", -"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000ff000000cb01000010010000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,", -"03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,", -"030000009b2800004200000001010000,Raphnet Technologies Dual NES to USB v2.0,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,", -"030000009b2800003200000001010000,Raphnet Technologies GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,", -"030000009b2800006000000001010000,Raphnet Technologies GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,", -"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,", -"030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000321500000204000011010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000321500000810000011010000,Razer Panthera Evo Arcade Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000321500000011000011010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", -"050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", -"0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", -"0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,", -"0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", -"030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", -"03000000a30600001005000000010000,Saitek P150,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,platform:Linux,", -"03000000a30600000701000000010000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Linux,", -"03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b0,y:b1,platform:Linux,", -"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,", -"03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", -"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,", -"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,", -"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", -"03000000a306000020f6000011010000,Saitek PS2700 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", -"03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux,", -"03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005f140000c501000010010000,SHANWAN Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000004c050000e60c000011810000,Sony DualSense,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"050000004c050000e60c000000810000,Sony DualSense ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", -"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000d11800000094000011010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", -"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", -"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", -"03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,", -"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", -"03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,", -"03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", -"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", -"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", -"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000003b07000004a1000000010000,Suncom SFX Plus for USB,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,", -"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", -"0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", -"03000000457500002211000010010000,SZMY-POWER CO. LTD. GAMEPAD,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000008f0e00000d31000010010000,SZMY-POWER CO. LTD. GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000008f0e00001431000010010000,SZMY-POWER CO. LTD. PS3 gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", -"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", -"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000004f0400000ed0000011010000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,platform:Linux,", -"030000004f04000003b3000010010000,Thrustmaster Firestorm Dual Analog 2,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b9,rightx:a2,righty:a3,x:b1,y:b3,platform:Linux,", -"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,", -"030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000004f04000007d0000000010000,Thrustmaster T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", -"03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", -"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,", -"030000005e0400008e02000070050000,Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c01100000591000011010000,Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", -"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", -"03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,", -"03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,", -"030000006f0e00000302000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000006f0e00000702000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", -"03000000791d00000103000010010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"050000000d0f0000f600000001000000,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,", -"030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e040000ea02000000000000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e040000ea02000001030000,Xbox One Wireless Controller (Model 1708),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e040000120b000005050000,XBox Series pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400008e02000000010000,xbox360 Wireless EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"03000000ac0500005b05000010010000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,", -"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,", -"03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000120c0000101e000011010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -#endif // GLFW_BUILD_LINUX_MAPPINGS -}; - diff --git a/libs/zglfw/libs/glfw/src/monitor.c b/libs/zglfw/libs/glfw/src/monitor.c deleted file mode 100644 index 2601d11b3..000000000 --- a/libs/zglfw/libs/glfw/src/monitor.c +++ /dev/null @@ -1,542 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include -#include -#include - - -// Lexically compare video modes, used by qsort -// -static int compareVideoModes(const void* fp, const void* sp) -{ - const GLFWvidmode* fm = fp; - const GLFWvidmode* sm = sp; - const int fbpp = fm->redBits + fm->greenBits + fm->blueBits; - const int sbpp = sm->redBits + sm->greenBits + sm->blueBits; - const int farea = fm->width * fm->height; - const int sarea = sm->width * sm->height; - - // First sort on color bits per pixel - if (fbpp != sbpp) - return fbpp - sbpp; - - // Then sort on screen area - if (farea != sarea) - return farea - sarea; - - // Then sort on width - if (fm->width != sm->width) - return fm->width - sm->width; - - // Lastly sort on refresh rate - return fm->refreshRate - sm->refreshRate; -} - -// Retrieves the available modes for the specified monitor -// -static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) -{ - int modeCount; - GLFWvidmode* modes; - - if (monitor->modes) - return GLFW_TRUE; - - modes = _glfwPlatformGetVideoModes(monitor, &modeCount); - if (!modes) - return GLFW_FALSE; - - qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes); - - free(monitor->modes); - monitor->modes = modes; - monitor->modeCount = modeCount; - - return GLFW_TRUE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW event API ////// -////////////////////////////////////////////////////////////////////////// - -// Notifies shared code of a monitor connection or disconnection -// -void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) -{ - if (action == GLFW_CONNECTED) - { - _glfw.monitorCount++; - _glfw.monitors = - realloc(_glfw.monitors, sizeof(_GLFWmonitor*) * _glfw.monitorCount); - - if (placement == _GLFW_INSERT_FIRST) - { - memmove(_glfw.monitors + 1, - _glfw.monitors, - ((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*)); - _glfw.monitors[0] = monitor; - } - else - _glfw.monitors[_glfw.monitorCount - 1] = monitor; - } - else if (action == GLFW_DISCONNECTED) - { - int i; - _GLFWwindow* window; - - for (window = _glfw.windowListHead; window; window = window->next) - { - if (window->monitor == monitor) - { - int width, height, xoff, yoff; - _glfwPlatformGetWindowSize(window, &width, &height); - _glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0); - _glfwPlatformGetWindowFrameSize(window, &xoff, &yoff, NULL, NULL); - _glfwPlatformSetWindowPos(window, xoff, yoff); - } - } - - for (i = 0; i < _glfw.monitorCount; i++) - { - if (_glfw.monitors[i] == monitor) - { - _glfw.monitorCount--; - memmove(_glfw.monitors + i, - _glfw.monitors + i + 1, - ((size_t) _glfw.monitorCount - i) * sizeof(_GLFWmonitor*)); - break; - } - } - } - - if (_glfw.callbacks.monitor) - _glfw.callbacks.monitor((GLFWmonitor*) monitor, action); - - if (action == GLFW_DISCONNECTED) - _glfwFreeMonitor(monitor); -} - -// Notifies shared code that a full screen window has acquired or released -// a monitor -// -void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) -{ - monitor->window = window; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Allocates and returns a monitor object with the specified name and dimensions -// -_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) -{ - _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor)); - monitor->widthMM = widthMM; - monitor->heightMM = heightMM; - - strncpy(monitor->name, name, sizeof(monitor->name) - 1); - - return monitor; -} - -// Frees a monitor object and any data associated with it -// -void _glfwFreeMonitor(_GLFWmonitor* monitor) -{ - if (monitor == NULL) - return; - - _glfwPlatformFreeMonitor(monitor); - - _glfwFreeGammaArrays(&monitor->originalRamp); - _glfwFreeGammaArrays(&monitor->currentRamp); - - free(monitor->modes); - free(monitor); -} - -// Allocates red, green and blue value arrays of the specified size -// -void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) -{ - ramp->red = calloc(size, sizeof(unsigned short)); - ramp->green = calloc(size, sizeof(unsigned short)); - ramp->blue = calloc(size, sizeof(unsigned short)); - ramp->size = size; -} - -// Frees the red, green and blue value arrays and clears the struct -// -void _glfwFreeGammaArrays(GLFWgammaramp* ramp) -{ - free(ramp->red); - free(ramp->green); - free(ramp->blue); - - memset(ramp, 0, sizeof(GLFWgammaramp)); -} - -// Chooses the video mode most closely matching the desired one -// -const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, - const GLFWvidmode* desired) -{ - int i; - unsigned int sizeDiff, leastSizeDiff = UINT_MAX; - unsigned int rateDiff, leastRateDiff = UINT_MAX; - unsigned int colorDiff, leastColorDiff = UINT_MAX; - const GLFWvidmode* current; - const GLFWvidmode* closest = NULL; - - if (!refreshVideoModes(monitor)) - return NULL; - - for (i = 0; i < monitor->modeCount; i++) - { - current = monitor->modes + i; - - colorDiff = 0; - - if (desired->redBits != GLFW_DONT_CARE) - colorDiff += abs(current->redBits - desired->redBits); - if (desired->greenBits != GLFW_DONT_CARE) - colorDiff += abs(current->greenBits - desired->greenBits); - if (desired->blueBits != GLFW_DONT_CARE) - colorDiff += abs(current->blueBits - desired->blueBits); - - sizeDiff = abs((current->width - desired->width) * - (current->width - desired->width) + - (current->height - desired->height) * - (current->height - desired->height)); - - if (desired->refreshRate != GLFW_DONT_CARE) - rateDiff = abs(current->refreshRate - desired->refreshRate); - else - rateDiff = UINT_MAX - current->refreshRate; - - if ((colorDiff < leastColorDiff) || - (colorDiff == leastColorDiff && sizeDiff < leastSizeDiff) || - (colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff)) - { - closest = current; - leastSizeDiff = sizeDiff; - leastRateDiff = rateDiff; - leastColorDiff = colorDiff; - } - } - - return closest; -} - -// Performs lexical comparison between two @ref GLFWvidmode structures -// -int _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm) -{ - return compareVideoModes(fm, sm); -} - -// Splits a color depth into red, green and blue bit depths -// -void _glfwSplitBPP(int bpp, int* red, int* green, int* blue) -{ - int delta; - - // We assume that by 32 the user really meant 24 - if (bpp == 32) - bpp = 24; - - // Convert "bits per pixel" to red, green & blue sizes - - *red = *green = *blue = bpp / 3; - delta = bpp - (*red * 3); - if (delta >= 1) - *green = *green + 1; - - if (delta == 2) - *red = *red + 1; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW public API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI GLFWmonitor** glfwGetMonitors(int* count) -{ - assert(count != NULL); - - *count = 0; - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - *count = _glfw.monitorCount; - return (GLFWmonitor**) _glfw.monitors; -} - -GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (!_glfw.monitorCount) - return NULL; - - return (GLFWmonitor*) _glfw.monitors[0]; -} - -GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - - if (xpos) - *xpos = 0; - if (ypos) - *ypos = 0; - - _GLFW_REQUIRE_INIT(); - - _glfwPlatformGetMonitorPos(monitor, xpos, ypos); -} - -GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, - int* xpos, int* ypos, - int* width, int* height) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - - if (xpos) - *xpos = 0; - if (ypos) - *ypos = 0; - if (width) - *width = 0; - if (height) - *height = 0; - - _GLFW_REQUIRE_INIT(); - - _glfwPlatformGetMonitorWorkarea(monitor, xpos, ypos, width, height); -} - -GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - - if (widthMM) - *widthMM = 0; - if (heightMM) - *heightMM = 0; - - _GLFW_REQUIRE_INIT(); - - if (widthMM) - *widthMM = monitor->widthMM; - if (heightMM) - *heightMM = monitor->heightMM; -} - -GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle, - float* xscale, float* yscale) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - - if (xscale) - *xscale = 0.f; - if (yscale) - *yscale = 0.f; - - _GLFW_REQUIRE_INIT(); - _glfwPlatformGetMonitorContentScale(monitor, xscale, yscale); -} - -GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return monitor->name; -} - -GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - - _GLFW_REQUIRE_INIT(); - monitor->userPointer = pointer; -} - -GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return monitor->userPointer; -} - -GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(_glfw.callbacks.monitor, cbfun); - return cbfun; -} - -GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - assert(count != NULL); - - *count = 0; - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (!refreshVideoModes(monitor)) - return NULL; - - *count = monitor->modeCount; - return monitor->modes; -} - -GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - _glfwPlatformGetVideoMode(monitor, &monitor->currentMode); - return &monitor->currentMode; -} - -GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) -{ - unsigned int i; - unsigned short* values; - GLFWgammaramp ramp; - const GLFWgammaramp* original; - assert(handle != NULL); - assert(gamma > 0.f); - assert(gamma <= FLT_MAX); - - _GLFW_REQUIRE_INIT(); - - if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX) - { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma); - return; - } - - original = glfwGetGammaRamp(handle); - if (!original) - return; - - values = calloc(original->size, sizeof(unsigned short)); - - for (i = 0; i < original->size; i++) - { - float value; - - // Calculate intensity - value = i / (float) (original->size - 1); - // Apply gamma curve - value = powf(value, 1.f / gamma) * 65535.f + 0.5f; - // Clamp to value range - value = _glfw_fminf(value, 65535.f); - - values[i] = (unsigned short) value; - } - - ramp.red = values; - ramp.green = values; - ramp.blue = values; - ramp.size = original->size; - - glfwSetGammaRamp(handle, &ramp); - free(values); -} - -GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - _glfwFreeGammaArrays(&monitor->currentRamp); - if (!_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp)) - return NULL; - - return &monitor->currentRamp; -} - -GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor != NULL); - assert(ramp != NULL); - assert(ramp->size > 0); - assert(ramp->red != NULL); - assert(ramp->green != NULL); - assert(ramp->blue != NULL); - - _GLFW_REQUIRE_INIT(); - - if (ramp->size <= 0) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid gamma ramp size %i", - ramp->size); - return; - } - - if (!monitor->originalRamp.size) - { - if (!_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp)) - return; - } - - _glfwPlatformSetGammaRamp(monitor, ramp); -} - diff --git a/libs/zglfw/libs/glfw/src/nsgl_context.h b/libs/zglfw/libs/glfw/src/nsgl_context.h deleted file mode 100644 index 010ce4dc8..000000000 --- a/libs/zglfw/libs/glfw/src/nsgl_context.h +++ /dev/null @@ -1,66 +0,0 @@ -//======================================================================== -// GLFW 3.3 macOS - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2009-2019 Camilla Löwy -// -// 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. -// -//======================================================================== - -// NOTE: Many Cocoa enum values have been renamed and we need to build across -// SDK versions where one is unavailable or deprecated. -// We use the newer names in code and replace them with the older names if -// the base SDK does not provide the newer names. - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 101400 - #define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval - #define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity -#endif - -#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl - -#include - - -// NSGL-specific per-context data -// -typedef struct _GLFWcontextNSGL -{ - id pixelFormat; - id object; -} _GLFWcontextNSGL; - -// NSGL-specific global data -// -typedef struct _GLFWlibraryNSGL -{ - // dlopen handle for OpenGL.framework (for glfwGetProcAddress) - CFBundleRef framework; -} _GLFWlibraryNSGL; - - -GLFWbool _glfwInitNSGL(void); -void _glfwTerminateNSGL(void); -GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); -void _glfwDestroyContextNSGL(_GLFWwindow* window); - diff --git a/libs/zglfw/libs/glfw/src/nsgl_context.m b/libs/zglfw/libs/glfw/src/nsgl_context.m deleted file mode 100644 index 78d688c49..000000000 --- a/libs/zglfw/libs/glfw/src/nsgl_context.m +++ /dev/null @@ -1,376 +0,0 @@ -//======================================================================== -// GLFW 3.3 macOS - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2009-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include -#include - -static void makeContextCurrentNSGL(_GLFWwindow* window) -{ - @autoreleasepool { - - if (window) - [window->context.nsgl.object makeCurrentContext]; - else - [NSOpenGLContext clearCurrentContext]; - - _glfwPlatformSetTls(&_glfw.contextSlot, window); - - } // autoreleasepool -} - -static void swapBuffersNSGL(_GLFWwindow* window) -{ - @autoreleasepool { - - // HACK: Simulate vsync with usleep as NSGL swap interval does not apply to - // windows with a non-visible occlusion state - if (window->ns.occluded) - { - int interval = 0; - [window->context.nsgl.object getValues:&interval - forParameter:NSOpenGLContextParameterSwapInterval]; - - if (interval > 0) - { - const double framerate = 60.0; - const uint64_t frequency = _glfwPlatformGetTimerFrequency(); - const uint64_t value = _glfwPlatformGetTimerValue(); - - const double elapsed = value / (double) frequency; - const double period = 1.0 / framerate; - const double delay = period - fmod(elapsed, period); - - usleep(floorl(delay * 1e6)); - } - } - - [window->context.nsgl.object flushBuffer]; - - } // autoreleasepool -} - -static void swapIntervalNSGL(int interval) -{ - @autoreleasepool { - - _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); - if (window) - { - [window->context.nsgl.object setValues:&interval - forParameter:NSOpenGLContextParameterSwapInterval]; - } - - } // autoreleasepool -} - -static int extensionSupportedNSGL(const char* extension) -{ - // There are no NSGL extensions - return GLFW_FALSE; -} - -static GLFWglproc getProcAddressNSGL(const char* procname) -{ - CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, - procname, - kCFStringEncodingASCII); - - GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework, - symbolName); - - CFRelease(symbolName); - - return symbol; -} - -static void destroyContextNSGL(_GLFWwindow* window) -{ - @autoreleasepool { - - [window->context.nsgl.pixelFormat release]; - window->context.nsgl.pixelFormat = nil; - - [window->context.nsgl.object release]; - window->context.nsgl.object = nil; - - } // autoreleasepool -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialize OpenGL support -// -GLFWbool _glfwInitNSGL(void) -{ - if (_glfw.nsgl.framework) - return GLFW_TRUE; - - _glfw.nsgl.framework = - CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); - if (_glfw.nsgl.framework == NULL) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "NSGL: Failed to locate OpenGL framework"); - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -// Terminate OpenGL support -// -void _glfwTerminateNSGL(void) -{ -} - -// Create the OpenGL context -// -GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - if (ctxconfig->client == GLFW_OPENGL_ES_API) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "NSGL: OpenGL ES is not available on macOS"); - return GLFW_FALSE; - } - - if (ctxconfig->major > 2) - { - if (ctxconfig->major == 3 && ctxconfig->minor < 2) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above"); - return GLFW_FALSE; - } - - if (!ctxconfig->forward || ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of macOS only supports forward-compatible core profile contexts for OpenGL 3.2 and above"); - return GLFW_FALSE; - } - } - - // Context robustness modes (GL_KHR_robustness) are not yet supported by - // macOS but are not a hard constraint, so ignore and continue - - // Context release behaviors (GL_KHR_context_flush_control) are not yet - // supported by macOS but are not a hard constraint, so ignore and continue - - // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not - // a hard constraint, so ignore and continue - - // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but - // are not a hard constraint, so ignore and continue - -#define addAttrib(a) \ -{ \ - assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ - attribs[index++] = a; \ -} -#define setAttrib(a, v) { addAttrib(a); addAttrib(v); } - - NSOpenGLPixelFormatAttribute attribs[40]; - int index = 0; - - addAttrib(NSOpenGLPFAAccelerated); - addAttrib(NSOpenGLPFAClosestPolicy); - - if (ctxconfig->nsgl.offline) - { - addAttrib(NSOpenGLPFAAllowOfflineRenderers); - // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in - // Info.plist for unbundled applications - // HACK: This assumes that NSOpenGLPixelFormat will remain - // a straightforward wrapper of its CGL counterpart - addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching); - } - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 - if (ctxconfig->major >= 4) - { - setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); - } - else -#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ - if (ctxconfig->major >= 3) - { - setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); - } - - if (ctxconfig->major <= 2) - { - if (fbconfig->auxBuffers != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); - - if (fbconfig->accumRedBits != GLFW_DONT_CARE && - fbconfig->accumGreenBits != GLFW_DONT_CARE && - fbconfig->accumBlueBits != GLFW_DONT_CARE && - fbconfig->accumAlphaBits != GLFW_DONT_CARE) - { - const int accumBits = fbconfig->accumRedBits + - fbconfig->accumGreenBits + - fbconfig->accumBlueBits + - fbconfig->accumAlphaBits; - - setAttrib(NSOpenGLPFAAccumSize, accumBits); - } - } - - if (fbconfig->redBits != GLFW_DONT_CARE && - fbconfig->greenBits != GLFW_DONT_CARE && - fbconfig->blueBits != GLFW_DONT_CARE) - { - int colorBits = fbconfig->redBits + - fbconfig->greenBits + - fbconfig->blueBits; - - // macOS needs non-zero color size, so set reasonable values - if (colorBits == 0) - colorBits = 24; - else if (colorBits < 15) - colorBits = 15; - - setAttrib(NSOpenGLPFAColorSize, colorBits); - } - - if (fbconfig->alphaBits != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); - - if (fbconfig->depthBits != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits); - - if (fbconfig->stencilBits != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits); - - if (fbconfig->stereo) - { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "NSGL: Stereo rendering is deprecated"); - return GLFW_FALSE; -#else - addAttrib(NSOpenGLPFAStereo); -#endif - } - - if (fbconfig->doublebuffer) - addAttrib(NSOpenGLPFADoubleBuffer); - - if (fbconfig->samples != GLFW_DONT_CARE) - { - if (fbconfig->samples == 0) - { - setAttrib(NSOpenGLPFASampleBuffers, 0); - } - else - { - setAttrib(NSOpenGLPFASampleBuffers, 1); - setAttrib(NSOpenGLPFASamples, fbconfig->samples); - } - } - - // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB - // framebuffer, so there's no need (and no way) to request it - - addAttrib(0); - -#undef addAttrib -#undef setAttrib - - window->context.nsgl.pixelFormat = - [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; - if (window->context.nsgl.pixelFormat == nil) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "NSGL: Failed to find a suitable pixel format"); - return GLFW_FALSE; - } - - NSOpenGLContext* share = nil; - - if (ctxconfig->share) - share = ctxconfig->share->context.nsgl.object; - - window->context.nsgl.object = - [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat - shareContext:share]; - if (window->context.nsgl.object == nil) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: Failed to create OpenGL context"); - return GLFW_FALSE; - } - - if (fbconfig->transparent) - { - GLint opaque = 0; - [window->context.nsgl.object setValues:&opaque - forParameter:NSOpenGLContextParameterSurfaceOpacity]; - } - - [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina]; - - [window->context.nsgl.object setView:window->ns.view]; - - window->context.makeCurrent = makeContextCurrentNSGL; - window->context.swapBuffers = swapBuffersNSGL; - window->context.swapInterval = swapIntervalNSGL; - window->context.extensionSupported = extensionSupportedNSGL; - window->context.getProcAddress = getProcAddressNSGL; - window->context.destroy = destroyContextNSGL; - - return GLFW_TRUE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(nil); - - if (window->context.source != GLFW_NATIVE_CONTEXT_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return nil; - } - - return window->context.nsgl.object; -} - diff --git a/libs/zglfw/libs/glfw/src/null_init.c b/libs/zglfw/libs/glfw/src/null_init.c deleted file mode 100644 index 569bc8c0a..000000000 --- a/libs/zglfw/libs/glfw/src/null_init.c +++ /dev/null @@ -1,52 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2016 Google Inc. -// Copyright (c) 2016-2017 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformInit(void) -{ - _glfwInitTimerPOSIX(); - return GLFW_TRUE; -} - -void _glfwPlatformTerminate(void) -{ - _glfwTerminateOSMesa(); -} - -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " null OSMesa"; -} - diff --git a/libs/zglfw/libs/glfw/src/null_joystick.c b/libs/zglfw/libs/glfw/src/null_joystick.c deleted file mode 100644 index 000faf289..000000000 --- a/libs/zglfw/libs/glfw/src/null_joystick.c +++ /dev/null @@ -1,44 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2016-2017 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) -{ - return GLFW_FALSE; -} - -void _glfwPlatformUpdateGamepadGUID(char* guid) -{ -} - diff --git a/libs/zglfw/libs/glfw/src/null_joystick.h b/libs/zglfw/libs/glfw/src/null_joystick.h deleted file mode 100644 index 9307ae88e..000000000 --- a/libs/zglfw/libs/glfw/src/null_joystick.h +++ /dev/null @@ -1,31 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#define _GLFW_PLATFORM_JOYSTICK_STATE struct { int dummyJoystick; } -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } - -#define _GLFW_PLATFORM_MAPPING_NAME "" - diff --git a/libs/zglfw/libs/glfw/src/null_monitor.c b/libs/zglfw/libs/glfw/src/null_monitor.c deleted file mode 100644 index 4514dae90..000000000 --- a/libs/zglfw/libs/glfw/src/null_monitor.c +++ /dev/null @@ -1,77 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2016 Google Inc. -// Copyright (c) 2016-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) -{ -} - -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) -{ -} - -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) -{ - if (xscale) - *xscale = 1.f; - if (yscale) - *yscale = 1.f; -} - -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, - int* xpos, int* ypos, - int* width, int* height) -{ -} - -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) -{ - return NULL; -} - -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) -{ -} - -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) -{ - return GLFW_FALSE; -} - -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) -{ -} - diff --git a/libs/zglfw/libs/glfw/src/null_platform.h b/libs/zglfw/libs/glfw/src/null_platform.h deleted file mode 100644 index 708975d1d..000000000 --- a/libs/zglfw/libs/glfw/src/null_platform.h +++ /dev/null @@ -1,62 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2016 Google Inc. -// Copyright (c) 2016-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#include - -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null - -#define _GLFW_PLATFORM_CONTEXT_STATE struct { int dummyContext; } -#define _GLFW_PLATFORM_MONITOR_STATE struct { int dummyMonitor; } -#define _GLFW_PLATFORM_CURSOR_STATE struct { int dummyCursor; } -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE struct { int dummyLibraryWindow; } -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; } -#define _GLFW_EGL_CONTEXT_STATE struct { int dummyEGLContext; } -#define _GLFW_EGL_LIBRARY_CONTEXT_STATE struct { int dummyEGLLibraryContext; } - -#include "osmesa_context.h" -#include "posix_time.h" -#include "posix_thread.h" -#include "null_joystick.h" - -#if defined(_GLFW_WIN32) - #define _glfw_dlopen(name) LoadLibraryA(name) - #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) - #define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) -#else - #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) - #define _glfw_dlclose(handle) dlclose(handle) - #define _glfw_dlsym(handle, name) dlsym(handle, name) -#endif - -// Null-specific per-window data -// -typedef struct _GLFWwindowNull -{ - int width; - int height; -} _GLFWwindowNull; - diff --git a/libs/zglfw/libs/glfw/src/null_window.c b/libs/zglfw/libs/glfw/src/null_window.c deleted file mode 100644 index e61c2bdd3..000000000 --- a/libs/zglfw/libs/glfw/src/null_window.c +++ /dev/null @@ -1,335 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2016 Google Inc. -// Copyright (c) 2016-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - - -static int createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig) -{ - window->null.width = wndconfig->width; - window->null.height = wndconfig->height; - - return GLFW_TRUE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - if (!createNativeWindow(window, wndconfig)) - return GLFW_FALSE; - - if (ctxconfig->client != GLFW_NO_API) - { - if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API || - ctxconfig->source == GLFW_OSMESA_CONTEXT_API) - { - if (!_glfwInitOSMesa()) - return GLFW_FALSE; - if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - else - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Null: EGL not available"); - return GLFW_FALSE; - } - - if (!_glfwRefreshContextAttribs(window, ctxconfig)) - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -void _glfwPlatformDestroyWindow(_GLFWwindow* window) -{ - if (window->context.destroy) - window->context.destroy(window); -} - -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) -{ -} - -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, - const GLFWimage* images) -{ -} - -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) -{ -} - -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) -{ -} - -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) -{ -} - -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) -{ - if (width) - *width = window->null.width; - if (height) - *height = window->null.height; -} - -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) -{ - window->null.width = width; - window->null.height = height; -} - -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) -{ -} - -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n, int d) -{ -} - -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) -{ - if (width) - *width = window->null.width; - if (height) - *height = window->null.height; -} - -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) -{ -} - -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) -{ - if (xscale) - *xscale = 1.f; - if (yscale) - *yscale = 1.f; -} - -void _glfwPlatformIconifyWindow(_GLFWwindow* window) -{ -} - -void _glfwPlatformRestoreWindow(_GLFWwindow* window) -{ -} - -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) -{ -} - -int _glfwPlatformWindowMaximized(_GLFWwindow* window) -{ - return GLFW_FALSE; -} - -int _glfwPlatformWindowHovered(_GLFWwindow* window) -{ - return GLFW_FALSE; -} - -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) -{ - return GLFW_FALSE; -} - -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) -{ -} - -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) -{ -} - -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) -{ -} - -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) -{ - return 1.f; -} - -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) -{ -} - -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) -{ -} - -GLFWbool _glfwPlatformRawMouseMotionSupported(void) -{ - return GLFW_FALSE; -} - -void _glfwPlatformShowWindow(_GLFWwindow* window) -{ -} - - -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) -{ -} - -void _glfwPlatformUnhideWindow(_GLFWwindow* window) -{ -} - -void _glfwPlatformHideWindow(_GLFWwindow* window) -{ -} - -void _glfwPlatformFocusWindow(_GLFWwindow* window) -{ -} - -int _glfwPlatformWindowFocused(_GLFWwindow* window) -{ - return GLFW_FALSE; -} - -int _glfwPlatformWindowIconified(_GLFWwindow* window) -{ - return GLFW_FALSE; -} - -int _glfwPlatformWindowVisible(_GLFWwindow* window) -{ - return GLFW_FALSE; -} - -void _glfwPlatformPollEvents(void) -{ -} - -void _glfwPlatformWaitEvents(void) -{ -} - -void _glfwPlatformWaitEventsTimeout(double timeout) -{ -} - -void _glfwPlatformPostEmptyEvent(void) -{ -} - -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) -{ -} - -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) -{ -} - -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) -{ -} - -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) -{ - return GLFW_TRUE; -} - -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) -{ - return GLFW_TRUE; -} - -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) -{ -} - -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) -{ -} - -void _glfwPlatformSetClipboardString(const char* string) -{ -} - -const char* _glfwPlatformGetClipboardString(void) -{ - return NULL; -} - -const char* _glfwPlatformGetScancodeName(int scancode) -{ - return ""; -} - -int _glfwPlatformGetKeyScancode(int key) -{ - return -1; -} - -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) -{ -} - -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) -{ - return GLFW_FALSE; -} - -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) -{ - // This seems like the most appropriate error to return here - return VK_ERROR_INITIALIZATION_FAILED; -} - diff --git a/libs/zglfw/libs/glfw/src/osmesa_context.c b/libs/zglfw/libs/glfw/src/osmesa_context.c deleted file mode 100644 index 4072728bc..000000000 --- a/libs/zglfw/libs/glfw/src/osmesa_context.c +++ /dev/null @@ -1,386 +0,0 @@ -//======================================================================== -// GLFW 3.3 OSMesa - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2016 Google Inc. -// Copyright (c) 2016-2017 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include -#include -#include - -#include "internal.h" - - -static void makeContextCurrentOSMesa(_GLFWwindow* window) -{ - if (window) - { - int width, height; - _glfwPlatformGetFramebufferSize(window, &width, &height); - - // Check to see if we need to allocate a new buffer - if ((window->context.osmesa.buffer == NULL) || - (width != window->context.osmesa.width) || - (height != window->context.osmesa.height)) - { - free(window->context.osmesa.buffer); - - // Allocate the new buffer (width * height * 8-bit RGBA) - window->context.osmesa.buffer = calloc(4, (size_t) width * height); - window->context.osmesa.width = width; - window->context.osmesa.height = height; - } - - if (!OSMesaMakeCurrent(window->context.osmesa.handle, - window->context.osmesa.buffer, - GL_UNSIGNED_BYTE, - width, height)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "OSMesa: Failed to make context current"); - return; - } - } - - _glfwPlatformSetTls(&_glfw.contextSlot, window); -} - -static GLFWglproc getProcAddressOSMesa(const char* procname) -{ - return (GLFWglproc) OSMesaGetProcAddress(procname); -} - -static void destroyContextOSMesa(_GLFWwindow* window) -{ - if (window->context.osmesa.handle) - { - OSMesaDestroyContext(window->context.osmesa.handle); - window->context.osmesa.handle = NULL; - } - - if (window->context.osmesa.buffer) - { - free(window->context.osmesa.buffer); - window->context.osmesa.width = 0; - window->context.osmesa.height = 0; - } -} - -static void swapBuffersOSMesa(_GLFWwindow* window) -{ - // No double buffering on OSMesa -} - -static void swapIntervalOSMesa(int interval) -{ - // No swap interval on OSMesa -} - -static int extensionSupportedOSMesa(const char* extension) -{ - // OSMesa does not have extensions - return GLFW_FALSE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwInitOSMesa(void) -{ - int i; - const char* sonames[] = - { -#if defined(_GLFW_OSMESA_LIBRARY) - _GLFW_OSMESA_LIBRARY, -#elif defined(_WIN32) - "libOSMesa.dll", - "OSMesa.dll", -#elif defined(__APPLE__) - "libOSMesa.8.dylib", -#elif defined(__CYGWIN__) - "libOSMesa-8.so", -#elif defined(__OpenBSD__) || defined(__NetBSD__) - "libOSMesa.so", -#else - "libOSMesa.so.8", - "libOSMesa.so.6", -#endif - NULL - }; - - if (_glfw.osmesa.handle) - return GLFW_TRUE; - - for (i = 0; sonames[i]; i++) - { - _glfw.osmesa.handle = _glfw_dlopen(sonames[i]); - if (_glfw.osmesa.handle) - break; - } - - if (!_glfw.osmesa.handle) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found"); - return GLFW_FALSE; - } - - _glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt"); - _glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs"); - _glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext"); - _glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent"); - _glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer"); - _glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer"); - _glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress"); - - if (!_glfw.osmesa.CreateContextExt || - !_glfw.osmesa.DestroyContext || - !_glfw.osmesa.MakeCurrent || - !_glfw.osmesa.GetColorBuffer || - !_glfw.osmesa.GetDepthBuffer || - !_glfw.osmesa.GetProcAddress) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "OSMesa: Failed to load required entry points"); - - _glfwTerminateOSMesa(); - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -void _glfwTerminateOSMesa(void) -{ - if (_glfw.osmesa.handle) - { - _glfw_dlclose(_glfw.osmesa.handle); - _glfw.osmesa.handle = NULL; - } -} - -#define setAttrib(a, v) \ -{ \ - assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ - attribs[index++] = a; \ - attribs[index++] = v; \ -} - -GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - OSMesaContext share = NULL; - const int accumBits = fbconfig->accumRedBits + - fbconfig->accumGreenBits + - fbconfig->accumBlueBits + - fbconfig->accumAlphaBits; - - if (ctxconfig->client == GLFW_OPENGL_ES_API) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "OSMesa: OpenGL ES is not available on OSMesa"); - return GLFW_FALSE; - } - - if (ctxconfig->share) - share = ctxconfig->share->context.osmesa.handle; - - if (OSMesaCreateContextAttribs) - { - int index = 0, attribs[40]; - - setAttrib(OSMESA_FORMAT, OSMESA_RGBA); - setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); - setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); - setAttrib(OSMESA_ACCUM_BITS, accumBits); - - if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) - { - setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); - } - else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) - { - setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); - } - - if (ctxconfig->major != 1 || ctxconfig->minor != 0) - { - setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); - setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); - } - - if (ctxconfig->forward) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "OSMesa: Forward-compatible contexts not supported"); - return GLFW_FALSE; - } - - setAttrib(0, 0); - - window->context.osmesa.handle = - OSMesaCreateContextAttribs(attribs, share); - } - else - { - if (ctxconfig->profile) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "OSMesa: OpenGL profiles unavailable"); - return GLFW_FALSE; - } - - window->context.osmesa.handle = - OSMesaCreateContextExt(OSMESA_RGBA, - fbconfig->depthBits, - fbconfig->stencilBits, - accumBits, - share); - } - - if (window->context.osmesa.handle == NULL) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "OSMesa: Failed to create context"); - return GLFW_FALSE; - } - - window->context.makeCurrent = makeContextCurrentOSMesa; - window->context.swapBuffers = swapBuffersOSMesa; - window->context.swapInterval = swapIntervalOSMesa; - window->context.extensionSupported = extensionSupportedOSMesa; - window->context.getProcAddress = getProcAddressOSMesa; - window->context.destroy = destroyContextOSMesa; - - return GLFW_TRUE; -} - -#undef setAttrib - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width, - int* height, int* format, void** buffer) -{ - void* mesaBuffer; - GLint mesaWidth, mesaHeight, mesaFormat; - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - - if (window->context.source != GLFW_OSMESA_CONTEXT_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return GLFW_FALSE; - } - - if (!OSMesaGetColorBuffer(window->context.osmesa.handle, - &mesaWidth, &mesaHeight, - &mesaFormat, &mesaBuffer)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "OSMesa: Failed to retrieve color buffer"); - return GLFW_FALSE; - } - - if (width) - *width = mesaWidth; - if (height) - *height = mesaHeight; - if (format) - *format = mesaFormat; - if (buffer) - *buffer = mesaBuffer; - - return GLFW_TRUE; -} - -GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle, - int* width, int* height, - int* bytesPerValue, - void** buffer) -{ - void* mesaBuffer; - GLint mesaWidth, mesaHeight, mesaBytes; - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - - if (window->context.source != GLFW_OSMESA_CONTEXT_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return GLFW_FALSE; - } - - if (!OSMesaGetDepthBuffer(window->context.osmesa.handle, - &mesaWidth, &mesaHeight, - &mesaBytes, &mesaBuffer)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "OSMesa: Failed to retrieve depth buffer"); - return GLFW_FALSE; - } - - if (width) - *width = mesaWidth; - if (height) - *height = mesaHeight; - if (bytesPerValue) - *bytesPerValue = mesaBytes; - if (buffer) - *buffer = mesaBuffer; - - return GLFW_TRUE; -} - -GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (window->context.source != GLFW_OSMESA_CONTEXT_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return NULL; - } - - return window->context.osmesa.handle; -} - diff --git a/libs/zglfw/libs/glfw/src/osmesa_context.h b/libs/zglfw/libs/glfw/src/osmesa_context.h deleted file mode 100644 index 6462637f4..000000000 --- a/libs/zglfw/libs/glfw/src/osmesa_context.h +++ /dev/null @@ -1,92 +0,0 @@ -//======================================================================== -// GLFW 3.3 OSMesa - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2016 Google Inc. -// Copyright (c) 2016-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#define OSMESA_RGBA 0x1908 -#define OSMESA_FORMAT 0x22 -#define OSMESA_DEPTH_BITS 0x30 -#define OSMESA_STENCIL_BITS 0x31 -#define OSMESA_ACCUM_BITS 0x32 -#define OSMESA_PROFILE 0x33 -#define OSMESA_CORE_PROFILE 0x34 -#define OSMESA_COMPAT_PROFILE 0x35 -#define OSMESA_CONTEXT_MAJOR_VERSION 0x36 -#define OSMESA_CONTEXT_MINOR_VERSION 0x37 - -typedef void* OSMesaContext; -typedef void (*OSMESAproc)(void); - -typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); -typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); -typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); -typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); -typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); -typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); -typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); -#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt -#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs -#define OSMesaDestroyContext _glfw.osmesa.DestroyContext -#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent -#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer -#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer -#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress - -#define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa -#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa - - -// OSMesa-specific per-context data -// -typedef struct _GLFWcontextOSMesa -{ - OSMesaContext handle; - int width; - int height; - void* buffer; -} _GLFWcontextOSMesa; - -// OSMesa-specific global data -// -typedef struct _GLFWlibraryOSMesa -{ - void* handle; - - PFN_OSMesaCreateContextExt CreateContextExt; - PFN_OSMesaCreateContextAttribs CreateContextAttribs; - PFN_OSMesaDestroyContext DestroyContext; - PFN_OSMesaMakeCurrent MakeCurrent; - PFN_OSMesaGetColorBuffer GetColorBuffer; - PFN_OSMesaGetDepthBuffer GetDepthBuffer; - PFN_OSMesaGetProcAddress GetProcAddress; -} _GLFWlibraryOSMesa; - - -GLFWbool _glfwInitOSMesa(void); -void _glfwTerminateOSMesa(void); -GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); - diff --git a/libs/zglfw/libs/glfw/src/posix_thread.c b/libs/zglfw/libs/glfw/src/posix_thread.c deleted file mode 100644 index f1697dce3..000000000 --- a/libs/zglfw/libs/glfw/src/posix_thread.c +++ /dev/null @@ -1,105 +0,0 @@ -//======================================================================== -// GLFW 3.3 POSIX - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) -{ - assert(tls->posix.allocated == GLFW_FALSE); - - if (pthread_key_create(&tls->posix.key, NULL) != 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "POSIX: Failed to create context TLS"); - return GLFW_FALSE; - } - - tls->posix.allocated = GLFW_TRUE; - return GLFW_TRUE; -} - -void _glfwPlatformDestroyTls(_GLFWtls* tls) -{ - if (tls->posix.allocated) - pthread_key_delete(tls->posix.key); - memset(tls, 0, sizeof(_GLFWtls)); -} - -void* _glfwPlatformGetTls(_GLFWtls* tls) -{ - assert(tls->posix.allocated == GLFW_TRUE); - return pthread_getspecific(tls->posix.key); -} - -void _glfwPlatformSetTls(_GLFWtls* tls, void* value) -{ - assert(tls->posix.allocated == GLFW_TRUE); - pthread_setspecific(tls->posix.key, value); -} - -GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex) -{ - assert(mutex->posix.allocated == GLFW_FALSE); - - if (pthread_mutex_init(&mutex->posix.handle, NULL) != 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "POSIX: Failed to create mutex"); - return GLFW_FALSE; - } - - return mutex->posix.allocated = GLFW_TRUE; -} - -void _glfwPlatformDestroyMutex(_GLFWmutex* mutex) -{ - if (mutex->posix.allocated) - pthread_mutex_destroy(&mutex->posix.handle); - memset(mutex, 0, sizeof(_GLFWmutex)); -} - -void _glfwPlatformLockMutex(_GLFWmutex* mutex) -{ - assert(mutex->posix.allocated == GLFW_TRUE); - pthread_mutex_lock(&mutex->posix.handle); -} - -void _glfwPlatformUnlockMutex(_GLFWmutex* mutex) -{ - assert(mutex->posix.allocated == GLFW_TRUE); - pthread_mutex_unlock(&mutex->posix.handle); -} - diff --git a/libs/zglfw/libs/glfw/src/posix_thread.h b/libs/zglfw/libs/glfw/src/posix_thread.h deleted file mode 100644 index 1c6a5c439..000000000 --- a/libs/zglfw/libs/glfw/src/posix_thread.h +++ /dev/null @@ -1,49 +0,0 @@ -//======================================================================== -// GLFW 3.3 POSIX - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#include - -#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix -#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix - - -// POSIX-specific thread local storage data -// -typedef struct _GLFWtlsPOSIX -{ - GLFWbool allocated; - pthread_key_t key; -} _GLFWtlsPOSIX; - -// POSIX-specific mutex data -// -typedef struct _GLFWmutexPOSIX -{ - GLFWbool allocated; - pthread_mutex_t handle; -} _GLFWmutexPOSIX; - diff --git a/libs/zglfw/libs/glfw/src/posix_time.c b/libs/zglfw/libs/glfw/src/posix_time.c deleted file mode 100644 index 040c8f180..000000000 --- a/libs/zglfw/libs/glfw/src/posix_time.c +++ /dev/null @@ -1,87 +0,0 @@ -//======================================================================== -// GLFW 3.3 POSIX - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialise timer -// -void _glfwInitTimerPOSIX(void) -{ -#if defined(CLOCK_MONOTONIC) - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) - { - _glfw.timer.posix.monotonic = GLFW_TRUE; - _glfw.timer.posix.frequency = 1000000000; - } - else -#endif - { - _glfw.timer.posix.monotonic = GLFW_FALSE; - _glfw.timer.posix.frequency = 1000000; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -uint64_t _glfwPlatformGetTimerValue(void) -{ -#if defined(CLOCK_MONOTONIC) - if (_glfw.timer.posix.monotonic) - { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; - } - else -#endif - { - struct timeval tv; - gettimeofday(&tv, NULL); - return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; - } -} - -uint64_t _glfwPlatformGetTimerFrequency(void) -{ - return _glfw.timer.posix.frequency; -} - diff --git a/libs/zglfw/libs/glfw/src/posix_time.h b/libs/zglfw/libs/glfw/src/posix_time.h deleted file mode 100644 index 911399eff..000000000 --- a/libs/zglfw/libs/glfw/src/posix_time.h +++ /dev/null @@ -1,43 +0,0 @@ -//======================================================================== -// GLFW 3.3 POSIX - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix - -#include - - -// POSIX-specific global timer data -// -typedef struct _GLFWtimerPOSIX -{ - GLFWbool monotonic; - uint64_t frequency; -} _GLFWtimerPOSIX; - - -void _glfwInitTimerPOSIX(void); - diff --git a/libs/zglfw/libs/glfw/src/vulkan.c b/libs/zglfw/libs/glfw/src/vulkan.c deleted file mode 100644 index 1b96579ca..000000000 --- a/libs/zglfw/libs/glfw/src/vulkan.c +++ /dev/null @@ -1,334 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2018 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include -#include - -#define _GLFW_FIND_LOADER 1 -#define _GLFW_REQUIRE_LOADER 2 - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwInitVulkan(int mode) -{ - VkResult err; - VkExtensionProperties* ep; - uint32_t i, count; - - if (_glfw.vk.available) - return GLFW_TRUE; - -#if !defined(_GLFW_VULKAN_STATIC) -#if defined(_GLFW_VULKAN_LIBRARY) - _glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY); -#elif defined(_GLFW_WIN32) - _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); -#elif defined(_GLFW_COCOA) - _glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib"); - if (!_glfw.vk.handle) - _glfw.vk.handle = _glfwLoadLocalVulkanLoaderNS(); -#elif defined(__OpenBSD__) || defined(__NetBSD__) - _glfw.vk.handle = _glfw_dlopen("libvulkan.so"); -#else - _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); -#endif - if (!_glfw.vk.handle) - { - if (mode == _GLFW_REQUIRE_LOADER) - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); - - return GLFW_FALSE; - } - - _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) - _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); - if (!_glfw.vk.GetInstanceProcAddr) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Vulkan: Loader does not export vkGetInstanceProcAddr"); - - _glfwTerminateVulkan(); - return GLFW_FALSE; - } - - _glfw.vk.EnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) - vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties"); - if (!_glfw.vk.EnumerateInstanceExtensionProperties) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Vulkan: Failed to retrieve vkEnumerateInstanceExtensionProperties"); - - _glfwTerminateVulkan(); - return GLFW_FALSE; - } -#endif // _GLFW_VULKAN_STATIC - - err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL); - if (err) - { - // NOTE: This happens on systems with a loader but without any Vulkan ICD - if (mode == _GLFW_REQUIRE_LOADER) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Vulkan: Failed to query instance extension count: %s", - _glfwGetVulkanResultString(err)); - } - - _glfwTerminateVulkan(); - return GLFW_FALSE; - } - - ep = calloc(count, sizeof(VkExtensionProperties)); - - err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep); - if (err) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Vulkan: Failed to query instance extensions: %s", - _glfwGetVulkanResultString(err)); - - free(ep); - _glfwTerminateVulkan(); - return GLFW_FALSE; - } - - for (i = 0; i < count; i++) - { - if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0) - _glfw.vk.KHR_surface = GLFW_TRUE; -#if defined(_GLFW_WIN32) - else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) - _glfw.vk.KHR_win32_surface = GLFW_TRUE; -#elif defined(_GLFW_COCOA) - else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) - _glfw.vk.MVK_macos_surface = GLFW_TRUE; - else if (strcmp(ep[i].extensionName, "VK_EXT_metal_surface") == 0) - _glfw.vk.EXT_metal_surface = GLFW_TRUE; -#elif defined(_GLFW_X11) - else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) - _glfw.vk.KHR_xlib_surface = GLFW_TRUE; - else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) - _glfw.vk.KHR_xcb_surface = GLFW_TRUE; -#elif defined(_GLFW_WAYLAND) - else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) - _glfw.vk.KHR_wayland_surface = GLFW_TRUE; -#endif - } - - free(ep); - - _glfw.vk.available = GLFW_TRUE; - - _glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions); - - return GLFW_TRUE; -} - -void _glfwTerminateVulkan(void) -{ -#if !defined(_GLFW_VULKAN_STATIC) - if (_glfw.vk.handle) - _glfw_dlclose(_glfw.vk.handle); -#endif -} - -const char* _glfwGetVulkanResultString(VkResult result) -{ - switch (result) - { - case VK_SUCCESS: - return "Success"; - case VK_NOT_READY: - return "A fence or query has not yet completed"; - case VK_TIMEOUT: - return "A wait operation has not completed in the specified time"; - case VK_EVENT_SET: - return "An event is signaled"; - case VK_EVENT_RESET: - return "An event is unsignaled"; - case VK_INCOMPLETE: - return "A return array was too small for the result"; - case VK_ERROR_OUT_OF_HOST_MEMORY: - return "A host memory allocation has failed"; - case VK_ERROR_OUT_OF_DEVICE_MEMORY: - return "A device memory allocation has failed"; - case VK_ERROR_INITIALIZATION_FAILED: - return "Initialization of an object could not be completed for implementation-specific reasons"; - case VK_ERROR_DEVICE_LOST: - return "The logical or physical device has been lost"; - case VK_ERROR_MEMORY_MAP_FAILED: - return "Mapping of a memory object has failed"; - case VK_ERROR_LAYER_NOT_PRESENT: - return "A requested layer is not present or could not be loaded"; - case VK_ERROR_EXTENSION_NOT_PRESENT: - return "A requested extension is not supported"; - case VK_ERROR_FEATURE_NOT_PRESENT: - return "A requested feature is not supported"; - case VK_ERROR_INCOMPATIBLE_DRIVER: - return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible"; - case VK_ERROR_TOO_MANY_OBJECTS: - return "Too many objects of the type have already been created"; - case VK_ERROR_FORMAT_NOT_SUPPORTED: - return "A requested format is not supported on this device"; - case VK_ERROR_SURFACE_LOST_KHR: - return "A surface is no longer available"; - case VK_SUBOPTIMAL_KHR: - return "A swapchain no longer matches the surface properties exactly, but can still be used"; - case VK_ERROR_OUT_OF_DATE_KHR: - return "A surface has changed in such a way that it is no longer compatible with the swapchain"; - case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: - return "The display used by a swapchain does not use the same presentable image layout"; - case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: - return "The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API"; - case VK_ERROR_VALIDATION_FAILED_EXT: - return "A validation layer found an error"; - default: - return "ERROR: UNKNOWN VULKAN ERROR"; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW public API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI int glfwVulkanSupported(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - return _glfwInitVulkan(_GLFW_FIND_LOADER); -} - -GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) -{ - assert(count != NULL); - - *count = 0; - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) - return NULL; - - if (!_glfw.vk.extensions[0]) - return NULL; - - *count = 2; - return (const char**) _glfw.vk.extensions; -} - -GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, - const char* procname) -{ - GLFWvkproc proc; - assert(procname != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) - return NULL; - - proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); -#if defined(_GLFW_VULKAN_STATIC) - if (!proc) - { - if (strcmp(procname, "vkGetInstanceProcAddr") == 0) - return (GLFWvkproc) vkGetInstanceProcAddr; - } -#else - if (!proc) - proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); -#endif - - return proc; -} - -GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) -{ - assert(instance != VK_NULL_HANDLE); - assert(device != VK_NULL_HANDLE); - - _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - - if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) - return GLFW_FALSE; - - if (!_glfw.vk.extensions[0]) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Vulkan: Window surface creation extensions not found"); - return GLFW_FALSE; - } - - return _glfwPlatformGetPhysicalDevicePresentationSupport(instance, - device, - queuefamily); -} - -GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, - GLFWwindow* handle, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(instance != VK_NULL_HANDLE); - assert(window != NULL); - assert(surface != NULL); - - *surface = VK_NULL_HANDLE; - - _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED); - - if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) - return VK_ERROR_INITIALIZATION_FAILED; - - if (!_glfw.vk.extensions[0]) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Vulkan: Window surface creation extensions not found"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - if (window->context.client != GLFW_NO_API) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Vulkan: Window surface creation requires the window to have the client API set to GLFW_NO_API"); - return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR; - } - - return _glfwPlatformCreateWindowSurface(instance, window, allocator, surface); -} - diff --git a/libs/zglfw/libs/glfw/src/wgl_context.c b/libs/zglfw/libs/glfw/src/wgl_context.c deleted file mode 100644 index 72ad11ded..000000000 --- a/libs/zglfw/libs/glfw/src/wgl_context.c +++ /dev/null @@ -1,798 +0,0 @@ -//======================================================================== -// GLFW 3.3 WGL - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include -#include - -// Return the value corresponding to the specified attribute -// -static int findPixelFormatAttribValue(const int* attribs, - int attribCount, - const int* values, - int attrib) -{ - int i; - - for (i = 0; i < attribCount; i++) - { - if (attribs[i] == attrib) - return values[i]; - } - - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Unknown pixel format attribute requested"); - return 0; -} - -#define addAttrib(a) \ -{ \ - assert((size_t) attribCount < sizeof(attribs) / sizeof(attribs[0])); \ - attribs[attribCount++] = a; \ -} -#define findAttribValue(a) \ - findPixelFormatAttribValue(attribs, attribCount, values, a) - -// Return a list of available and usable framebuffer configs -// -static int choosePixelFormat(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - _GLFWfbconfig* usableConfigs; - const _GLFWfbconfig* closest; - int i, pixelFormat, nativeCount, usableCount = 0, attribCount = 0; - int attribs[40]; - int values[sizeof(attribs) / sizeof(attribs[0])]; - - if (_glfw.wgl.ARB_pixel_format) - { - const int attrib = WGL_NUMBER_PIXEL_FORMATS_ARB; - - if (!wglGetPixelFormatAttribivARB(window->context.wgl.dc, - 1, 0, 1, &attrib, &nativeCount)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve pixel format attribute"); - return 0; - } - - addAttrib(WGL_SUPPORT_OPENGL_ARB); - addAttrib(WGL_DRAW_TO_WINDOW_ARB); - addAttrib(WGL_PIXEL_TYPE_ARB); - addAttrib(WGL_ACCELERATION_ARB); - addAttrib(WGL_RED_BITS_ARB); - addAttrib(WGL_RED_SHIFT_ARB); - addAttrib(WGL_GREEN_BITS_ARB); - addAttrib(WGL_GREEN_SHIFT_ARB); - addAttrib(WGL_BLUE_BITS_ARB); - addAttrib(WGL_BLUE_SHIFT_ARB); - addAttrib(WGL_ALPHA_BITS_ARB); - addAttrib(WGL_ALPHA_SHIFT_ARB); - addAttrib(WGL_DEPTH_BITS_ARB); - addAttrib(WGL_STENCIL_BITS_ARB); - addAttrib(WGL_ACCUM_BITS_ARB); - addAttrib(WGL_ACCUM_RED_BITS_ARB); - addAttrib(WGL_ACCUM_GREEN_BITS_ARB); - addAttrib(WGL_ACCUM_BLUE_BITS_ARB); - addAttrib(WGL_ACCUM_ALPHA_BITS_ARB); - addAttrib(WGL_AUX_BUFFERS_ARB); - addAttrib(WGL_STEREO_ARB); - addAttrib(WGL_DOUBLE_BUFFER_ARB); - - if (_glfw.wgl.ARB_multisample) - addAttrib(WGL_SAMPLES_ARB); - - if (ctxconfig->client == GLFW_OPENGL_API) - { - if (_glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB) - addAttrib(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); - } - else - { - if (_glfw.wgl.EXT_colorspace) - addAttrib(WGL_COLORSPACE_EXT); - } - } - else - { - nativeCount = DescribePixelFormat(window->context.wgl.dc, - 1, - sizeof(PIXELFORMATDESCRIPTOR), - NULL); - } - - usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); - - for (i = 0; i < nativeCount; i++) - { - _GLFWfbconfig* u = usableConfigs + usableCount; - pixelFormat = i + 1; - - if (_glfw.wgl.ARB_pixel_format) - { - // Get pixel format attributes through "modern" extension - - if (!wglGetPixelFormatAttribivARB(window->context.wgl.dc, - pixelFormat, 0, - attribCount, - attribs, values)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve pixel format attributes"); - - free(usableConfigs); - return 0; - } - - if (!findAttribValue(WGL_SUPPORT_OPENGL_ARB) || - !findAttribValue(WGL_DRAW_TO_WINDOW_ARB)) - { - continue; - } - - if (findAttribValue(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) - continue; - - if (findAttribValue(WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) - continue; - - if (findAttribValue(WGL_DOUBLE_BUFFER_ARB) != fbconfig->doublebuffer) - continue; - - u->redBits = findAttribValue(WGL_RED_BITS_ARB); - u->greenBits = findAttribValue(WGL_GREEN_BITS_ARB); - u->blueBits = findAttribValue(WGL_BLUE_BITS_ARB); - u->alphaBits = findAttribValue(WGL_ALPHA_BITS_ARB); - - u->depthBits = findAttribValue(WGL_DEPTH_BITS_ARB); - u->stencilBits = findAttribValue(WGL_STENCIL_BITS_ARB); - - u->accumRedBits = findAttribValue(WGL_ACCUM_RED_BITS_ARB); - u->accumGreenBits = findAttribValue(WGL_ACCUM_GREEN_BITS_ARB); - u->accumBlueBits = findAttribValue(WGL_ACCUM_BLUE_BITS_ARB); - u->accumAlphaBits = findAttribValue(WGL_ACCUM_ALPHA_BITS_ARB); - - u->auxBuffers = findAttribValue(WGL_AUX_BUFFERS_ARB); - - if (findAttribValue(WGL_STEREO_ARB)) - u->stereo = GLFW_TRUE; - - if (_glfw.wgl.ARB_multisample) - u->samples = findAttribValue(WGL_SAMPLES_ARB); - - if (ctxconfig->client == GLFW_OPENGL_API) - { - if (_glfw.wgl.ARB_framebuffer_sRGB || - _glfw.wgl.EXT_framebuffer_sRGB) - { - if (findAttribValue(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) - u->sRGB = GLFW_TRUE; - } - } - else - { - if (_glfw.wgl.EXT_colorspace) - { - if (findAttribValue(WGL_COLORSPACE_EXT) == WGL_COLORSPACE_SRGB_EXT) - u->sRGB = GLFW_TRUE; - } - } - } - else - { - // Get pixel format attributes through legacy PFDs - - PIXELFORMATDESCRIPTOR pfd; - - if (!DescribePixelFormat(window->context.wgl.dc, - pixelFormat, - sizeof(PIXELFORMATDESCRIPTOR), - &pfd)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to describe pixel format"); - - free(usableConfigs); - return 0; - } - - if (!(pfd.dwFlags & PFD_DRAW_TO_WINDOW) || - !(pfd.dwFlags & PFD_SUPPORT_OPENGL)) - { - continue; - } - - if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) && - (pfd.dwFlags & PFD_GENERIC_FORMAT)) - { - continue; - } - - if (pfd.iPixelType != PFD_TYPE_RGBA) - continue; - - if (!!(pfd.dwFlags & PFD_DOUBLEBUFFER) != fbconfig->doublebuffer) - continue; - - u->redBits = pfd.cRedBits; - u->greenBits = pfd.cGreenBits; - u->blueBits = pfd.cBlueBits; - u->alphaBits = pfd.cAlphaBits; - - u->depthBits = pfd.cDepthBits; - u->stencilBits = pfd.cStencilBits; - - u->accumRedBits = pfd.cAccumRedBits; - u->accumGreenBits = pfd.cAccumGreenBits; - u->accumBlueBits = pfd.cAccumBlueBits; - u->accumAlphaBits = pfd.cAccumAlphaBits; - - u->auxBuffers = pfd.cAuxBuffers; - - if (pfd.dwFlags & PFD_STEREO) - u->stereo = GLFW_TRUE; - } - - u->handle = pixelFormat; - usableCount++; - } - - if (!usableCount) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "WGL: The driver does not appear to support OpenGL"); - - free(usableConfigs); - return 0; - } - - closest = _glfwChooseFBConfig(fbconfig, usableConfigs, usableCount); - if (!closest) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "WGL: Failed to find a suitable pixel format"); - - free(usableConfigs); - return 0; - } - - pixelFormat = (int) closest->handle; - free(usableConfigs); - - return pixelFormat; -} - -#undef addAttrib -#undef findAttribValue - -static void makeContextCurrentWGL(_GLFWwindow* window) -{ - if (window) - { - if (wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle)) - _glfwPlatformSetTls(&_glfw.contextSlot, window); - else - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to make context current"); - _glfwPlatformSetTls(&_glfw.contextSlot, NULL); - } - } - else - { - if (!wglMakeCurrent(NULL, NULL)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to clear current context"); - } - - _glfwPlatformSetTls(&_glfw.contextSlot, NULL); - } -} - -static void swapBuffersWGL(_GLFWwindow* window) -{ - if (!window->monitor) - { - if (IsWindowsVistaOrGreater()) - { - // DWM Composition is always enabled on Win8+ - BOOL enabled = IsWindows8OrGreater(); - - // HACK: Use DwmFlush when desktop composition is enabled - if (enabled || - (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)) - { - int count = abs(window->context.wgl.interval); - while (count--) - DwmFlush(); - } - } - } - - SwapBuffers(window->context.wgl.dc); -} - -static void swapIntervalWGL(int interval) -{ - _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); - - window->context.wgl.interval = interval; - - if (!window->monitor) - { - if (IsWindowsVistaOrGreater()) - { - // DWM Composition is always enabled on Win8+ - BOOL enabled = IsWindows8OrGreater(); - - // HACK: Disable WGL swap interval when desktop composition is enabled to - // avoid interfering with DWM vsync - if (enabled || - (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)) - interval = 0; - } - } - - if (_glfw.wgl.EXT_swap_control) - wglSwapIntervalEXT(interval); -} - -static int extensionSupportedWGL(const char* extension) -{ - const char* extensions = NULL; - - if (_glfw.wgl.GetExtensionsStringARB) - extensions = wglGetExtensionsStringARB(wglGetCurrentDC()); - else if (_glfw.wgl.GetExtensionsStringEXT) - extensions = wglGetExtensionsStringEXT(); - - if (!extensions) - return GLFW_FALSE; - - return _glfwStringInExtensionString(extension, extensions); -} - -static GLFWglproc getProcAddressWGL(const char* procname) -{ - const GLFWglproc proc = (GLFWglproc) wglGetProcAddress(procname); - if (proc) - return proc; - - return (GLFWglproc) GetProcAddress(_glfw.wgl.instance, procname); -} - -static void destroyContextWGL(_GLFWwindow* window) -{ - if (window->context.wgl.handle) - { - wglDeleteContext(window->context.wgl.handle); - window->context.wgl.handle = NULL; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialize WGL -// -GLFWbool _glfwInitWGL(void) -{ - PIXELFORMATDESCRIPTOR pfd; - HGLRC prc, rc; - HDC pdc, dc; - - if (_glfw.wgl.instance) - return GLFW_TRUE; - - _glfw.wgl.instance = LoadLibraryA("opengl32.dll"); - if (!_glfw.wgl.instance) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to load opengl32.dll"); - return GLFW_FALSE; - } - - _glfw.wgl.CreateContext = (PFN_wglCreateContext) - GetProcAddress(_glfw.wgl.instance, "wglCreateContext"); - _glfw.wgl.DeleteContext = (PFN_wglDeleteContext) - GetProcAddress(_glfw.wgl.instance, "wglDeleteContext"); - _glfw.wgl.GetProcAddress = (PFN_wglGetProcAddress) - GetProcAddress(_glfw.wgl.instance, "wglGetProcAddress"); - _glfw.wgl.GetCurrentDC = (PFN_wglGetCurrentDC) - GetProcAddress(_glfw.wgl.instance, "wglGetCurrentDC"); - _glfw.wgl.GetCurrentContext = (PFN_wglGetCurrentContext) - GetProcAddress(_glfw.wgl.instance, "wglGetCurrentContext"); - _glfw.wgl.MakeCurrent = (PFN_wglMakeCurrent) - GetProcAddress(_glfw.wgl.instance, "wglMakeCurrent"); - _glfw.wgl.ShareLists = (PFN_wglShareLists) - GetProcAddress(_glfw.wgl.instance, "wglShareLists"); - - // NOTE: A dummy context has to be created for opengl32.dll to load the - // OpenGL ICD, from which we can then query WGL extensions - // NOTE: This code will accept the Microsoft GDI ICD; accelerated context - // creation failure occurs during manual pixel format enumeration - - dc = GetDC(_glfw.win32.helperWindowHandle); - - ZeroMemory(&pfd, sizeof(pfd)); - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 24; - - if (!SetPixelFormat(dc, ChoosePixelFormat(dc, &pfd), &pfd)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to set pixel format for dummy context"); - return GLFW_FALSE; - } - - rc = wglCreateContext(dc); - if (!rc) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to create dummy context"); - return GLFW_FALSE; - } - - pdc = wglGetCurrentDC(); - prc = wglGetCurrentContext(); - - if (!wglMakeCurrent(dc, rc)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to make dummy context current"); - wglMakeCurrent(pdc, prc); - wglDeleteContext(rc); - return GLFW_FALSE; - } - - // NOTE: Functions must be loaded first as they're needed to retrieve the - // extension string that tells us whether the functions are supported - _glfw.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) - wglGetProcAddress("wglGetExtensionsStringEXT"); - _glfw.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) - wglGetProcAddress("wglGetExtensionsStringARB"); - _glfw.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) - wglGetProcAddress("wglCreateContextAttribsARB"); - _glfw.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) - wglGetProcAddress("wglSwapIntervalEXT"); - _glfw.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) - wglGetProcAddress("wglGetPixelFormatAttribivARB"); - - // NOTE: WGL_ARB_extensions_string and WGL_EXT_extensions_string are not - // checked below as we are already using them - _glfw.wgl.ARB_multisample = - extensionSupportedWGL("WGL_ARB_multisample"); - _glfw.wgl.ARB_framebuffer_sRGB = - extensionSupportedWGL("WGL_ARB_framebuffer_sRGB"); - _glfw.wgl.EXT_framebuffer_sRGB = - extensionSupportedWGL("WGL_EXT_framebuffer_sRGB"); - _glfw.wgl.ARB_create_context = - extensionSupportedWGL("WGL_ARB_create_context"); - _glfw.wgl.ARB_create_context_profile = - extensionSupportedWGL("WGL_ARB_create_context_profile"); - _glfw.wgl.EXT_create_context_es2_profile = - extensionSupportedWGL("WGL_EXT_create_context_es2_profile"); - _glfw.wgl.ARB_create_context_robustness = - extensionSupportedWGL("WGL_ARB_create_context_robustness"); - _glfw.wgl.ARB_create_context_no_error = - extensionSupportedWGL("WGL_ARB_create_context_no_error"); - _glfw.wgl.EXT_swap_control = - extensionSupportedWGL("WGL_EXT_swap_control"); - _glfw.wgl.EXT_colorspace = - extensionSupportedWGL("WGL_EXT_colorspace"); - _glfw.wgl.ARB_pixel_format = - extensionSupportedWGL("WGL_ARB_pixel_format"); - _glfw.wgl.ARB_context_flush_control = - extensionSupportedWGL("WGL_ARB_context_flush_control"); - - wglMakeCurrent(pdc, prc); - wglDeleteContext(rc); - return GLFW_TRUE; -} - -// Terminate WGL -// -void _glfwTerminateWGL(void) -{ - if (_glfw.wgl.instance) - FreeLibrary(_glfw.wgl.instance); -} - -#define setAttrib(a, v) \ -{ \ - assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ - attribs[index++] = a; \ - attribs[index++] = v; \ -} - -// Create the OpenGL or OpenGL ES context -// -GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - int attribs[40]; - int pixelFormat; - PIXELFORMATDESCRIPTOR pfd; - HGLRC share = NULL; - - if (ctxconfig->share) - share = ctxconfig->share->context.wgl.handle; - - window->context.wgl.dc = GetDC(window->win32.handle); - if (!window->context.wgl.dc) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve DC for window"); - return GLFW_FALSE; - } - - pixelFormat = choosePixelFormat(window, ctxconfig, fbconfig); - if (!pixelFormat) - return GLFW_FALSE; - - if (!DescribePixelFormat(window->context.wgl.dc, - pixelFormat, sizeof(pfd), &pfd)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve PFD for selected pixel format"); - return GLFW_FALSE; - } - - if (!SetPixelFormat(window->context.wgl.dc, pixelFormat, &pfd)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to set selected pixel format"); - return GLFW_FALSE; - } - - if (ctxconfig->client == GLFW_OPENGL_API) - { - if (ctxconfig->forward) - { - if (!_glfw.wgl.ARB_create_context) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "WGL: A forward compatible OpenGL context requested but WGL_ARB_create_context is unavailable"); - return GLFW_FALSE; - } - } - - if (ctxconfig->profile) - { - if (!_glfw.wgl.ARB_create_context_profile) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "WGL: OpenGL profile requested but WGL_ARB_create_context_profile is unavailable"); - return GLFW_FALSE; - } - } - } - else - { - if (!_glfw.wgl.ARB_create_context || - !_glfw.wgl.ARB_create_context_profile || - !_glfw.wgl.EXT_create_context_es2_profile) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "WGL: OpenGL ES requested but WGL_ARB_create_context_es2_profile is unavailable"); - return GLFW_FALSE; - } - } - - if (_glfw.wgl.ARB_create_context) - { - int index = 0, mask = 0, flags = 0; - - if (ctxconfig->client == GLFW_OPENGL_API) - { - if (ctxconfig->forward) - flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; - - if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) - mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; - else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) - mask |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; - } - else - mask |= WGL_CONTEXT_ES2_PROFILE_BIT_EXT; - - if (ctxconfig->debug) - flags |= WGL_CONTEXT_DEBUG_BIT_ARB; - - if (ctxconfig->robustness) - { - if (_glfw.wgl.ARB_create_context_robustness) - { - if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) - { - setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - WGL_NO_RESET_NOTIFICATION_ARB); - } - else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) - { - setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - WGL_LOSE_CONTEXT_ON_RESET_ARB); - } - - flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; - } - } - - if (ctxconfig->release) - { - if (_glfw.wgl.ARB_context_flush_control) - { - if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) - { - setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, - WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); - } - else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) - { - setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, - WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); - } - } - } - - if (ctxconfig->noerror) - { - if (_glfw.wgl.ARB_create_context_no_error) - setAttrib(WGL_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); - } - - // NOTE: Only request an explicitly versioned context when necessary, as - // explicitly requesting version 1.0 does not always return the - // highest version supported by the driver - if (ctxconfig->major != 1 || ctxconfig->minor != 0) - { - setAttrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); - setAttrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); - } - - if (flags) - setAttrib(WGL_CONTEXT_FLAGS_ARB, flags); - - if (mask) - setAttrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); - - setAttrib(0, 0); - - window->context.wgl.handle = - wglCreateContextAttribsARB(window->context.wgl.dc, share, attribs); - if (!window->context.wgl.handle) - { - const DWORD error = GetLastError(); - - if (error == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) - { - if (ctxconfig->client == GLFW_OPENGL_API) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "WGL: Driver does not support OpenGL version %i.%i", - ctxconfig->major, - ctxconfig->minor); - } - else - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "WGL: Driver does not support OpenGL ES version %i.%i", - ctxconfig->major, - ctxconfig->minor); - } - } - else if (error == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "WGL: Driver does not support the requested OpenGL profile"); - } - else if (error == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) - { - _glfwInputError(GLFW_INVALID_VALUE, - "WGL: The share context is not compatible with the requested context"); - } - else - { - if (ctxconfig->client == GLFW_OPENGL_API) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "WGL: Failed to create OpenGL context"); - } - else - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "WGL: Failed to create OpenGL ES context"); - } - } - - return GLFW_FALSE; - } - } - else - { - window->context.wgl.handle = wglCreateContext(window->context.wgl.dc); - if (!window->context.wgl.handle) - { - _glfwInputErrorWin32(GLFW_VERSION_UNAVAILABLE, - "WGL: Failed to create OpenGL context"); - return GLFW_FALSE; - } - - if (share) - { - if (!wglShareLists(share, window->context.wgl.handle)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to enable sharing with specified OpenGL context"); - return GLFW_FALSE; - } - } - } - - window->context.makeCurrent = makeContextCurrentWGL; - window->context.swapBuffers = swapBuffersWGL; - window->context.swapInterval = swapIntervalWGL; - window->context.extensionSupported = extensionSupportedWGL; - window->context.getProcAddress = getProcAddressWGL; - window->context.destroy = destroyContextWGL; - - return GLFW_TRUE; -} - -#undef setAttrib - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (window->context.source != GLFW_NATIVE_CONTEXT_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return NULL; - } - - return window->context.wgl.handle; -} - diff --git a/libs/zglfw/libs/glfw/src/wgl_context.h b/libs/zglfw/libs/glfw/src/wgl_context.h deleted file mode 100644 index 47f045927..000000000 --- a/libs/zglfw/libs/glfw/src/wgl_context.h +++ /dev/null @@ -1,158 +0,0 @@ -//======================================================================== -// GLFW 3.3 WGL - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2018 Camilla Löwy -// -// 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. -// -//======================================================================== - -#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 -#define WGL_SUPPORT_OPENGL_ARB 0x2010 -#define WGL_DRAW_TO_WINDOW_ARB 0x2001 -#define WGL_PIXEL_TYPE_ARB 0x2013 -#define WGL_TYPE_RGBA_ARB 0x202b -#define WGL_ACCELERATION_ARB 0x2003 -#define WGL_NO_ACCELERATION_ARB 0x2025 -#define WGL_RED_BITS_ARB 0x2015 -#define WGL_RED_SHIFT_ARB 0x2016 -#define WGL_GREEN_BITS_ARB 0x2017 -#define WGL_GREEN_SHIFT_ARB 0x2018 -#define WGL_BLUE_BITS_ARB 0x2019 -#define WGL_BLUE_SHIFT_ARB 0x201a -#define WGL_ALPHA_BITS_ARB 0x201b -#define WGL_ALPHA_SHIFT_ARB 0x201c -#define WGL_ACCUM_BITS_ARB 0x201d -#define WGL_ACCUM_RED_BITS_ARB 0x201e -#define WGL_ACCUM_GREEN_BITS_ARB 0x201f -#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 -#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 -#define WGL_DEPTH_BITS_ARB 0x2022 -#define WGL_STENCIL_BITS_ARB 0x2023 -#define WGL_AUX_BUFFERS_ARB 0x2024 -#define WGL_STEREO_ARB 0x2012 -#define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_SAMPLES_ARB 0x2042 -#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 -#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 -#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 -#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 -#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 -#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define WGL_CONTEXT_FLAGS_ARB 0x2094 -#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 -#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 -#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 -#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 -#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 -#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 -#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 -#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 -#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 -#define WGL_COLORSPACE_EXT 0x309d -#define WGL_COLORSPACE_SRGB_EXT 0x3089 - -#define ERROR_INVALID_VERSION_ARB 0x2095 -#define ERROR_INVALID_PROFILE_ARB 0x2096 -#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 - -// WGL extension pointer typedefs -typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); -typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); -typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); -typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); -typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); -#define wglSwapIntervalEXT _glfw.wgl.SwapIntervalEXT -#define wglGetPixelFormatAttribivARB _glfw.wgl.GetPixelFormatAttribivARB -#define wglGetExtensionsStringEXT _glfw.wgl.GetExtensionsStringEXT -#define wglGetExtensionsStringARB _glfw.wgl.GetExtensionsStringARB -#define wglCreateContextAttribsARB _glfw.wgl.CreateContextAttribsARB - -// opengl32.dll function pointer typedefs -typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); -typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); -typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); -typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); -typedef HGLRC (WINAPI * PFN_wglGetCurrentContext)(void); -typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); -typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC); -#define wglCreateContext _glfw.wgl.CreateContext -#define wglDeleteContext _glfw.wgl.DeleteContext -#define wglGetProcAddress _glfw.wgl.GetProcAddress -#define wglGetCurrentDC _glfw.wgl.GetCurrentDC -#define wglGetCurrentContext _glfw.wgl.GetCurrentContext -#define wglMakeCurrent _glfw.wgl.MakeCurrent -#define wglShareLists _glfw.wgl.ShareLists - -#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextWGL wgl -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl - - -// WGL-specific per-context data -// -typedef struct _GLFWcontextWGL -{ - HDC dc; - HGLRC handle; - int interval; -} _GLFWcontextWGL; - -// WGL-specific global data -// -typedef struct _GLFWlibraryWGL -{ - HINSTANCE instance; - PFN_wglCreateContext CreateContext; - PFN_wglDeleteContext DeleteContext; - PFN_wglGetProcAddress GetProcAddress; - PFN_wglGetCurrentDC GetCurrentDC; - PFN_wglGetCurrentContext GetCurrentContext; - PFN_wglMakeCurrent MakeCurrent; - PFN_wglShareLists ShareLists; - - PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; - PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; - PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; - PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; - PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; - GLFWbool EXT_swap_control; - GLFWbool EXT_colorspace; - GLFWbool ARB_multisample; - GLFWbool ARB_framebuffer_sRGB; - GLFWbool EXT_framebuffer_sRGB; - GLFWbool ARB_pixel_format; - GLFWbool ARB_create_context; - GLFWbool ARB_create_context_profile; - GLFWbool EXT_create_context_es2_profile; - GLFWbool ARB_create_context_robustness; - GLFWbool ARB_create_context_no_error; - GLFWbool ARB_context_flush_control; -} _GLFWlibraryWGL; - - -GLFWbool _glfwInitWGL(void); -void _glfwTerminateWGL(void); -GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); - diff --git a/libs/zglfw/libs/glfw/src/win32_init.c b/libs/zglfw/libs/glfw/src/win32_init.c deleted file mode 100644 index 885f32fa9..000000000 --- a/libs/zglfw/libs/glfw/src/win32_init.c +++ /dev/null @@ -1,638 +0,0 @@ -//======================================================================== -// GLFW 3.3 Win32 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include - -static const GUID _glfw_GUID_DEVINTERFACE_HID = - {0x4d1e55b2,0xf16f,0x11cf,{0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30}}; - -#define GUID_DEVINTERFACE_HID _glfw_GUID_DEVINTERFACE_HID - -#if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) - -#if defined(_GLFW_BUILD_DLL) - #pragma message("These symbols must be exported by the executable and have no effect in a DLL") -#endif - -// Executables (but not DLLs) exporting this symbol with this value will be -// automatically directed to the high-performance GPU on Nvidia Optimus systems -// with up-to-date drivers -// -__declspec(dllexport) DWORD NvOptimusEnablement = 1; - -// Executables (but not DLLs) exporting this symbol with this value will be -// automatically directed to the high-performance GPU on AMD PowerXpress systems -// with up-to-date drivers -// -__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; - -#endif // _GLFW_USE_HYBRID_HPG - -#if defined(_GLFW_BUILD_DLL) - -// GLFW DLL entry point -// -BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) -{ - return TRUE; -} - -#endif // _GLFW_BUILD_DLL - -// Load necessary libraries (DLLs) -// -static GLFWbool loadLibraries(void) -{ - if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - (const WCHAR*) &_glfw, - (HMODULE*) &_glfw.win32.instance)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to retrieve own module handle"); - return GLFW_FALSE; - } - - _glfw.win32.user32.instance = LoadLibraryA("user32.dll"); - if (!_glfw.win32.user32.instance) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to load user32.dll"); - return GLFW_FALSE; - } - - _glfw.win32.user32.SetProcessDPIAware_ = (PFN_SetProcessDPIAware) - GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); - _glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx) - GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); - _glfw.win32.user32.EnableNonClientDpiScaling_ = (PFN_EnableNonClientDpiScaling) - GetProcAddress(_glfw.win32.user32.instance, "EnableNonClientDpiScaling"); - _glfw.win32.user32.SetProcessDpiAwarenessContext_ = (PFN_SetProcessDpiAwarenessContext) - GetProcAddress(_glfw.win32.user32.instance, "SetProcessDpiAwarenessContext"); - _glfw.win32.user32.GetDpiForWindow_ = (PFN_GetDpiForWindow) - GetProcAddress(_glfw.win32.user32.instance, "GetDpiForWindow"); - _glfw.win32.user32.AdjustWindowRectExForDpi_ = (PFN_AdjustWindowRectExForDpi) - GetProcAddress(_glfw.win32.user32.instance, "AdjustWindowRectExForDpi"); - _glfw.win32.user32.GetSystemMetricsForDpi_ = (PFN_GetSystemMetricsForDpi) - GetProcAddress(_glfw.win32.user32.instance, "GetSystemMetricsForDpi"); - - _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); - if (_glfw.win32.dinput8.instance) - { - _glfw.win32.dinput8.Create = (PFN_DirectInput8Create) - GetProcAddress(_glfw.win32.dinput8.instance, "DirectInput8Create"); - } - - { - int i; - const char* names[] = - { - "xinput1_4.dll", - "xinput1_3.dll", - "xinput9_1_0.dll", - "xinput1_2.dll", - "xinput1_1.dll", - NULL - }; - - for (i = 0; names[i]; i++) - { - _glfw.win32.xinput.instance = LoadLibraryA(names[i]); - if (_glfw.win32.xinput.instance) - { - _glfw.win32.xinput.GetCapabilities = (PFN_XInputGetCapabilities) - GetProcAddress(_glfw.win32.xinput.instance, "XInputGetCapabilities"); - _glfw.win32.xinput.GetState = (PFN_XInputGetState) - GetProcAddress(_glfw.win32.xinput.instance, "XInputGetState"); - - break; - } - } - } - - _glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll"); - if (_glfw.win32.dwmapi.instance) - { - _glfw.win32.dwmapi.IsCompositionEnabled = (PFN_DwmIsCompositionEnabled) - GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); - _glfw.win32.dwmapi.Flush = (PFN_DwmFlush) - GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); - _glfw.win32.dwmapi.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow) - GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); - _glfw.win32.dwmapi.GetColorizationColor = (PFN_DwmGetColorizationColor) - GetProcAddress(_glfw.win32.dwmapi.instance, "DwmGetColorizationColor"); - } - - _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); - if (_glfw.win32.shcore.instance) - { - _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness) - GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); - _glfw.win32.shcore.GetDpiForMonitor_ = (PFN_GetDpiForMonitor) - GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor"); - } - - _glfw.win32.ntdll.instance = LoadLibraryA("ntdll.dll"); - if (_glfw.win32.ntdll.instance) - { - _glfw.win32.ntdll.RtlVerifyVersionInfo_ = (PFN_RtlVerifyVersionInfo) - GetProcAddress(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); - } - - return GLFW_TRUE; -} - -// Unload used libraries (DLLs) -// -static void freeLibraries(void) -{ - if (_glfw.win32.xinput.instance) - FreeLibrary(_glfw.win32.xinput.instance); - - if (_glfw.win32.dinput8.instance) - FreeLibrary(_glfw.win32.dinput8.instance); - - if (_glfw.win32.user32.instance) - FreeLibrary(_glfw.win32.user32.instance); - - if (_glfw.win32.dwmapi.instance) - FreeLibrary(_glfw.win32.dwmapi.instance); - - if (_glfw.win32.shcore.instance) - FreeLibrary(_glfw.win32.shcore.instance); - - if (_glfw.win32.ntdll.instance) - FreeLibrary(_glfw.win32.ntdll.instance); -} - -// Create key code translation tables -// -static void createKeyTables(void) -{ - int scancode; - - memset(_glfw.win32.keycodes, -1, sizeof(_glfw.win32.keycodes)); - memset(_glfw.win32.scancodes, -1, sizeof(_glfw.win32.scancodes)); - - _glfw.win32.keycodes[0x00B] = GLFW_KEY_0; - _glfw.win32.keycodes[0x002] = GLFW_KEY_1; - _glfw.win32.keycodes[0x003] = GLFW_KEY_2; - _glfw.win32.keycodes[0x004] = GLFW_KEY_3; - _glfw.win32.keycodes[0x005] = GLFW_KEY_4; - _glfw.win32.keycodes[0x006] = GLFW_KEY_5; - _glfw.win32.keycodes[0x007] = GLFW_KEY_6; - _glfw.win32.keycodes[0x008] = GLFW_KEY_7; - _glfw.win32.keycodes[0x009] = GLFW_KEY_8; - _glfw.win32.keycodes[0x00A] = GLFW_KEY_9; - _glfw.win32.keycodes[0x01E] = GLFW_KEY_A; - _glfw.win32.keycodes[0x030] = GLFW_KEY_B; - _glfw.win32.keycodes[0x02E] = GLFW_KEY_C; - _glfw.win32.keycodes[0x020] = GLFW_KEY_D; - _glfw.win32.keycodes[0x012] = GLFW_KEY_E; - _glfw.win32.keycodes[0x021] = GLFW_KEY_F; - _glfw.win32.keycodes[0x022] = GLFW_KEY_G; - _glfw.win32.keycodes[0x023] = GLFW_KEY_H; - _glfw.win32.keycodes[0x017] = GLFW_KEY_I; - _glfw.win32.keycodes[0x024] = GLFW_KEY_J; - _glfw.win32.keycodes[0x025] = GLFW_KEY_K; - _glfw.win32.keycodes[0x026] = GLFW_KEY_L; - _glfw.win32.keycodes[0x032] = GLFW_KEY_M; - _glfw.win32.keycodes[0x031] = GLFW_KEY_N; - _glfw.win32.keycodes[0x018] = GLFW_KEY_O; - _glfw.win32.keycodes[0x019] = GLFW_KEY_P; - _glfw.win32.keycodes[0x010] = GLFW_KEY_Q; - _glfw.win32.keycodes[0x013] = GLFW_KEY_R; - _glfw.win32.keycodes[0x01F] = GLFW_KEY_S; - _glfw.win32.keycodes[0x014] = GLFW_KEY_T; - _glfw.win32.keycodes[0x016] = GLFW_KEY_U; - _glfw.win32.keycodes[0x02F] = GLFW_KEY_V; - _glfw.win32.keycodes[0x011] = GLFW_KEY_W; - _glfw.win32.keycodes[0x02D] = GLFW_KEY_X; - _glfw.win32.keycodes[0x015] = GLFW_KEY_Y; - _glfw.win32.keycodes[0x02C] = GLFW_KEY_Z; - - _glfw.win32.keycodes[0x028] = GLFW_KEY_APOSTROPHE; - _glfw.win32.keycodes[0x02B] = GLFW_KEY_BACKSLASH; - _glfw.win32.keycodes[0x033] = GLFW_KEY_COMMA; - _glfw.win32.keycodes[0x00D] = GLFW_KEY_EQUAL; - _glfw.win32.keycodes[0x029] = GLFW_KEY_GRAVE_ACCENT; - _glfw.win32.keycodes[0x01A] = GLFW_KEY_LEFT_BRACKET; - _glfw.win32.keycodes[0x00C] = GLFW_KEY_MINUS; - _glfw.win32.keycodes[0x034] = GLFW_KEY_PERIOD; - _glfw.win32.keycodes[0x01B] = GLFW_KEY_RIGHT_BRACKET; - _glfw.win32.keycodes[0x027] = GLFW_KEY_SEMICOLON; - _glfw.win32.keycodes[0x035] = GLFW_KEY_SLASH; - _glfw.win32.keycodes[0x056] = GLFW_KEY_WORLD_2; - - _glfw.win32.keycodes[0x00E] = GLFW_KEY_BACKSPACE; - _glfw.win32.keycodes[0x153] = GLFW_KEY_DELETE; - _glfw.win32.keycodes[0x14F] = GLFW_KEY_END; - _glfw.win32.keycodes[0x01C] = GLFW_KEY_ENTER; - _glfw.win32.keycodes[0x001] = GLFW_KEY_ESCAPE; - _glfw.win32.keycodes[0x147] = GLFW_KEY_HOME; - _glfw.win32.keycodes[0x152] = GLFW_KEY_INSERT; - _glfw.win32.keycodes[0x15D] = GLFW_KEY_MENU; - _glfw.win32.keycodes[0x151] = GLFW_KEY_PAGE_DOWN; - _glfw.win32.keycodes[0x149] = GLFW_KEY_PAGE_UP; - _glfw.win32.keycodes[0x045] = GLFW_KEY_PAUSE; - _glfw.win32.keycodes[0x039] = GLFW_KEY_SPACE; - _glfw.win32.keycodes[0x00F] = GLFW_KEY_TAB; - _glfw.win32.keycodes[0x03A] = GLFW_KEY_CAPS_LOCK; - _glfw.win32.keycodes[0x145] = GLFW_KEY_NUM_LOCK; - _glfw.win32.keycodes[0x046] = GLFW_KEY_SCROLL_LOCK; - _glfw.win32.keycodes[0x03B] = GLFW_KEY_F1; - _glfw.win32.keycodes[0x03C] = GLFW_KEY_F2; - _glfw.win32.keycodes[0x03D] = GLFW_KEY_F3; - _glfw.win32.keycodes[0x03E] = GLFW_KEY_F4; - _glfw.win32.keycodes[0x03F] = GLFW_KEY_F5; - _glfw.win32.keycodes[0x040] = GLFW_KEY_F6; - _glfw.win32.keycodes[0x041] = GLFW_KEY_F7; - _glfw.win32.keycodes[0x042] = GLFW_KEY_F8; - _glfw.win32.keycodes[0x043] = GLFW_KEY_F9; - _glfw.win32.keycodes[0x044] = GLFW_KEY_F10; - _glfw.win32.keycodes[0x057] = GLFW_KEY_F11; - _glfw.win32.keycodes[0x058] = GLFW_KEY_F12; - _glfw.win32.keycodes[0x064] = GLFW_KEY_F13; - _glfw.win32.keycodes[0x065] = GLFW_KEY_F14; - _glfw.win32.keycodes[0x066] = GLFW_KEY_F15; - _glfw.win32.keycodes[0x067] = GLFW_KEY_F16; - _glfw.win32.keycodes[0x068] = GLFW_KEY_F17; - _glfw.win32.keycodes[0x069] = GLFW_KEY_F18; - _glfw.win32.keycodes[0x06A] = GLFW_KEY_F19; - _glfw.win32.keycodes[0x06B] = GLFW_KEY_F20; - _glfw.win32.keycodes[0x06C] = GLFW_KEY_F21; - _glfw.win32.keycodes[0x06D] = GLFW_KEY_F22; - _glfw.win32.keycodes[0x06E] = GLFW_KEY_F23; - _glfw.win32.keycodes[0x076] = GLFW_KEY_F24; - _glfw.win32.keycodes[0x038] = GLFW_KEY_LEFT_ALT; - _glfw.win32.keycodes[0x01D] = GLFW_KEY_LEFT_CONTROL; - _glfw.win32.keycodes[0x02A] = GLFW_KEY_LEFT_SHIFT; - _glfw.win32.keycodes[0x15B] = GLFW_KEY_LEFT_SUPER; - _glfw.win32.keycodes[0x137] = GLFW_KEY_PRINT_SCREEN; - _glfw.win32.keycodes[0x138] = GLFW_KEY_RIGHT_ALT; - _glfw.win32.keycodes[0x11D] = GLFW_KEY_RIGHT_CONTROL; - _glfw.win32.keycodes[0x036] = GLFW_KEY_RIGHT_SHIFT; - _glfw.win32.keycodes[0x15C] = GLFW_KEY_RIGHT_SUPER; - _glfw.win32.keycodes[0x150] = GLFW_KEY_DOWN; - _glfw.win32.keycodes[0x14B] = GLFW_KEY_LEFT; - _glfw.win32.keycodes[0x14D] = GLFW_KEY_RIGHT; - _glfw.win32.keycodes[0x148] = GLFW_KEY_UP; - - _glfw.win32.keycodes[0x052] = GLFW_KEY_KP_0; - _glfw.win32.keycodes[0x04F] = GLFW_KEY_KP_1; - _glfw.win32.keycodes[0x050] = GLFW_KEY_KP_2; - _glfw.win32.keycodes[0x051] = GLFW_KEY_KP_3; - _glfw.win32.keycodes[0x04B] = GLFW_KEY_KP_4; - _glfw.win32.keycodes[0x04C] = GLFW_KEY_KP_5; - _glfw.win32.keycodes[0x04D] = GLFW_KEY_KP_6; - _glfw.win32.keycodes[0x047] = GLFW_KEY_KP_7; - _glfw.win32.keycodes[0x048] = GLFW_KEY_KP_8; - _glfw.win32.keycodes[0x049] = GLFW_KEY_KP_9; - _glfw.win32.keycodes[0x04E] = GLFW_KEY_KP_ADD; - _glfw.win32.keycodes[0x053] = GLFW_KEY_KP_DECIMAL; - _glfw.win32.keycodes[0x135] = GLFW_KEY_KP_DIVIDE; - _glfw.win32.keycodes[0x11C] = GLFW_KEY_KP_ENTER; - _glfw.win32.keycodes[0x059] = GLFW_KEY_KP_EQUAL; - _glfw.win32.keycodes[0x037] = GLFW_KEY_KP_MULTIPLY; - _glfw.win32.keycodes[0x04A] = GLFW_KEY_KP_SUBTRACT; - - for (scancode = 0; scancode < 512; scancode++) - { - if (_glfw.win32.keycodes[scancode] > 0) - _glfw.win32.scancodes[_glfw.win32.keycodes[scancode]] = scancode; - } -} - -// Creates a dummy window for behind-the-scenes work -// -static GLFWbool createHelperWindow(void) -{ - MSG msg; - - _glfw.win32.helperWindowHandle = - CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, - _GLFW_WNDCLASSNAME, - L"GLFW message window", - WS_CLIPSIBLINGS | WS_CLIPCHILDREN, - 0, 0, 1, 1, - NULL, NULL, - _glfw.win32.instance, - NULL); - - if (!_glfw.win32.helperWindowHandle) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to create helper window"); - return GLFW_FALSE; - } - - // HACK: The command to the first ShowWindow call is ignored if the parent - // process passed along a STARTUPINFO, so clear that with a no-op call - ShowWindow(_glfw.win32.helperWindowHandle, SW_HIDE); - - // Register for HID device notifications - { - DEV_BROADCAST_DEVICEINTERFACE_W dbi; - ZeroMemory(&dbi, sizeof(dbi)); - dbi.dbcc_size = sizeof(dbi); - dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; - dbi.dbcc_classguid = GUID_DEVINTERFACE_HID; - - _glfw.win32.deviceNotificationHandle = - RegisterDeviceNotificationW(_glfw.win32.helperWindowHandle, - (DEV_BROADCAST_HDR*) &dbi, - DEVICE_NOTIFY_WINDOW_HANDLE); - } - - while (PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessageW(&msg); - } - - return GLFW_TRUE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Returns a wide string version of the specified UTF-8 string -// -WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) -{ - WCHAR* target; - int count; - - count = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); - if (!count) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert string from UTF-8"); - return NULL; - } - - target = calloc(count, sizeof(WCHAR)); - - if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, count)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert string from UTF-8"); - free(target); - return NULL; - } - - return target; -} - -// Returns a UTF-8 string version of the specified wide string -// -char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) -{ - char* target; - int size; - - size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); - if (!size) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert string to UTF-8"); - return NULL; - } - - target = calloc(size, 1); - - if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert string to UTF-8"); - free(target); - return NULL; - } - - return target; -} - -// Reports the specified error, appending information about the last Win32 error -// -void _glfwInputErrorWin32(int error, const char* description) -{ - WCHAR buffer[_GLFW_MESSAGE_SIZE] = L""; - char message[_GLFW_MESSAGE_SIZE] = ""; - - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, - GetLastError() & 0xffff, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - buffer, - sizeof(buffer) / sizeof(WCHAR), - NULL); - WideCharToMultiByte(CP_UTF8, 0, buffer, -1, message, sizeof(message), NULL, NULL); - - _glfwInputError(error, "%s: %s", description, message); -} - -// Updates key names according to the current keyboard layout -// -void _glfwUpdateKeyNamesWin32(void) -{ - int key; - BYTE state[256] = {0}; - - memset(_glfw.win32.keynames, 0, sizeof(_glfw.win32.keynames)); - - for (key = GLFW_KEY_SPACE; key <= GLFW_KEY_LAST; key++) - { - UINT vk; - int scancode, length; - WCHAR chars[16]; - - scancode = _glfw.win32.scancodes[key]; - if (scancode == -1) - continue; - - if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) - { - const UINT vks[] = { - VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, - VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, - VK_NUMPAD8, VK_NUMPAD9, VK_DECIMAL, VK_DIVIDE, - VK_MULTIPLY, VK_SUBTRACT, VK_ADD - }; - - vk = vks[key - GLFW_KEY_KP_0]; - } - else - vk = MapVirtualKeyW(scancode, MAPVK_VSC_TO_VK); - - length = ToUnicode(vk, scancode, state, - chars, sizeof(chars) / sizeof(WCHAR), - 0); - - if (length == -1) - { - // This is a dead key, so we need a second simulated key press - // to make it output its own character (usually a diacritic) - length = ToUnicode(vk, scancode, state, - chars, sizeof(chars) / sizeof(WCHAR), - 0); - } - - if (length < 1) - continue; - - WideCharToMultiByte(CP_UTF8, 0, chars, 1, - _glfw.win32.keynames[key], - sizeof(_glfw.win32.keynames[key]), - NULL, NULL); - } -} - -// Replacement for IsWindowsVersionOrGreater, as we cannot rely on the -// application having a correct embedded manifest -// -BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp) -{ - OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; - DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; - ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); - cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); - cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); - // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the - // latter lies unless the user knew to embed a non-default manifest - // announcing support for Windows 10 via supportedOS GUID - return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; -} - -// Checks whether we are on at least the specified build of Windows 10 -// -BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build) -{ - OSVERSIONINFOEXW osvi = { sizeof(osvi), 10, 0, build }; - DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER; - ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); - cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); - cond = VerSetConditionMask(cond, VER_BUILDNUMBER, VER_GREATER_EQUAL); - // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the - // latter lies unless the user knew to embed a non-default manifest - // announcing support for Windows 10 via supportedOS GUID - return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformInit(void) -{ - // To make SetForegroundWindow work as we want, we need to fiddle - // with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early - // as possible in the hope of still being the foreground process) - SystemParametersInfoW(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, - &_glfw.win32.foregroundLockTimeout, 0); - SystemParametersInfoW(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, UIntToPtr(0), - SPIF_SENDCHANGE); - - if (!loadLibraries()) - return GLFW_FALSE; - - createKeyTables(); - _glfwUpdateKeyNamesWin32(); - - if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) - SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - else if (IsWindows8Point1OrGreater()) - SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - else if (IsWindowsVistaOrGreater()) - SetProcessDPIAware(); - - if (!_glfwRegisterWindowClassWin32()) - return GLFW_FALSE; - - if (!createHelperWindow()) - return GLFW_FALSE; - - _glfwInitTimerWin32(); - _glfwInitJoysticksWin32(); - - _glfwPollMonitorsWin32(); - return GLFW_TRUE; -} - -void _glfwPlatformTerminate(void) -{ - if (_glfw.win32.deviceNotificationHandle) - UnregisterDeviceNotification(_glfw.win32.deviceNotificationHandle); - - if (_glfw.win32.helperWindowHandle) - DestroyWindow(_glfw.win32.helperWindowHandle); - - _glfwUnregisterWindowClassWin32(); - - // Restore previous foreground lock timeout system setting - SystemParametersInfoW(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, - UIntToPtr(_glfw.win32.foregroundLockTimeout), - SPIF_SENDCHANGE); - - free(_glfw.win32.clipboardString); - free(_glfw.win32.rawInput); - - _glfwTerminateWGL(); - _glfwTerminateEGL(); - _glfwTerminateOSMesa(); - - _glfwTerminateJoysticksWin32(); - - freeLibraries(); -} - -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " Win32 WGL EGL OSMesa" -#if defined(__MINGW32__) - " MinGW" -#elif defined(_MSC_VER) - " VisualC" -#endif -#if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) - " hybrid-GPU" -#endif -#if defined(_GLFW_BUILD_DLL) - " DLL" -#endif - ; -} - diff --git a/libs/zglfw/libs/glfw/src/win32_joystick.c b/libs/zglfw/libs/glfw/src/win32_joystick.c deleted file mode 100644 index f471f0a93..000000000 --- a/libs/zglfw/libs/glfw/src/win32_joystick.c +++ /dev/null @@ -1,755 +0,0 @@ -//======================================================================== -// GLFW 3.3 Win32 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include - -#define _GLFW_TYPE_AXIS 0 -#define _GLFW_TYPE_SLIDER 1 -#define _GLFW_TYPE_BUTTON 2 -#define _GLFW_TYPE_POV 3 - -// Data produced with DirectInput device object enumeration -// -typedef struct _GLFWobjenumWin32 -{ - IDirectInputDevice8W* device; - _GLFWjoyobjectWin32* objects; - int objectCount; - int axisCount; - int sliderCount; - int buttonCount; - int povCount; -} _GLFWobjenumWin32; - -// Define local copies of the necessary GUIDs -// -static const GUID _glfw_IID_IDirectInput8W = - {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}}; -static const GUID _glfw_GUID_XAxis = - {0xa36d02e0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_YAxis = - {0xa36d02e1,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_ZAxis = - {0xa36d02e2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_RxAxis = - {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_RyAxis = - {0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_RzAxis = - {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_Slider = - {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_POV = - {0xa36d02f2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; - -#define IID_IDirectInput8W _glfw_IID_IDirectInput8W -#define GUID_XAxis _glfw_GUID_XAxis -#define GUID_YAxis _glfw_GUID_YAxis -#define GUID_ZAxis _glfw_GUID_ZAxis -#define GUID_RxAxis _glfw_GUID_RxAxis -#define GUID_RyAxis _glfw_GUID_RyAxis -#define GUID_RzAxis _glfw_GUID_RzAxis -#define GUID_Slider _glfw_GUID_Slider -#define GUID_POV _glfw_GUID_POV - -// Object data array for our clone of c_dfDIJoystick -// Generated with https://github.com/elmindreda/c_dfDIJoystick2 -// -static DIOBJECTDATAFORMAT _glfwObjectDataFormats[] = -{ - { &GUID_XAxis,DIJOFS_X,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, - { &GUID_YAxis,DIJOFS_Y,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, - { &GUID_ZAxis,DIJOFS_Z,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, - { &GUID_RxAxis,DIJOFS_RX,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, - { &GUID_RyAxis,DIJOFS_RY,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, - { &GUID_RzAxis,DIJOFS_RZ,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, - { &GUID_Slider,DIJOFS_SLIDER(0),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, - { &GUID_Slider,DIJOFS_SLIDER(1),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, - { &GUID_POV,DIJOFS_POV(0),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { &GUID_POV,DIJOFS_POV(1),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { &GUID_POV,DIJOFS_POV(2),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { &GUID_POV,DIJOFS_POV(3),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(0),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(1),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(2),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(3),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(4),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(5),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(6),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(7),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(8),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(9),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(10),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(11),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(12),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(13),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(14),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(15),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(16),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(17),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(18),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(19),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(20),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(21),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(22),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(23),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(24),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(25),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(26),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(27),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(28),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(29),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(30),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, - { NULL,DIJOFS_BUTTON(31),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, -}; - -// Our clone of c_dfDIJoystick -// -static const DIDATAFORMAT _glfwDataFormat = -{ - sizeof(DIDATAFORMAT), - sizeof(DIOBJECTDATAFORMAT), - DIDFT_ABSAXIS, - sizeof(DIJOYSTATE), - sizeof(_glfwObjectDataFormats) / sizeof(DIOBJECTDATAFORMAT), - _glfwObjectDataFormats -}; - -// Returns a description fitting the specified XInput capabilities -// -static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic) -{ - switch (xic->SubType) - { - case XINPUT_DEVSUBTYPE_WHEEL: - return "XInput Wheel"; - case XINPUT_DEVSUBTYPE_ARCADE_STICK: - return "XInput Arcade Stick"; - case XINPUT_DEVSUBTYPE_FLIGHT_STICK: - return "XInput Flight Stick"; - case XINPUT_DEVSUBTYPE_DANCE_PAD: - return "XInput Dance Pad"; - case XINPUT_DEVSUBTYPE_GUITAR: - return "XInput Guitar"; - case XINPUT_DEVSUBTYPE_DRUM_KIT: - return "XInput Drum Kit"; - case XINPUT_DEVSUBTYPE_GAMEPAD: - { - if (xic->Flags & XINPUT_CAPS_WIRELESS) - return "Wireless Xbox Controller"; - else - return "Xbox Controller"; - } - } - - return "Unknown XInput Device"; -} - -// Lexically compare device objects -// -static int compareJoystickObjects(const void* first, const void* second) -{ - const _GLFWjoyobjectWin32* fo = first; - const _GLFWjoyobjectWin32* so = second; - - if (fo->type != so->type) - return fo->type - so->type; - - return fo->offset - so->offset; -} - -// Checks whether the specified device supports XInput -// Technique from FDInputJoystickManager::IsXInputDeviceFast in ZDoom -// -static GLFWbool supportsXInput(const GUID* guid) -{ - UINT i, count = 0; - RAWINPUTDEVICELIST* ridl; - GLFWbool result = GLFW_FALSE; - - if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0) - return GLFW_FALSE; - - ridl = calloc(count, sizeof(RAWINPUTDEVICELIST)); - - if (GetRawInputDeviceList(ridl, &count, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1) - { - free(ridl); - return GLFW_FALSE; - } - - for (i = 0; i < count; i++) - { - RID_DEVICE_INFO rdi; - char name[256]; - UINT size; - - if (ridl[i].dwType != RIM_TYPEHID) - continue; - - ZeroMemory(&rdi, sizeof(rdi)); - rdi.cbSize = sizeof(rdi); - size = sizeof(rdi); - - if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice, - RIDI_DEVICEINFO, - &rdi, &size) == -1) - { - continue; - } - - if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) != (LONG) guid->Data1) - continue; - - memset(name, 0, sizeof(name)); - size = sizeof(name); - - if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice, - RIDI_DEVICENAME, - name, &size) == -1) - { - break; - } - - name[sizeof(name) - 1] = '\0'; - if (strstr(name, "IG_")) - { - result = GLFW_TRUE; - break; - } - } - - free(ridl); - return result; -} - -// Frees all resources associated with the specified joystick -// -static void closeJoystick(_GLFWjoystick* js) -{ - _glfwInputJoystick(js, GLFW_DISCONNECTED); - - if (js->win32.device) - { - IDirectInputDevice8_Unacquire(js->win32.device); - IDirectInputDevice8_Release(js->win32.device); - } - - free(js->win32.objects); - _glfwFreeJoystick(js); -} - -// DirectInput device object enumeration callback -// Insights gleaned from SDL -// -static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, - void* user) -{ - _GLFWobjenumWin32* data = user; - _GLFWjoyobjectWin32* object = data->objects + data->objectCount; - - if (DIDFT_GETTYPE(doi->dwType) & DIDFT_AXIS) - { - DIPROPRANGE dipr; - - if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) - object->offset = DIJOFS_SLIDER(data->sliderCount); - else if (memcmp(&doi->guidType, &GUID_XAxis, sizeof(GUID)) == 0) - object->offset = DIJOFS_X; - else if (memcmp(&doi->guidType, &GUID_YAxis, sizeof(GUID)) == 0) - object->offset = DIJOFS_Y; - else if (memcmp(&doi->guidType, &GUID_ZAxis, sizeof(GUID)) == 0) - object->offset = DIJOFS_Z; - else if (memcmp(&doi->guidType, &GUID_RxAxis, sizeof(GUID)) == 0) - object->offset = DIJOFS_RX; - else if (memcmp(&doi->guidType, &GUID_RyAxis, sizeof(GUID)) == 0) - object->offset = DIJOFS_RY; - else if (memcmp(&doi->guidType, &GUID_RzAxis, sizeof(GUID)) == 0) - object->offset = DIJOFS_RZ; - else - return DIENUM_CONTINUE; - - ZeroMemory(&dipr, sizeof(dipr)); - dipr.diph.dwSize = sizeof(dipr); - dipr.diph.dwHeaderSize = sizeof(dipr.diph); - dipr.diph.dwObj = doi->dwType; - dipr.diph.dwHow = DIPH_BYID; - dipr.lMin = -32768; - dipr.lMax = 32767; - - if (FAILED(IDirectInputDevice8_SetProperty(data->device, - DIPROP_RANGE, - &dipr.diph))) - { - return DIENUM_CONTINUE; - } - - if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) - { - object->type = _GLFW_TYPE_SLIDER; - data->sliderCount++; - } - else - { - object->type = _GLFW_TYPE_AXIS; - data->axisCount++; - } - } - else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_BUTTON) - { - object->offset = DIJOFS_BUTTON(data->buttonCount); - object->type = _GLFW_TYPE_BUTTON; - data->buttonCount++; - } - else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_POV) - { - object->offset = DIJOFS_POV(data->povCount); - object->type = _GLFW_TYPE_POV; - data->povCount++; - } - - data->objectCount++; - return DIENUM_CONTINUE; -} - -// DirectInput device enumeration callback -// -static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) -{ - int jid = 0; - DIDEVCAPS dc; - DIPROPDWORD dipd; - IDirectInputDevice8* device; - _GLFWobjenumWin32 data; - _GLFWjoystick* js; - char guid[33]; - char name[256]; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - js = _glfw.joysticks + jid; - if (js->connected) - { - if (memcmp(&js->win32.guid, &di->guidInstance, sizeof(GUID)) == 0) - return DIENUM_CONTINUE; - } - } - - if (supportsXInput(&di->guidProduct)) - return DIENUM_CONTINUE; - - if (FAILED(IDirectInput8_CreateDevice(_glfw.win32.dinput8.api, - &di->guidInstance, - &device, - NULL))) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create device"); - return DIENUM_CONTINUE; - } - - if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat))) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to set device data format"); - - IDirectInputDevice8_Release(device); - return DIENUM_CONTINUE; - } - - ZeroMemory(&dc, sizeof(dc)); - dc.dwSize = sizeof(dc); - - if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc))) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to query device capabilities"); - - IDirectInputDevice8_Release(device); - return DIENUM_CONTINUE; - } - - ZeroMemory(&dipd, sizeof(dipd)); - dipd.diph.dwSize = sizeof(dipd); - dipd.diph.dwHeaderSize = sizeof(dipd.diph); - dipd.diph.dwHow = DIPH_DEVICE; - dipd.dwData = DIPROPAXISMODE_ABS; - - if (FAILED(IDirectInputDevice8_SetProperty(device, - DIPROP_AXISMODE, - &dipd.diph))) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to set device axis mode"); - - IDirectInputDevice8_Release(device); - return DIENUM_CONTINUE; - } - - memset(&data, 0, sizeof(data)); - data.device = device; - data.objects = calloc(dc.dwAxes + (size_t) dc.dwButtons + dc.dwPOVs, - sizeof(_GLFWjoyobjectWin32)); - - if (FAILED(IDirectInputDevice8_EnumObjects(device, - deviceObjectCallback, - &data, - DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV))) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to enumerate device objects"); - - IDirectInputDevice8_Release(device); - free(data.objects); - return DIENUM_CONTINUE; - } - - qsort(data.objects, data.objectCount, - sizeof(_GLFWjoyobjectWin32), - compareJoystickObjects); - - if (!WideCharToMultiByte(CP_UTF8, 0, - di->tszInstanceName, -1, - name, sizeof(name), - NULL, NULL)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert joystick name to UTF-8"); - - IDirectInputDevice8_Release(device); - free(data.objects); - return DIENUM_STOP; - } - - // Generate a joystick GUID that matches the SDL 2.0.5+ one - if (memcmp(&di->guidProduct.Data4[2], "PIDVID", 6) == 0) - { - sprintf(guid, "03000000%02x%02x0000%02x%02x000000000000", - (uint8_t) di->guidProduct.Data1, - (uint8_t) (di->guidProduct.Data1 >> 8), - (uint8_t) (di->guidProduct.Data1 >> 16), - (uint8_t) (di->guidProduct.Data1 >> 24)); - } - else - { - sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", - name[0], name[1], name[2], name[3], - name[4], name[5], name[6], name[7], - name[8], name[9], name[10]); - } - - js = _glfwAllocJoystick(name, guid, - data.axisCount + data.sliderCount, - data.buttonCount, - data.povCount); - if (!js) - { - IDirectInputDevice8_Release(device); - free(data.objects); - return DIENUM_STOP; - } - - js->win32.device = device; - js->win32.guid = di->guidInstance; - js->win32.objects = data.objects; - js->win32.objectCount = data.objectCount; - - _glfwInputJoystick(js, GLFW_CONNECTED); - return DIENUM_CONTINUE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialize joystick interface -// -void _glfwInitJoysticksWin32(void) -{ - if (_glfw.win32.dinput8.instance) - { - if (FAILED(DirectInput8Create(_glfw.win32.instance, - DIRECTINPUT_VERSION, - &IID_IDirectInput8W, - (void**) &_glfw.win32.dinput8.api, - NULL))) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to create interface"); - } - } - - _glfwDetectJoystickConnectionWin32(); -} - -// Close all opened joystick handles -// -void _glfwTerminateJoysticksWin32(void) -{ - int jid; - - for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) - closeJoystick(_glfw.joysticks + jid); - - if (_glfw.win32.dinput8.api) - IDirectInput8_Release(_glfw.win32.dinput8.api); -} - -// Checks for new joysticks after DBT_DEVICEARRIVAL -// -void _glfwDetectJoystickConnectionWin32(void) -{ - if (_glfw.win32.xinput.instance) - { - DWORD index; - - for (index = 0; index < XUSER_MAX_COUNT; index++) - { - int jid; - char guid[33]; - XINPUT_CAPABILITIES xic; - _GLFWjoystick* js; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - if (_glfw.joysticks[jid].connected && - _glfw.joysticks[jid].win32.device == NULL && - _glfw.joysticks[jid].win32.index == index) - { - break; - } - } - - if (jid <= GLFW_JOYSTICK_LAST) - continue; - - if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) - continue; - - // Generate a joystick GUID that matches the SDL 2.0.5+ one - sprintf(guid, "78696e707574%02x000000000000000000", - xic.SubType & 0xff); - - js = _glfwAllocJoystick(getDeviceDescription(&xic), guid, 6, 10, 1); - if (!js) - continue; - - js->win32.index = index; - - _glfwInputJoystick(js, GLFW_CONNECTED); - } - } - - if (_glfw.win32.dinput8.api) - { - if (FAILED(IDirectInput8_EnumDevices(_glfw.win32.dinput8.api, - DI8DEVCLASS_GAMECTRL, - deviceCallback, - NULL, - DIEDFL_ALLDEVICES))) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Failed to enumerate DirectInput8 devices"); - return; - } - } -} - -// Checks for joystick disconnection after DBT_DEVICEREMOVECOMPLETE -// -void _glfwDetectJoystickDisconnectionWin32(void) -{ - int jid; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - { - _GLFWjoystick* js = _glfw.joysticks + jid; - if (js->connected) - _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) -{ - if (js->win32.device) - { - int i, ai = 0, bi = 0, pi = 0; - HRESULT result; - DIJOYSTATE state = {0}; - - IDirectInputDevice8_Poll(js->win32.device); - result = IDirectInputDevice8_GetDeviceState(js->win32.device, - sizeof(state), - &state); - if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) - { - IDirectInputDevice8_Acquire(js->win32.device); - IDirectInputDevice8_Poll(js->win32.device); - result = IDirectInputDevice8_GetDeviceState(js->win32.device, - sizeof(state), - &state); - } - - if (FAILED(result)) - { - closeJoystick(js); - return GLFW_FALSE; - } - - if (mode == _GLFW_POLL_PRESENCE) - return GLFW_TRUE; - - for (i = 0; i < js->win32.objectCount; i++) - { - const void* data = (char*) &state + js->win32.objects[i].offset; - - switch (js->win32.objects[i].type) - { - case _GLFW_TYPE_AXIS: - case _GLFW_TYPE_SLIDER: - { - const float value = (*((LONG*) data) + 0.5f) / 32767.5f; - _glfwInputJoystickAxis(js, ai, value); - ai++; - break; - } - - case _GLFW_TYPE_BUTTON: - { - const char value = (*((BYTE*) data) & 0x80) != 0; - _glfwInputJoystickButton(js, bi, value); - bi++; - break; - } - - case _GLFW_TYPE_POV: - { - const int states[9] = - { - GLFW_HAT_UP, - GLFW_HAT_RIGHT_UP, - GLFW_HAT_RIGHT, - GLFW_HAT_RIGHT_DOWN, - GLFW_HAT_DOWN, - GLFW_HAT_LEFT_DOWN, - GLFW_HAT_LEFT, - GLFW_HAT_LEFT_UP, - GLFW_HAT_CENTERED - }; - - // Screams of horror are appropriate at this point - int stateIndex = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); - if (stateIndex < 0 || stateIndex > 8) - stateIndex = 8; - - _glfwInputJoystickHat(js, pi, states[stateIndex]); - pi++; - break; - } - } - } - } - else - { - int i, dpad = 0; - DWORD result; - XINPUT_STATE xis; - const WORD buttons[10] = - { - XINPUT_GAMEPAD_A, - XINPUT_GAMEPAD_B, - XINPUT_GAMEPAD_X, - XINPUT_GAMEPAD_Y, - XINPUT_GAMEPAD_LEFT_SHOULDER, - XINPUT_GAMEPAD_RIGHT_SHOULDER, - XINPUT_GAMEPAD_BACK, - XINPUT_GAMEPAD_START, - XINPUT_GAMEPAD_LEFT_THUMB, - XINPUT_GAMEPAD_RIGHT_THUMB - }; - - result = XInputGetState(js->win32.index, &xis); - if (result != ERROR_SUCCESS) - { - if (result == ERROR_DEVICE_NOT_CONNECTED) - closeJoystick(js); - - return GLFW_FALSE; - } - - if (mode == _GLFW_POLL_PRESENCE) - return GLFW_TRUE; - - _glfwInputJoystickAxis(js, 0, (xis.Gamepad.sThumbLX + 0.5f) / 32767.5f); - _glfwInputJoystickAxis(js, 1, -(xis.Gamepad.sThumbLY + 0.5f) / 32767.5f); - _glfwInputJoystickAxis(js, 2, (xis.Gamepad.sThumbRX + 0.5f) / 32767.5f); - _glfwInputJoystickAxis(js, 3, -(xis.Gamepad.sThumbRY + 0.5f) / 32767.5f); - _glfwInputJoystickAxis(js, 4, xis.Gamepad.bLeftTrigger / 127.5f - 1.f); - _glfwInputJoystickAxis(js, 5, xis.Gamepad.bRightTrigger / 127.5f - 1.f); - - for (i = 0; i < 10; i++) - { - const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; - _glfwInputJoystickButton(js, i, value); - } - - if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) - dpad |= GLFW_HAT_UP; - if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) - dpad |= GLFW_HAT_RIGHT; - if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) - dpad |= GLFW_HAT_DOWN; - if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) - dpad |= GLFW_HAT_LEFT; - - _glfwInputJoystickHat(js, 0, dpad); - } - - return GLFW_TRUE; -} - -void _glfwPlatformUpdateGamepadGUID(char* guid) -{ - if (strcmp(guid + 20, "504944564944") == 0) - { - char original[33]; - strncpy(original, guid, sizeof(original) - 1); - sprintf(guid, "03000000%.4s0000%.4s000000000000", - original, original + 4); - } -} - diff --git a/libs/zglfw/libs/glfw/src/win32_joystick.h b/libs/zglfw/libs/glfw/src/win32_joystick.h deleted file mode 100644 index d591a8262..000000000 --- a/libs/zglfw/libs/glfw/src/win32_joystick.h +++ /dev/null @@ -1,57 +0,0 @@ -//======================================================================== -// GLFW 3.3 Win32 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickWin32 win32 -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } - -#define _GLFW_PLATFORM_MAPPING_NAME "Windows" -#define GLFW_BUILD_WIN32_MAPPINGS - -// Joystick element (axis, button or slider) -// -typedef struct _GLFWjoyobjectWin32 -{ - int offset; - int type; -} _GLFWjoyobjectWin32; - -// Win32-specific per-joystick data -// -typedef struct _GLFWjoystickWin32 -{ - _GLFWjoyobjectWin32* objects; - int objectCount; - IDirectInputDevice8W* device; - DWORD index; - GUID guid; -} _GLFWjoystickWin32; - - -void _glfwInitJoysticksWin32(void); -void _glfwTerminateJoysticksWin32(void); -void _glfwDetectJoystickConnectionWin32(void); -void _glfwDetectJoystickDisconnectionWin32(void); - diff --git a/libs/zglfw/libs/glfw/src/win32_monitor.c b/libs/zglfw/libs/glfw/src/win32_monitor.c deleted file mode 100644 index 67337fd81..000000000 --- a/libs/zglfw/libs/glfw/src/win32_monitor.c +++ /dev/null @@ -1,548 +0,0 @@ -//======================================================================== -// GLFW 3.3 Win32 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include -#include - - -// Callback for EnumDisplayMonitors in createMonitor -// -static BOOL CALLBACK monitorCallback(HMONITOR handle, - HDC dc, - RECT* rect, - LPARAM data) -{ - MONITORINFOEXW mi; - ZeroMemory(&mi, sizeof(mi)); - mi.cbSize = sizeof(mi); - - if (GetMonitorInfoW(handle, (MONITORINFO*) &mi)) - { - _GLFWmonitor* monitor = (_GLFWmonitor*) data; - if (wcscmp(mi.szDevice, monitor->win32.adapterName) == 0) - monitor->win32.handle = handle; - } - - return TRUE; -} - -// Create monitor from an adapter and (optionally) a display -// -static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, - DISPLAY_DEVICEW* display) -{ - _GLFWmonitor* monitor; - int widthMM, heightMM; - char* name; - HDC dc; - DEVMODEW dm; - RECT rect; - - if (display) - name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString); - else - name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString); - if (!name) - return NULL; - - ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(dm); - EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm); - - dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL); - - if (IsWindows8Point1OrGreater()) - { - widthMM = GetDeviceCaps(dc, HORZSIZE); - heightMM = GetDeviceCaps(dc, VERTSIZE); - } - else - { - widthMM = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX)); - heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY)); - } - - DeleteDC(dc); - - monitor = _glfwAllocMonitor(name, widthMM, heightMM); - free(name); - - if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) - monitor->win32.modesPruned = GLFW_TRUE; - - wcscpy(monitor->win32.adapterName, adapter->DeviceName); - WideCharToMultiByte(CP_UTF8, 0, - adapter->DeviceName, -1, - monitor->win32.publicAdapterName, - sizeof(monitor->win32.publicAdapterName), - NULL, NULL); - - if (display) - { - wcscpy(monitor->win32.displayName, display->DeviceName); - WideCharToMultiByte(CP_UTF8, 0, - display->DeviceName, -1, - monitor->win32.publicDisplayName, - sizeof(monitor->win32.publicDisplayName), - NULL, NULL); - } - - rect.left = dm.dmPosition.x; - rect.top = dm.dmPosition.y; - rect.right = dm.dmPosition.x + dm.dmPelsWidth; - rect.bottom = dm.dmPosition.y + dm.dmPelsHeight; - - EnumDisplayMonitors(NULL, &rect, monitorCallback, (LPARAM) monitor); - return monitor; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Poll for changes in the set of connected monitors -// -void _glfwPollMonitorsWin32(void) -{ - int i, disconnectedCount; - _GLFWmonitor** disconnected = NULL; - DWORD adapterIndex, displayIndex; - DISPLAY_DEVICEW adapter, display; - _GLFWmonitor* monitor; - - disconnectedCount = _glfw.monitorCount; - if (disconnectedCount) - { - disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); - memcpy(disconnected, - _glfw.monitors, - _glfw.monitorCount * sizeof(_GLFWmonitor*)); - } - - for (adapterIndex = 0; ; adapterIndex++) - { - int type = _GLFW_INSERT_LAST; - - ZeroMemory(&adapter, sizeof(adapter)); - adapter.cb = sizeof(adapter); - - if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) - break; - - if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) - continue; - - if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) - type = _GLFW_INSERT_FIRST; - - for (displayIndex = 0; ; displayIndex++) - { - ZeroMemory(&display, sizeof(display)); - display.cb = sizeof(display); - - if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) - break; - - if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE)) - continue; - - for (i = 0; i < disconnectedCount; i++) - { - if (disconnected[i] && - wcscmp(disconnected[i]->win32.displayName, - display.DeviceName) == 0) - { - disconnected[i] = NULL; - // handle may have changed, update - EnumDisplayMonitors(NULL, NULL, monitorCallback, (LPARAM) _glfw.monitors[i]); - break; - } - } - - if (i < disconnectedCount) - continue; - - monitor = createMonitor(&adapter, &display); - if (!monitor) - { - free(disconnected); - return; - } - - _glfwInputMonitor(monitor, GLFW_CONNECTED, type); - - type = _GLFW_INSERT_LAST; - } - - // HACK: If an active adapter does not have any display devices - // (as sometimes happens), add it directly as a monitor - if (displayIndex == 0) - { - for (i = 0; i < disconnectedCount; i++) - { - if (disconnected[i] && - wcscmp(disconnected[i]->win32.adapterName, - adapter.DeviceName) == 0) - { - disconnected[i] = NULL; - break; - } - } - - if (i < disconnectedCount) - continue; - - monitor = createMonitor(&adapter, NULL); - if (!monitor) - { - free(disconnected); - return; - } - - _glfwInputMonitor(monitor, GLFW_CONNECTED, type); - } - } - - for (i = 0; i < disconnectedCount; i++) - { - if (disconnected[i]) - _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); - } - - free(disconnected); -} - -// Change the current video mode -// -void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired) -{ - GLFWvidmode current; - const GLFWvidmode* best; - DEVMODEW dm; - LONG result; - - best = _glfwChooseVideoMode(monitor, desired); - _glfwPlatformGetVideoMode(monitor, ¤t); - if (_glfwCompareVideoModes(¤t, best) == 0) - return; - - ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(dm); - dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | - DM_DISPLAYFREQUENCY; - dm.dmPelsWidth = best->width; - dm.dmPelsHeight = best->height; - dm.dmBitsPerPel = best->redBits + best->greenBits + best->blueBits; - dm.dmDisplayFrequency = best->refreshRate; - - if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24) - dm.dmBitsPerPel = 32; - - result = ChangeDisplaySettingsExW(monitor->win32.adapterName, - &dm, - NULL, - CDS_FULLSCREEN, - NULL); - if (result == DISP_CHANGE_SUCCESSFUL) - monitor->win32.modeChanged = GLFW_TRUE; - else - { - const char* description = "Unknown error"; - - if (result == DISP_CHANGE_BADDUALVIEW) - description = "The system uses DualView"; - else if (result == DISP_CHANGE_BADFLAGS) - description = "Invalid flags"; - else if (result == DISP_CHANGE_BADMODE) - description = "Graphics mode not supported"; - else if (result == DISP_CHANGE_BADPARAM) - description = "Invalid parameter"; - else if (result == DISP_CHANGE_FAILED) - description = "Graphics mode failed"; - else if (result == DISP_CHANGE_NOTUPDATED) - description = "Failed to write to registry"; - else if (result == DISP_CHANGE_RESTART) - description = "Computer restart required"; - - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to set video mode: %s", - description); - } -} - -// Restore the previously saved (original) video mode -// -void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) -{ - if (monitor->win32.modeChanged) - { - ChangeDisplaySettingsExW(monitor->win32.adapterName, - NULL, NULL, CDS_FULLSCREEN, NULL); - monitor->win32.modeChanged = GLFW_FALSE; - } -} - -void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) -{ - UINT xdpi, ydpi; - - if (xscale) - *xscale = 0.f; - if (yscale) - *yscale = 0.f; - - if (IsWindows8Point1OrGreater()) - { - if (GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi) != S_OK) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to query monitor DPI"); - return; - } - } - else - { - const HDC dc = GetDC(NULL); - xdpi = GetDeviceCaps(dc, LOGPIXELSX); - ydpi = GetDeviceCaps(dc, LOGPIXELSY); - ReleaseDC(NULL, dc); - } - - if (xscale) - *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI; - if (yscale) - *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) -{ -} - -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) -{ - DEVMODEW dm; - ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(dm); - - EnumDisplaySettingsExW(monitor->win32.adapterName, - ENUM_CURRENT_SETTINGS, - &dm, - EDS_ROTATEDMODE); - - if (xpos) - *xpos = dm.dmPosition.x; - if (ypos) - *ypos = dm.dmPosition.y; -} - -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) -{ - _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale); -} - -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, - int* xpos, int* ypos, - int* width, int* height) -{ - MONITORINFO mi = { sizeof(mi) }; - GetMonitorInfoW(monitor->win32.handle, &mi); - - if (xpos) - *xpos = mi.rcWork.left; - if (ypos) - *ypos = mi.rcWork.top; - if (width) - *width = mi.rcWork.right - mi.rcWork.left; - if (height) - *height = mi.rcWork.bottom - mi.rcWork.top; -} - -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) -{ - int modeIndex = 0, size = 0; - GLFWvidmode* result = NULL; - - *count = 0; - - for (;;) - { - int i; - GLFWvidmode mode; - DEVMODEW dm; - - ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(dm); - - if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm)) - break; - - modeIndex++; - - // Skip modes with less than 15 BPP - if (dm.dmBitsPerPel < 15) - continue; - - mode.width = dm.dmPelsWidth; - mode.height = dm.dmPelsHeight; - mode.refreshRate = dm.dmDisplayFrequency; - _glfwSplitBPP(dm.dmBitsPerPel, - &mode.redBits, - &mode.greenBits, - &mode.blueBits); - - for (i = 0; i < *count; i++) - { - if (_glfwCompareVideoModes(result + i, &mode) == 0) - break; - } - - // Skip duplicate modes - if (i < *count) - continue; - - if (monitor->win32.modesPruned) - { - // Skip modes not supported by the connected displays - if (ChangeDisplaySettingsExW(monitor->win32.adapterName, - &dm, - NULL, - CDS_TEST, - NULL) != DISP_CHANGE_SUCCESSFUL) - { - continue; - } - } - - if (*count == size) - { - size += 128; - result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode)); - } - - (*count)++; - result[*count - 1] = mode; - } - - if (!*count) - { - // HACK: Report the current mode if no valid modes were found - result = calloc(1, sizeof(GLFWvidmode)); - _glfwPlatformGetVideoMode(monitor, result); - *count = 1; - } - - return result; -} - -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) -{ - DEVMODEW dm; - ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(dm); - - EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm); - - mode->width = dm.dmPelsWidth; - mode->height = dm.dmPelsHeight; - mode->refreshRate = dm.dmDisplayFrequency; - _glfwSplitBPP(dm.dmBitsPerPel, - &mode->redBits, - &mode->greenBits, - &mode->blueBits); -} - -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) -{ - HDC dc; - WORD values[3][256]; - - dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); - GetDeviceGammaRamp(dc, values); - DeleteDC(dc); - - _glfwAllocGammaArrays(ramp, 256); - - memcpy(ramp->red, values[0], sizeof(values[0])); - memcpy(ramp->green, values[1], sizeof(values[1])); - memcpy(ramp->blue, values[2], sizeof(values[2])); - - return GLFW_TRUE; -} - -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) -{ - HDC dc; - WORD values[3][256]; - - if (ramp->size != 256) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Gamma ramp size must be 256"); - return; - } - - memcpy(values[0], ramp->red, sizeof(values[0])); - memcpy(values[1], ramp->green, sizeof(values[1])); - memcpy(values[2], ramp->blue, sizeof(values[2])); - - dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); - SetDeviceGammaRamp(dc, values); - DeleteDC(dc); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return monitor->win32.publicAdapterName; -} - -GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return monitor->win32.publicDisplayName; -} - diff --git a/libs/zglfw/libs/glfw/src/win32_platform.h b/libs/zglfw/libs/glfw/src/win32_platform.h deleted file mode 100644 index bf703d7e6..000000000 --- a/libs/zglfw/libs/glfw/src/win32_platform.h +++ /dev/null @@ -1,458 +0,0 @@ -//======================================================================== -// GLFW 3.3 Win32 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== - -// We don't need all the fancy stuff -#ifndef NOMINMAX - #define NOMINMAX -#endif - -#ifndef VC_EXTRALEAN - #define VC_EXTRALEAN -#endif - -#ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN -#endif - -// This is a workaround for the fact that glfw3.h needs to export APIENTRY (for -// example to allow applications to correctly declare a GL_KHR_debug callback) -// but windows.h assumes no one will define APIENTRY before it does -#undef APIENTRY - -// GLFW on Windows is Unicode only and does not work in MBCS mode -#ifndef UNICODE - #define UNICODE -#endif - -// GLFW requires Windows XP or later -#if WINVER < 0x0501 - #undef WINVER - #define WINVER 0x0501 -#endif -#if _WIN32_WINNT < 0x0501 - #undef _WIN32_WINNT - #define _WIN32_WINNT 0x0501 -#endif - -// GLFW uses DirectInput8 interfaces -#define DIRECTINPUT_VERSION 0x0800 - -// GLFW uses OEM cursor resources -#define OEMRESOURCE - -#include -#include -#include -#include -#include - -// HACK: Define macros that some windows.h variants don't -#ifndef WM_MOUSEHWHEEL - #define WM_MOUSEHWHEEL 0x020E -#endif -#ifndef WM_DWMCOMPOSITIONCHANGED - #define WM_DWMCOMPOSITIONCHANGED 0x031E -#endif -#ifndef WM_DWMCOLORIZATIONCOLORCHANGED - #define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 -#endif -#ifndef WM_COPYGLOBALDATA - #define WM_COPYGLOBALDATA 0x0049 -#endif -#ifndef WM_UNICHAR - #define WM_UNICHAR 0x0109 -#endif -#ifndef UNICODE_NOCHAR - #define UNICODE_NOCHAR 0xFFFF -#endif -#ifndef WM_DPICHANGED - #define WM_DPICHANGED 0x02E0 -#endif -#ifndef GET_XBUTTON_WPARAM - #define GET_XBUTTON_WPARAM(w) (HIWORD(w)) -#endif -#ifndef EDS_ROTATEDMODE - #define EDS_ROTATEDMODE 0x00000004 -#endif -#ifndef DISPLAY_DEVICE_ACTIVE - #define DISPLAY_DEVICE_ACTIVE 0x00000001 -#endif -#ifndef _WIN32_WINNT_WINBLUE - #define _WIN32_WINNT_WINBLUE 0x0603 -#endif -#ifndef _WIN32_WINNT_WIN8 - #define _WIN32_WINNT_WIN8 0x0602 -#endif -#ifndef WM_GETDPISCALEDSIZE - #define WM_GETDPISCALEDSIZE 0x02e4 -#endif -#ifndef USER_DEFAULT_SCREEN_DPI - #define USER_DEFAULT_SCREEN_DPI 96 -#endif -#ifndef OCR_HAND - #define OCR_HAND 32649 -#endif - -#if WINVER < 0x0601 -typedef struct -{ - DWORD cbSize; - DWORD ExtStatus; -} CHANGEFILTERSTRUCT; -#ifndef MSGFLT_ALLOW - #define MSGFLT_ALLOW 1 -#endif -#endif /*Windows 7*/ - -#if WINVER < 0x0600 -#define DWM_BB_ENABLE 0x00000001 -#define DWM_BB_BLURREGION 0x00000002 -typedef struct -{ - DWORD dwFlags; - BOOL fEnable; - HRGN hRgnBlur; - BOOL fTransitionOnMaximized; -} DWM_BLURBEHIND; -#else - #include -#endif /*Windows Vista*/ - -#ifndef DPI_ENUMS_DECLARED -typedef enum -{ - PROCESS_DPI_UNAWARE = 0, - PROCESS_SYSTEM_DPI_AWARE = 1, - PROCESS_PER_MONITOR_DPI_AWARE = 2 -} PROCESS_DPI_AWARENESS; -typedef enum -{ - MDT_EFFECTIVE_DPI = 0, - MDT_ANGULAR_DPI = 1, - MDT_RAW_DPI = 2, - MDT_DEFAULT = MDT_EFFECTIVE_DPI -} MONITOR_DPI_TYPE; -#endif /*DPI_ENUMS_DECLARED*/ - -#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 -#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE) -4) -#endif /*DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2*/ - -// Replacement for versionhelpers.h macros, as we cannot rely on the -// application having a correct embedded manifest -// -#define IsWindowsXPOrGreater() \ - _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINXP), \ - LOBYTE(_WIN32_WINNT_WINXP), 0) -#define IsWindowsVistaOrGreater() \ - _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_VISTA), \ - LOBYTE(_WIN32_WINNT_VISTA), 0) -#define IsWindows7OrGreater() \ - _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN7), \ - LOBYTE(_WIN32_WINNT_WIN7), 0) -#define IsWindows8OrGreater() \ - _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN8), \ - LOBYTE(_WIN32_WINNT_WIN8), 0) -#define IsWindows8Point1OrGreater() \ - _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINBLUE), \ - LOBYTE(_WIN32_WINNT_WINBLUE), 0) - -#define _glfwIsWindows10AnniversaryUpdateOrGreaterWin32() \ - _glfwIsWindows10BuildOrGreaterWin32(14393) -#define _glfwIsWindows10CreatorsUpdateOrGreaterWin32() \ - _glfwIsWindows10BuildOrGreaterWin32(15063) - -// HACK: Define macros that some xinput.h variants don't -#ifndef XINPUT_CAPS_WIRELESS - #define XINPUT_CAPS_WIRELESS 0x0002 -#endif -#ifndef XINPUT_DEVSUBTYPE_WHEEL - #define XINPUT_DEVSUBTYPE_WHEEL 0x02 -#endif -#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK - #define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03 -#endif -#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK - #define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04 -#endif -#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD - #define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05 -#endif -#ifndef XINPUT_DEVSUBTYPE_GUITAR - #define XINPUT_DEVSUBTYPE_GUITAR 0x06 -#endif -#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT - #define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08 -#endif -#ifndef XINPUT_DEVSUBTYPE_ARCADE_PAD - #define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13 -#endif -#ifndef XUSER_MAX_COUNT - #define XUSER_MAX_COUNT 4 -#endif - -// HACK: Define macros that some dinput.h variants don't -#ifndef DIDFT_OPTIONAL - #define DIDFT_OPTIONAL 0x80000000 -#endif - -// xinput.dll function pointer typedefs -typedef DWORD (WINAPI * PFN_XInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*); -typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); -#define XInputGetCapabilities _glfw.win32.xinput.GetCapabilities -#define XInputGetState _glfw.win32.xinput.GetState - -// dinput8.dll function pointer typedefs -typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN); -#define DirectInput8Create _glfw.win32.dinput8.Create - -// user32.dll function pointer typedefs -typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void); -typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,CHANGEFILTERSTRUCT*); -typedef BOOL (WINAPI * PFN_EnableNonClientDpiScaling)(HWND); -typedef BOOL (WINAPI * PFN_SetProcessDpiAwarenessContext)(HANDLE); -typedef UINT (WINAPI * PFN_GetDpiForWindow)(HWND); -typedef BOOL (WINAPI * PFN_AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT); -typedef int (WINAPI * PFN_GetSystemMetricsForDpi)(int,UINT); -#define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_ -#define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_ -#define EnableNonClientDpiScaling _glfw.win32.user32.EnableNonClientDpiScaling_ -#define SetProcessDpiAwarenessContext _glfw.win32.user32.SetProcessDpiAwarenessContext_ -#define GetDpiForWindow _glfw.win32.user32.GetDpiForWindow_ -#define AdjustWindowRectExForDpi _glfw.win32.user32.AdjustWindowRectExForDpi_ -#define GetSystemMetricsForDpi _glfw.win32.user32.GetSystemMetricsForDpi_ - -// dwmapi.dll function pointer typedefs -typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); -typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); -typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); -typedef HRESULT (WINAPI * PFN_DwmGetColorizationColor)(DWORD*,BOOL*); -#define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled -#define DwmFlush _glfw.win32.dwmapi.Flush -#define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow -#define DwmGetColorizationColor _glfw.win32.dwmapi.GetColorizationColor - -// shcore.dll function pointer typedefs -typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); -typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); -#define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_ -#define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_ - -// ntdll.dll function pointer typedefs -typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG); -#define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_ - -typedef VkFlags VkWin32SurfaceCreateFlagsKHR; - -typedef struct VkWin32SurfaceCreateInfoKHR -{ - VkStructureType sType; - const void* pNext; - VkWin32SurfaceCreateFlagsKHR flags; - HINSTANCE hinstance; - HWND hwnd; -} VkWin32SurfaceCreateInfoKHR; - -typedef VkResult (APIENTRY *PFN_vkCreateWin32SurfaceKHR)(VkInstance,const VkWin32SurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); -typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice,uint32_t); - -#include "win32_joystick.h" -#include "wgl_context.h" -#include "egl_context.h" -#include "osmesa_context.h" - -#if !defined(_GLFW_WNDCLASSNAME) - #define _GLFW_WNDCLASSNAME L"GLFW30" -#endif - -#define _glfw_dlopen(name) LoadLibraryA(name) -#define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) -#define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) - -#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->win32.handle) -#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY - -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 -#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerWin32 win32 -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32 -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32 -#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsWin32 win32 -#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexWin32 win32 - - -// Win32-specific per-window data -// -typedef struct _GLFWwindowWin32 -{ - HWND handle; - HICON bigIcon; - HICON smallIcon; - - GLFWbool cursorTracked; - GLFWbool frameAction; - GLFWbool iconified; - GLFWbool maximized; - // Whether to enable framebuffer transparency on DWM - GLFWbool transparent; - GLFWbool scaleToMonitor; - - // Cached size used to filter out duplicate events - int width, height; - - // The last received cursor position, regardless of source - int lastCursorPosX, lastCursorPosY; - // The last recevied high surrogate when decoding pairs of UTF-16 messages - WCHAR highSurrogate; -} _GLFWwindowWin32; - -// Win32-specific global data -// -typedef struct _GLFWlibraryWin32 -{ - HINSTANCE instance; - HWND helperWindowHandle; - HDEVNOTIFY deviceNotificationHandle; - DWORD foregroundLockTimeout; - int acquiredMonitorCount; - char* clipboardString; - short int keycodes[512]; - short int scancodes[GLFW_KEY_LAST + 1]; - char keynames[GLFW_KEY_LAST + 1][5]; - // Where to place the cursor when re-enabled - double restoreCursorPosX, restoreCursorPosY; - // The window whose disabled cursor mode is active - _GLFWwindow* disabledCursorWindow; - RAWINPUT* rawInput; - int rawInputSize; - UINT mouseTrailSize; - - struct { - HINSTANCE instance; - PFN_DirectInput8Create Create; - IDirectInput8W* api; - } dinput8; - - struct { - HINSTANCE instance; - PFN_XInputGetCapabilities GetCapabilities; - PFN_XInputGetState GetState; - } xinput; - - struct { - HINSTANCE instance; - PFN_SetProcessDPIAware SetProcessDPIAware_; - PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_; - PFN_EnableNonClientDpiScaling EnableNonClientDpiScaling_; - PFN_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContext_; - PFN_GetDpiForWindow GetDpiForWindow_; - PFN_AdjustWindowRectExForDpi AdjustWindowRectExForDpi_; - PFN_GetSystemMetricsForDpi GetSystemMetricsForDpi_; - } user32; - - struct { - HINSTANCE instance; - PFN_DwmIsCompositionEnabled IsCompositionEnabled; - PFN_DwmFlush Flush; - PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; - PFN_DwmGetColorizationColor GetColorizationColor; - } dwmapi; - - struct { - HINSTANCE instance; - PFN_SetProcessDpiAwareness SetProcessDpiAwareness_; - PFN_GetDpiForMonitor GetDpiForMonitor_; - } shcore; - - struct { - HINSTANCE instance; - PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; - } ntdll; -} _GLFWlibraryWin32; - -// Win32-specific per-monitor data -// -typedef struct _GLFWmonitorWin32 -{ - HMONITOR handle; - // This size matches the static size of DISPLAY_DEVICE.DeviceName - WCHAR adapterName[32]; - WCHAR displayName[32]; - char publicAdapterName[32]; - char publicDisplayName[32]; - GLFWbool modesPruned; - GLFWbool modeChanged; -} _GLFWmonitorWin32; - -// Win32-specific per-cursor data -// -typedef struct _GLFWcursorWin32 -{ - HCURSOR handle; -} _GLFWcursorWin32; - -// Win32-specific global timer data -// -typedef struct _GLFWtimerWin32 -{ - uint64_t frequency; -} _GLFWtimerWin32; - -// Win32-specific thread local storage data -// -typedef struct _GLFWtlsWin32 -{ - GLFWbool allocated; - DWORD index; -} _GLFWtlsWin32; - -// Win32-specific mutex data -// -typedef struct _GLFWmutexWin32 -{ - GLFWbool allocated; - CRITICAL_SECTION section; -} _GLFWmutexWin32; - - -GLFWbool _glfwRegisterWindowClassWin32(void); -void _glfwUnregisterWindowClassWin32(void); - -WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); -char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); -BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp); -BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build); -void _glfwInputErrorWin32(int error, const char* description); -void _glfwUpdateKeyNamesWin32(void); - -void _glfwInitTimerWin32(void); - -void _glfwPollMonitorsWin32(void); -void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); -void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); -void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale); - diff --git a/libs/zglfw/libs/glfw/src/win32_thread.c b/libs/zglfw/libs/glfw/src/win32_thread.c deleted file mode 100644 index ce0686db8..000000000 --- a/libs/zglfw/libs/glfw/src/win32_thread.c +++ /dev/null @@ -1,99 +0,0 @@ -//======================================================================== -// GLFW 3.3 Win32 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) -{ - assert(tls->win32.allocated == GLFW_FALSE); - - tls->win32.index = TlsAlloc(); - if (tls->win32.index == TLS_OUT_OF_INDEXES) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to allocate TLS index"); - return GLFW_FALSE; - } - - tls->win32.allocated = GLFW_TRUE; - return GLFW_TRUE; -} - -void _glfwPlatformDestroyTls(_GLFWtls* tls) -{ - if (tls->win32.allocated) - TlsFree(tls->win32.index); - memset(tls, 0, sizeof(_GLFWtls)); -} - -void* _glfwPlatformGetTls(_GLFWtls* tls) -{ - assert(tls->win32.allocated == GLFW_TRUE); - return TlsGetValue(tls->win32.index); -} - -void _glfwPlatformSetTls(_GLFWtls* tls, void* value) -{ - assert(tls->win32.allocated == GLFW_TRUE); - TlsSetValue(tls->win32.index, value); -} - -GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex) -{ - assert(mutex->win32.allocated == GLFW_FALSE); - InitializeCriticalSection(&mutex->win32.section); - return mutex->win32.allocated = GLFW_TRUE; -} - -void _glfwPlatformDestroyMutex(_GLFWmutex* mutex) -{ - if (mutex->win32.allocated) - DeleteCriticalSection(&mutex->win32.section); - memset(mutex, 0, sizeof(_GLFWmutex)); -} - -void _glfwPlatformLockMutex(_GLFWmutex* mutex) -{ - assert(mutex->win32.allocated == GLFW_TRUE); - EnterCriticalSection(&mutex->win32.section); -} - -void _glfwPlatformUnlockMutex(_GLFWmutex* mutex) -{ - assert(mutex->win32.allocated == GLFW_TRUE); - LeaveCriticalSection(&mutex->win32.section); -} - diff --git a/libs/zglfw/libs/glfw/src/win32_time.c b/libs/zglfw/libs/glfw/src/win32_time.c deleted file mode 100644 index b4e31ab27..000000000 --- a/libs/zglfw/libs/glfw/src/win32_time.c +++ /dev/null @@ -1,60 +0,0 @@ -//======================================================================== -// GLFW 3.3 Win32 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialise timer -// -void _glfwInitTimerWin32(void) -{ - QueryPerformanceFrequency((LARGE_INTEGER*) &_glfw.timer.win32.frequency); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -uint64_t _glfwPlatformGetTimerValue(void) -{ - uint64_t value; - QueryPerformanceCounter((LARGE_INTEGER*) &value); - return value; -} - -uint64_t _glfwPlatformGetTimerFrequency(void) -{ - return _glfw.timer.win32.frequency; -} - diff --git a/libs/zglfw/libs/glfw/src/win32_window.c b/libs/zglfw/libs/glfw/src/win32_window.c deleted file mode 100644 index 073ceee91..000000000 --- a/libs/zglfw/libs/glfw/src/win32_window.c +++ /dev/null @@ -1,2396 +0,0 @@ -//======================================================================== -// GLFW 3.3 Win32 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include -#include -#include - -// Returns the window style for the specified window -// -static DWORD getWindowStyle(const _GLFWwindow* window) -{ - DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - - if (window->monitor) - style |= WS_POPUP; - else - { - style |= WS_SYSMENU | WS_MINIMIZEBOX; - - if (window->decorated) - { - style |= WS_CAPTION; - - if (window->resizable) - style |= WS_MAXIMIZEBOX | WS_THICKFRAME; - } - else - style |= WS_POPUP; - } - - return style; -} - -// Returns the extended window style for the specified window -// -static DWORD getWindowExStyle(const _GLFWwindow* window) -{ - DWORD style = WS_EX_APPWINDOW; - - if (window->monitor || window->floating) - style |= WS_EX_TOPMOST; - - return style; -} - -// Returns the image whose area most closely matches the desired one -// -static const GLFWimage* chooseImage(int count, const GLFWimage* images, - int width, int height) -{ - int i, leastDiff = INT_MAX; - const GLFWimage* closest = NULL; - - for (i = 0; i < count; i++) - { - const int currDiff = abs(images[i].width * images[i].height - - width * height); - if (currDiff < leastDiff) - { - closest = images + i; - leastDiff = currDiff; - } - } - - return closest; -} - -// Creates an RGBA icon or cursor -// -static HICON createIcon(const GLFWimage* image, - int xhot, int yhot, GLFWbool icon) -{ - int i; - HDC dc; - HICON handle; - HBITMAP color, mask; - BITMAPV5HEADER bi; - ICONINFO ii; - unsigned char* target = NULL; - unsigned char* source = image->pixels; - - ZeroMemory(&bi, sizeof(bi)); - bi.bV5Size = sizeof(bi); - bi.bV5Width = image->width; - bi.bV5Height = -image->height; - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_BITFIELDS; - bi.bV5RedMask = 0x00ff0000; - bi.bV5GreenMask = 0x0000ff00; - bi.bV5BlueMask = 0x000000ff; - bi.bV5AlphaMask = 0xff000000; - - dc = GetDC(NULL); - color = CreateDIBSection(dc, - (BITMAPINFO*) &bi, - DIB_RGB_COLORS, - (void**) &target, - NULL, - (DWORD) 0); - ReleaseDC(NULL, dc); - - if (!color) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to create RGBA bitmap"); - return NULL; - } - - mask = CreateBitmap(image->width, image->height, 1, 1, NULL); - if (!mask) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to create mask bitmap"); - DeleteObject(color); - return NULL; - } - - for (i = 0; i < image->width * image->height; i++) - { - target[0] = source[2]; - target[1] = source[1]; - target[2] = source[0]; - target[3] = source[3]; - target += 4; - source += 4; - } - - ZeroMemory(&ii, sizeof(ii)); - ii.fIcon = icon; - ii.xHotspot = xhot; - ii.yHotspot = yhot; - ii.hbmMask = mask; - ii.hbmColor = color; - - handle = CreateIconIndirect(&ii); - - DeleteObject(color); - DeleteObject(mask); - - if (!handle) - { - if (icon) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to create icon"); - } - else - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to create cursor"); - } - } - - return handle; -} - -// Translate content area size to full window size according to styles and DPI -// -static void getFullWindowSize(DWORD style, DWORD exStyle, - int contentWidth, int contentHeight, - int* fullWidth, int* fullHeight, - UINT dpi) -{ - RECT rect = { 0, 0, contentWidth, contentHeight }; - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); - else - AdjustWindowRectEx(&rect, style, FALSE, exStyle); - - *fullWidth = rect.right - rect.left; - *fullHeight = rect.bottom - rect.top; -} - -// Enforce the content area aspect ratio based on which edge is being dragged -// -static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) -{ - int xoff, yoff; - UINT dpi = USER_DEFAULT_SCREEN_DPI; - const float ratio = (float) window->numer / (float) window->denom; - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - dpi = GetDpiForWindow(window->win32.handle); - - getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), - 0, 0, &xoff, &yoff, dpi); - - if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT || - edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT) - { - area->bottom = area->top + yoff + - (int) ((area->right - area->left - xoff) / ratio); - } - else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) - { - area->top = area->bottom - yoff - - (int) ((area->right - area->left - xoff) / ratio); - } - else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM) - { - area->right = area->left + xoff + - (int) ((area->bottom - area->top - yoff) * ratio); - } -} - -// Updates the cursor image according to its cursor mode -// -static void updateCursorImage(_GLFWwindow* window) -{ - if (window->cursorMode == GLFW_CURSOR_NORMAL) - { - if (window->cursor) - SetCursor(window->cursor->win32.handle); - else - SetCursor(LoadCursorW(NULL, IDC_ARROW)); - } - else - SetCursor(NULL); -} - -// Updates the cursor clip rect -// -static void updateClipRect(_GLFWwindow* window) -{ - if (window) - { - RECT clipRect; - GetClientRect(window->win32.handle, &clipRect); - ClientToScreen(window->win32.handle, (POINT*) &clipRect.left); - ClientToScreen(window->win32.handle, (POINT*) &clipRect.right); - ClipCursor(&clipRect); - } - else - ClipCursor(NULL); -} - -// Enables WM_INPUT messages for the mouse for the specified window -// -static void enableRawMouseMotion(_GLFWwindow* window) -{ - const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle }; - - if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to register raw input device"); - } -} - -// Disables WM_INPUT messages for the mouse -// -static void disableRawMouseMotion(_GLFWwindow* window) -{ - const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; - - if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to remove raw input device"); - } -} - -// Apply disabled cursor mode to a focused window -// -static void disableCursor(_GLFWwindow* window) -{ - _glfw.win32.disabledCursorWindow = window; - _glfwPlatformGetCursorPos(window, - &_glfw.win32.restoreCursorPosX, - &_glfw.win32.restoreCursorPosY); - updateCursorImage(window); - _glfwCenterCursorInContentArea(window); - updateClipRect(window); - - if (window->rawMouseMotion) - enableRawMouseMotion(window); -} - -// Exit disabled cursor mode for the specified window -// -static void enableCursor(_GLFWwindow* window) -{ - if (window->rawMouseMotion) - disableRawMouseMotion(window); - - _glfw.win32.disabledCursorWindow = NULL; - updateClipRect(NULL); - _glfwPlatformSetCursorPos(window, - _glfw.win32.restoreCursorPosX, - _glfw.win32.restoreCursorPosY); - updateCursorImage(window); -} - -// Returns whether the cursor is in the content area of the specified window -// -static GLFWbool cursorInContentArea(_GLFWwindow* window) -{ - RECT area; - POINT pos; - - if (!GetCursorPos(&pos)) - return GLFW_FALSE; - - if (WindowFromPoint(pos) != window->win32.handle) - return GLFW_FALSE; - - GetClientRect(window->win32.handle, &area); - ClientToScreen(window->win32.handle, (POINT*) &area.left); - ClientToScreen(window->win32.handle, (POINT*) &area.right); - - return PtInRect(&area, pos); -} - -// Update native window styles to match attributes -// -static void updateWindowStyles(const _GLFWwindow* window) -{ - RECT rect; - DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); - style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP); - style |= getWindowStyle(window); - - GetClientRect(window->win32.handle, &rect); - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - { - AdjustWindowRectExForDpi(&rect, style, FALSE, - getWindowExStyle(window), - GetDpiForWindow(window->win32.handle)); - } - else - AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window)); - - ClientToScreen(window->win32.handle, (POINT*) &rect.left); - ClientToScreen(window->win32.handle, (POINT*) &rect.right); - SetWindowLongW(window->win32.handle, GWL_STYLE, style); - SetWindowPos(window->win32.handle, HWND_TOP, - rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); -} - -// Update window framebuffer transparency -// -static void updateFramebufferTransparency(const _GLFWwindow* window) -{ - BOOL composition, opaque; - DWORD color; - - if (!IsWindowsVistaOrGreater()) - return; - - if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition) - return; - - if (IsWindows8OrGreater() || - (SUCCEEDED(DwmGetColorizationColor(&color, &opaque)) && !opaque)) - { - HRGN region = CreateRectRgn(0, 0, -1, -1); - DWM_BLURBEHIND bb = {0}; - bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; - bb.hRgnBlur = region; - bb.fEnable = TRUE; - - DwmEnableBlurBehindWindow(window->win32.handle, &bb); - DeleteObject(region); - } - else - { - // HACK: Disable framebuffer transparency on Windows 7 when the - // colorization color is opaque, because otherwise the window - // contents is blended additively with the previous frame instead - // of replacing it - DWM_BLURBEHIND bb = {0}; - bb.dwFlags = DWM_BB_ENABLE; - DwmEnableBlurBehindWindow(window->win32.handle, &bb); - } -} - -// Retrieves and translates modifier keys -// -static int getKeyMods(void) -{ - int mods = 0; - - if (GetKeyState(VK_SHIFT) & 0x8000) - mods |= GLFW_MOD_SHIFT; - if (GetKeyState(VK_CONTROL) & 0x8000) - mods |= GLFW_MOD_CONTROL; - if (GetKeyState(VK_MENU) & 0x8000) - mods |= GLFW_MOD_ALT; - if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) - mods |= GLFW_MOD_SUPER; - if (GetKeyState(VK_CAPITAL) & 1) - mods |= GLFW_MOD_CAPS_LOCK; - if (GetKeyState(VK_NUMLOCK) & 1) - mods |= GLFW_MOD_NUM_LOCK; - - return mods; -} - -static void fitToMonitor(_GLFWwindow* window) -{ - MONITORINFO mi = { sizeof(mi) }; - GetMonitorInfoW(window->monitor->win32.handle, &mi); - SetWindowPos(window->win32.handle, HWND_TOPMOST, - mi.rcMonitor.left, - mi.rcMonitor.top, - mi.rcMonitor.right - mi.rcMonitor.left, - mi.rcMonitor.bottom - mi.rcMonitor.top, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS); -} - -// Make the specified window and its video mode active on its monitor -// -static void acquireMonitor(_GLFWwindow* window) -{ - if (!_glfw.win32.acquiredMonitorCount) - { - SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); - - // HACK: When mouse trails are enabled the cursor becomes invisible when - // the OpenGL ICD switches to page flipping - if (IsWindowsXPOrGreater()) - { - SystemParametersInfoW(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0); - SystemParametersInfoW(SPI_SETMOUSETRAILS, 0, 0, 0); - } - } - - if (!window->monitor->window) - _glfw.win32.acquiredMonitorCount++; - - _glfwSetVideoModeWin32(window->monitor, &window->videoMode); - _glfwInputMonitorWindow(window->monitor, window); -} - -// Remove the window and restore the original video mode -// -static void releaseMonitor(_GLFWwindow* window) -{ - if (window->monitor->window != window) - return; - - _glfw.win32.acquiredMonitorCount--; - if (!_glfw.win32.acquiredMonitorCount) - { - SetThreadExecutionState(ES_CONTINUOUS); - - // HACK: Restore mouse trail length saved in acquireMonitor - if (IsWindowsXPOrGreater()) - SystemParametersInfoW(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0); - } - - _glfwInputMonitorWindow(window->monitor, NULL); - _glfwRestoreVideoModeWin32(window->monitor); -} - -// Manually maximize the window, for when SW_MAXIMIZE cannot be used -// -static void maximizeWindowManually(_GLFWwindow* window) -{ - RECT rect; - DWORD style; - MONITORINFO mi = { sizeof(mi) }; - - GetMonitorInfoW(MonitorFromWindow(window->win32.handle, - MONITOR_DEFAULTTONEAREST), &mi); - - rect = mi.rcWork; - - if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) - { - rect.right = _glfw_min(rect.right, rect.left + window->maxwidth); - rect.bottom = _glfw_min(rect.bottom, rect.top + window->maxheight); - } - - style = GetWindowLongW(window->win32.handle, GWL_STYLE); - style |= WS_MAXIMIZE; - SetWindowLongW(window->win32.handle, GWL_STYLE, style); - - if (window->decorated) - { - const DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - { - const UINT dpi = GetDpiForWindow(window->win32.handle); - AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); - OffsetRect(&rect, 0, GetSystemMetricsForDpi(SM_CYCAPTION, dpi)); - } - else - { - AdjustWindowRectEx(&rect, style, FALSE, exStyle); - OffsetRect(&rect, 0, GetSystemMetrics(SM_CYCAPTION)); - } - - rect.bottom = _glfw_min(rect.bottom, mi.rcWork.bottom); - } - - SetWindowPos(window->win32.handle, HWND_TOP, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); -} - -// Window callback function (handles window messages) -// -static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, - WPARAM wParam, LPARAM lParam) -{ - _GLFWwindow* window = GetPropW(hWnd, L"GLFW"); - if (!window) - { - // This is the message handling for the hidden helper window - // and for a regular window during its initial creation - - switch (uMsg) - { - case WM_NCCREATE: - { - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - { - const CREATESTRUCTW* cs = (const CREATESTRUCTW*) lParam; - const _GLFWwndconfig* wndconfig = cs->lpCreateParams; - - // On per-monitor DPI aware V1 systems, only enable - // non-client scaling for windows that scale the client area - // We need WM_GETDPISCALEDSIZE from V2 to keep the client - // area static when the non-client area is scaled - if (wndconfig && wndconfig->scaleToMonitor) - EnableNonClientDpiScaling(hWnd); - } - - break; - } - - case WM_DISPLAYCHANGE: - _glfwPollMonitorsWin32(); - break; - - case WM_DEVICECHANGE: - { - if (wParam == DBT_DEVICEARRIVAL) - { - DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; - if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) - _glfwDetectJoystickConnectionWin32(); - } - else if (wParam == DBT_DEVICEREMOVECOMPLETE) - { - DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; - if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) - _glfwDetectJoystickDisconnectionWin32(); - } - - break; - } - } - - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } - - switch (uMsg) - { - case WM_MOUSEACTIVATE: - { - // HACK: Postpone cursor disabling when the window was activated by - // clicking a caption button - if (HIWORD(lParam) == WM_LBUTTONDOWN) - { - if (LOWORD(lParam) != HTCLIENT) - window->win32.frameAction = GLFW_TRUE; - } - - break; - } - - case WM_CAPTURECHANGED: - { - // HACK: Disable the cursor once the caption button action has been - // completed or cancelled - if (lParam == 0 && window->win32.frameAction) - { - if (window->cursorMode == GLFW_CURSOR_DISABLED) - disableCursor(window); - - window->win32.frameAction = GLFW_FALSE; - } - - break; - } - - case WM_SETFOCUS: - { - _glfwInputWindowFocus(window, GLFW_TRUE); - - // HACK: Do not disable cursor while the user is interacting with - // a caption button - if (window->win32.frameAction) - break; - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - disableCursor(window); - - return 0; - } - - case WM_KILLFOCUS: - { - if (window->cursorMode == GLFW_CURSOR_DISABLED) - enableCursor(window); - - if (window->monitor && window->autoIconify) - _glfwPlatformIconifyWindow(window); - - _glfwInputWindowFocus(window, GLFW_FALSE); - return 0; - } - - case WM_SYSCOMMAND: - { - switch (wParam & 0xfff0) - { - case SC_SCREENSAVE: - case SC_MONITORPOWER: - { - if (window->monitor) - { - // We are running in full screen mode, so disallow - // screen saver and screen blanking - return 0; - } - else - break; - } - - // User trying to access application menu using ALT? - case SC_KEYMENU: - return 0; - } - break; - } - - case WM_CLOSE: - { - _glfwInputWindowCloseRequest(window); - return 0; - } - - case WM_INPUTLANGCHANGE: - { - _glfwUpdateKeyNamesWin32(); - break; - } - - case WM_CHAR: - case WM_SYSCHAR: - { - if (wParam >= 0xd800 && wParam <= 0xdbff) - window->win32.highSurrogate = (WCHAR) wParam; - else - { - uint32_t codepoint = 0; - - if (wParam >= 0xdc00 && wParam <= 0xdfff) - { - if (window->win32.highSurrogate) - { - codepoint += (window->win32.highSurrogate - 0xd800) << 10; - codepoint += (WCHAR) wParam - 0xdc00; - codepoint += 0x10000; - } - } - else - codepoint = (WCHAR) wParam; - - window->win32.highSurrogate = 0; - _glfwInputChar(window, codepoint, getKeyMods(), uMsg != WM_SYSCHAR); - } - - return 0; - } - - case WM_UNICHAR: - { - if (wParam == UNICODE_NOCHAR) - { - // WM_UNICHAR is not sent by Windows, but is sent by some - // third-party input method engine - // Returning TRUE here announces support for this message - return TRUE; - } - - _glfwInputChar(window, (uint32_t) wParam, getKeyMods(), GLFW_TRUE); - return 0; - } - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - case WM_KEYUP: - case WM_SYSKEYUP: - { - int key, scancode; - const int action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS; - const int mods = getKeyMods(); - - scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff)); - if (!scancode) - { - // NOTE: Some synthetic key messages have a scancode of zero - // HACK: Map the virtual key back to a usable scancode - scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC); - } - - // HACK: Alt+PrtSc has a different scancode than just PrtSc - if (scancode == 0x54) - scancode = 0x137; - - // HACK: Ctrl+Pause has a different scancode than just Pause - if (scancode == 0x146) - scancode = 0x45; - - // HACK: CJK IME sets the extended bit for right Shift - if (scancode == 0x136) - scancode = 0x36; - - key = _glfw.win32.keycodes[scancode]; - - // The Ctrl keys require special handling - if (wParam == VK_CONTROL) - { - if (HIWORD(lParam) & KF_EXTENDED) - { - // Right side keys have the extended key bit set - key = GLFW_KEY_RIGHT_CONTROL; - } - else - { - // NOTE: Alt Gr sends Left Ctrl followed by Right Alt - // HACK: We only want one event for Alt Gr, so if we detect - // this sequence we discard this Left Ctrl message now - // and later report Right Alt normally - MSG next; - const DWORD time = GetMessageTime(); - - if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE)) - { - if (next.message == WM_KEYDOWN || - next.message == WM_SYSKEYDOWN || - next.message == WM_KEYUP || - next.message == WM_SYSKEYUP) - { - if (next.wParam == VK_MENU && - (HIWORD(next.lParam) & KF_EXTENDED) && - next.time == time) - { - // Next message is Right Alt down so discard this - break; - } - } - } - - // This is a regular Left Ctrl message - key = GLFW_KEY_LEFT_CONTROL; - } - } - else if (wParam == VK_PROCESSKEY) - { - // IME notifies that keys have been filtered by setting the - // virtual key-code to VK_PROCESSKEY - break; - } - - if (action == GLFW_RELEASE && wParam == VK_SHIFT) - { - // HACK: Release both Shift keys on Shift up event, as when both - // are pressed the first release does not emit any event - // NOTE: The other half of this is in _glfwPlatformPollEvents - _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods); - _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods); - } - else if (wParam == VK_SNAPSHOT) - { - // HACK: Key down is not reported for the Print Screen key - _glfwInputKey(window, key, scancode, GLFW_PRESS, mods); - _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods); - } - else - _glfwInputKey(window, key, scancode, action, mods); - - break; - } - - case WM_LBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_XBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - case WM_XBUTTONUP: - { - int i, button, action; - - if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) - button = GLFW_MOUSE_BUTTON_LEFT; - else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP) - button = GLFW_MOUSE_BUTTON_RIGHT; - else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) - button = GLFW_MOUSE_BUTTON_MIDDLE; - else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) - button = GLFW_MOUSE_BUTTON_4; - else - button = GLFW_MOUSE_BUTTON_5; - - if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || - uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN) - { - action = GLFW_PRESS; - } - else - action = GLFW_RELEASE; - - for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) - { - if (window->mouseButtons[i] == GLFW_PRESS) - break; - } - - if (i > GLFW_MOUSE_BUTTON_LAST) - SetCapture(hWnd); - - _glfwInputMouseClick(window, button, action, getKeyMods()); - - for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) - { - if (window->mouseButtons[i] == GLFW_PRESS) - break; - } - - if (i > GLFW_MOUSE_BUTTON_LAST) - ReleaseCapture(); - - if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) - return TRUE; - - return 0; - } - - case WM_MOUSEMOVE: - { - const int x = GET_X_LPARAM(lParam); - const int y = GET_Y_LPARAM(lParam); - - if (!window->win32.cursorTracked) - { - TRACKMOUSEEVENT tme; - ZeroMemory(&tme, sizeof(tme)); - tme.cbSize = sizeof(tme); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = window->win32.handle; - TrackMouseEvent(&tme); - - window->win32.cursorTracked = GLFW_TRUE; - _glfwInputCursorEnter(window, GLFW_TRUE); - } - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - const int dx = x - window->win32.lastCursorPosX; - const int dy = y - window->win32.lastCursorPosY; - - if (_glfw.win32.disabledCursorWindow != window) - break; - if (window->rawMouseMotion) - break; - - _glfwInputCursorPos(window, - window->virtualCursorPosX + dx, - window->virtualCursorPosY + dy); - } - else - _glfwInputCursorPos(window, x, y); - - window->win32.lastCursorPosX = x; - window->win32.lastCursorPosY = y; - - return 0; - } - - case WM_INPUT: - { - UINT size = 0; - HRAWINPUT ri = (HRAWINPUT) lParam; - RAWINPUT* data = NULL; - int dx, dy; - - if (_glfw.win32.disabledCursorWindow != window) - break; - if (!window->rawMouseMotion) - break; - - GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); - if (size > (UINT) _glfw.win32.rawInputSize) - { - free(_glfw.win32.rawInput); - _glfw.win32.rawInput = calloc(size, 1); - _glfw.win32.rawInputSize = size; - } - - size = _glfw.win32.rawInputSize; - if (GetRawInputData(ri, RID_INPUT, - _glfw.win32.rawInput, &size, - sizeof(RAWINPUTHEADER)) == (UINT) -1) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to retrieve raw input data"); - break; - } - - data = _glfw.win32.rawInput; - if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) - { - dx = data->data.mouse.lLastX - window->win32.lastCursorPosX; - dy = data->data.mouse.lLastY - window->win32.lastCursorPosY; - } - else - { - dx = data->data.mouse.lLastX; - dy = data->data.mouse.lLastY; - } - - _glfwInputCursorPos(window, - window->virtualCursorPosX + dx, - window->virtualCursorPosY + dy); - - window->win32.lastCursorPosX += dx; - window->win32.lastCursorPosY += dy; - break; - } - - case WM_MOUSELEAVE: - { - window->win32.cursorTracked = GLFW_FALSE; - _glfwInputCursorEnter(window, GLFW_FALSE); - return 0; - } - - case WM_MOUSEWHEEL: - { - _glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA); - return 0; - } - - case WM_MOUSEHWHEEL: - { - // This message is only sent on Windows Vista and later - // NOTE: The X-axis is inverted for consistency with macOS and X11 - _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0); - return 0; - } - - case WM_ENTERSIZEMOVE: - case WM_ENTERMENULOOP: - { - if (window->win32.frameAction) - break; - - // HACK: Enable the cursor while the user is moving or - // resizing the window or using the window menu - if (window->cursorMode == GLFW_CURSOR_DISABLED) - enableCursor(window); - - break; - } - - case WM_EXITSIZEMOVE: - case WM_EXITMENULOOP: - { - if (window->win32.frameAction) - break; - - // HACK: Disable the cursor once the user is done moving or - // resizing the window or using the menu - if (window->cursorMode == GLFW_CURSOR_DISABLED) - disableCursor(window); - - break; - } - - case WM_SIZE: - { - const int width = LOWORD(lParam); - const int height = HIWORD(lParam); - const GLFWbool iconified = wParam == SIZE_MINIMIZED; - const GLFWbool maximized = wParam == SIZE_MAXIMIZED || - (window->win32.maximized && - wParam != SIZE_RESTORED); - - if (_glfw.win32.disabledCursorWindow == window) - updateClipRect(window); - - if (window->win32.iconified != iconified) - _glfwInputWindowIconify(window, iconified); - - if (window->win32.maximized != maximized) - _glfwInputWindowMaximize(window, maximized); - - if (width != window->win32.width || height != window->win32.height) - { - window->win32.width = width; - window->win32.height = height; - - _glfwInputFramebufferSize(window, width, height); - _glfwInputWindowSize(window, width, height); - } - - if (window->monitor && window->win32.iconified != iconified) - { - if (iconified) - releaseMonitor(window); - else - { - acquireMonitor(window); - fitToMonitor(window); - } - } - - window->win32.iconified = iconified; - window->win32.maximized = maximized; - return 0; - } - - case WM_MOVE: - { - if (_glfw.win32.disabledCursorWindow == window) - updateClipRect(window); - - // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as - // those macros do not handle negative window positions correctly - _glfwInputWindowPos(window, - GET_X_LPARAM(lParam), - GET_Y_LPARAM(lParam)); - return 0; - } - - case WM_SIZING: - { - if (window->numer == GLFW_DONT_CARE || - window->denom == GLFW_DONT_CARE) - { - break; - } - - applyAspectRatio(window, (int) wParam, (RECT*) lParam); - return TRUE; - } - - case WM_GETMINMAXINFO: - { - int xoff, yoff; - UINT dpi = USER_DEFAULT_SCREEN_DPI; - MINMAXINFO* mmi = (MINMAXINFO*) lParam; - - if (window->monitor) - break; - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - dpi = GetDpiForWindow(window->win32.handle); - - getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), - 0, 0, &xoff, &yoff, dpi); - - if (window->minwidth != GLFW_DONT_CARE && - window->minheight != GLFW_DONT_CARE) - { - mmi->ptMinTrackSize.x = window->minwidth + xoff; - mmi->ptMinTrackSize.y = window->minheight + yoff; - } - - if (window->maxwidth != GLFW_DONT_CARE && - window->maxheight != GLFW_DONT_CARE) - { - mmi->ptMaxTrackSize.x = window->maxwidth + xoff; - mmi->ptMaxTrackSize.y = window->maxheight + yoff; - } - - if (!window->decorated) - { - MONITORINFO mi; - const HMONITOR mh = MonitorFromWindow(window->win32.handle, - MONITOR_DEFAULTTONEAREST); - - ZeroMemory(&mi, sizeof(mi)); - mi.cbSize = sizeof(mi); - GetMonitorInfoW(mh, &mi); - - mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left; - mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top; - mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left; - mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top; - } - - return 0; - } - - case WM_PAINT: - { - _glfwInputWindowDamage(window); - break; - } - - case WM_ERASEBKGND: - { - return TRUE; - } - - case WM_NCACTIVATE: - case WM_NCPAINT: - { - // Prevent title bar from being drawn after restoring a minimized - // undecorated window - if (!window->decorated) - return TRUE; - - break; - } - - case WM_DWMCOMPOSITIONCHANGED: - case WM_DWMCOLORIZATIONCOLORCHANGED: - { - if (window->win32.transparent) - updateFramebufferTransparency(window); - return 0; - } - - case WM_GETDPISCALEDSIZE: - { - if (window->win32.scaleToMonitor) - break; - - // Adjust the window size to keep the content area size constant - if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) - { - RECT source = {0}, target = {0}; - SIZE* size = (SIZE*) lParam; - - AdjustWindowRectExForDpi(&source, getWindowStyle(window), - FALSE, getWindowExStyle(window), - GetDpiForWindow(window->win32.handle)); - AdjustWindowRectExForDpi(&target, getWindowStyle(window), - FALSE, getWindowExStyle(window), - LOWORD(wParam)); - - size->cx += (target.right - target.left) - - (source.right - source.left); - size->cy += (target.bottom - target.top) - - (source.bottom - source.top); - return TRUE; - } - - break; - } - - case WM_DPICHANGED: - { - const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; - const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; - - // Resize windowed mode windows that either permit rescaling or that - // need it to compensate for non-client area scaling - if (!window->monitor && - (window->win32.scaleToMonitor || - _glfwIsWindows10CreatorsUpdateOrGreaterWin32())) - { - RECT* suggested = (RECT*) lParam; - SetWindowPos(window->win32.handle, HWND_TOP, - suggested->left, - suggested->top, - suggested->right - suggested->left, - suggested->bottom - suggested->top, - SWP_NOACTIVATE | SWP_NOZORDER); - } - - _glfwInputWindowContentScale(window, xscale, yscale); - break; - } - - case WM_SETCURSOR: - { - if (LOWORD(lParam) == HTCLIENT) - { - updateCursorImage(window); - return TRUE; - } - - break; - } - - case WM_DROPFILES: - { - HDROP drop = (HDROP) wParam; - POINT pt; - int i; - - const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0); - char** paths = calloc(count, sizeof(char*)); - - // Move the mouse to the position of the drop - DragQueryPoint(drop, &pt); - _glfwInputCursorPos(window, pt.x, pt.y); - - for (i = 0; i < count; i++) - { - const UINT length = DragQueryFileW(drop, i, NULL, 0); - WCHAR* buffer = calloc((size_t) length + 1, sizeof(WCHAR)); - - DragQueryFileW(drop, i, buffer, length + 1); - paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); - - free(buffer); - } - - _glfwInputDrop(window, count, (const char**) paths); - - for (i = 0; i < count; i++) - free(paths[i]); - free(paths); - - DragFinish(drop); - return 0; - } - } - - return DefWindowProcW(hWnd, uMsg, wParam, lParam); -} - -// Creates the GLFW window -// -static int createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWfbconfig* fbconfig) -{ - int xpos, ypos, fullWidth, fullHeight; - WCHAR* wideTitle; - DWORD style = getWindowStyle(window); - DWORD exStyle = getWindowExStyle(window); - - if (window->monitor) - { - MONITORINFO mi = { sizeof(mi) }; - GetMonitorInfoW(window->monitor->win32.handle, &mi); - - // NOTE: This window placement is temporary and approximate, as the - // correct position and size cannot be known until the monitor - // video mode has been picked in _glfwSetVideoModeWin32 - xpos = mi.rcMonitor.left; - ypos = mi.rcMonitor.top; - fullWidth = mi.rcMonitor.right - mi.rcMonitor.left; - fullHeight = mi.rcMonitor.bottom - mi.rcMonitor.top; - } - else - { - xpos = CW_USEDEFAULT; - ypos = CW_USEDEFAULT; - - window->win32.maximized = wndconfig->maximized; - if (wndconfig->maximized) - style |= WS_MAXIMIZE; - - getFullWindowSize(style, exStyle, - wndconfig->width, wndconfig->height, - &fullWidth, &fullHeight, - USER_DEFAULT_SCREEN_DPI); - } - - wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title); - if (!wideTitle) - return GLFW_FALSE; - - window->win32.handle = CreateWindowExW(exStyle, - _GLFW_WNDCLASSNAME, - wideTitle, - style, - xpos, ypos, - fullWidth, fullHeight, - NULL, // No parent window - NULL, // No window menu - _glfw.win32.instance, - (LPVOID) wndconfig); - - free(wideTitle); - - if (!window->win32.handle) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to create window"); - return GLFW_FALSE; - } - - SetPropW(window->win32.handle, L"GLFW", window); - - if (IsWindows7OrGreater()) - { - ChangeWindowMessageFilterEx(window->win32.handle, - WM_DROPFILES, MSGFLT_ALLOW, NULL); - ChangeWindowMessageFilterEx(window->win32.handle, - WM_COPYDATA, MSGFLT_ALLOW, NULL); - ChangeWindowMessageFilterEx(window->win32.handle, - WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); - } - - window->win32.scaleToMonitor = wndconfig->scaleToMonitor; - - if (!window->monitor) - { - RECT rect = { 0, 0, wndconfig->width, wndconfig->height }; - WINDOWPLACEMENT wp = { sizeof(wp) }; - const HMONITOR mh = MonitorFromWindow(window->win32.handle, - MONITOR_DEFAULTTONEAREST); - - // Adjust window rect to account for DPI scaling of the window frame and - // (if enabled) DPI scaling of the content area - // This cannot be done until we know what monitor the window was placed on - // Only update the restored window rect as the window may be maximized - - if (wndconfig->scaleToMonitor) - { - float xscale, yscale; - _glfwGetMonitorContentScaleWin32(mh, &xscale, &yscale); - - if (xscale > 0.f && yscale > 0.f) - { - rect.right = (int) (rect.right * xscale); - rect.bottom = (int) (rect.bottom * yscale); - } - } - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - { - AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, - GetDpiForWindow(window->win32.handle)); - } - else - AdjustWindowRectEx(&rect, style, FALSE, exStyle); - - GetWindowPlacement(window->win32.handle, &wp); - OffsetRect(&rect, - wp.rcNormalPosition.left - rect.left, - wp.rcNormalPosition.top - rect.top); - - wp.rcNormalPosition = rect; - wp.showCmd = SW_HIDE; - SetWindowPlacement(window->win32.handle, &wp); - - // Adjust rect of maximized undecorated window, because by default Windows will - // make such a window cover the whole monitor instead of its workarea - - if (wndconfig->maximized && !wndconfig->decorated) - { - MONITORINFO mi = { sizeof(mi) }; - GetMonitorInfoW(mh, &mi); - - SetWindowPos(window->win32.handle, HWND_TOP, - mi.rcWork.left, - mi.rcWork.top, - mi.rcWork.right - mi.rcWork.left, - mi.rcWork.bottom - mi.rcWork.top, - SWP_NOACTIVATE | SWP_NOZORDER); - } - } - - DragAcceptFiles(window->win32.handle, TRUE); - - if (fbconfig->transparent) - { - updateFramebufferTransparency(window); - window->win32.transparent = GLFW_TRUE; - } - - _glfwPlatformGetWindowSize(window, &window->win32.width, &window->win32.height); - - return GLFW_TRUE; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Registers the GLFW window class -// -GLFWbool _glfwRegisterWindowClassWin32(void) -{ - WNDCLASSEXW wc; - - ZeroMemory(&wc, sizeof(wc)); - wc.cbSize = sizeof(wc); - wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wc.lpfnWndProc = windowProc; - wc.hInstance = _glfw.win32.instance; - wc.hCursor = LoadCursorW(NULL, IDC_ARROW); - wc.lpszClassName = _GLFW_WNDCLASSNAME; - - // Load user-provided icon if available - wc.hIcon = LoadImageW(GetModuleHandleW(NULL), - L"GLFW_ICON", IMAGE_ICON, - 0, 0, LR_DEFAULTSIZE | LR_SHARED); - if (!wc.hIcon) - { - // No user-provided icon found, load default icon - wc.hIcon = LoadImageW(NULL, - IDI_APPLICATION, IMAGE_ICON, - 0, 0, LR_DEFAULTSIZE | LR_SHARED); - } - - if (!RegisterClassExW(&wc)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to register window class"); - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -// Unregisters the GLFW window class -// -void _glfwUnregisterWindowClassWin32(void) -{ - UnregisterClassW(_GLFW_WNDCLASSNAME, _glfw.win32.instance); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - if (!createNativeWindow(window, wndconfig, fbconfig)) - return GLFW_FALSE; - - if (ctxconfig->client != GLFW_NO_API) - { - if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) - { - if (!_glfwInitWGL()) - return GLFW_FALSE; - if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) - { - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) - { - if (!_glfwInitOSMesa()) - return GLFW_FALSE; - if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - - if (!_glfwRefreshContextAttribs(window, ctxconfig)) - return GLFW_FALSE; - } - - if (window->monitor) - { - _glfwPlatformShowWindow(window); - _glfwPlatformFocusWindow(window); - acquireMonitor(window); - fitToMonitor(window); - - if (wndconfig->centerCursor) - _glfwCenterCursorInContentArea(window); - } - else - { - if (wndconfig->visible) - { - _glfwPlatformShowWindow(window); - if (wndconfig->focused) - _glfwPlatformFocusWindow(window); - } - } - - return GLFW_TRUE; -} - -void _glfwPlatformDestroyWindow(_GLFWwindow* window) -{ - if (window->monitor) - releaseMonitor(window); - - if (window->context.destroy) - window->context.destroy(window); - - if (_glfw.win32.disabledCursorWindow == window) - _glfw.win32.disabledCursorWindow = NULL; - - if (window->win32.handle) - { - RemovePropW(window->win32.handle, L"GLFW"); - DestroyWindow(window->win32.handle); - window->win32.handle = NULL; - } - - if (window->win32.bigIcon) - DestroyIcon(window->win32.bigIcon); - - if (window->win32.smallIcon) - DestroyIcon(window->win32.smallIcon); -} - -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) -{ - WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title); - if (!wideTitle) - return; - - SetWindowTextW(window->win32.handle, wideTitle); - free(wideTitle); -} - -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images) -{ - HICON bigIcon = NULL, smallIcon = NULL; - - if (count) - { - const GLFWimage* bigImage = chooseImage(count, images, - GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON)); - const GLFWimage* smallImage = chooseImage(count, images, - GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CYSMICON)); - - bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE); - smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE); - } - else - { - bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON); - smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM); - } - - SendMessageW(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon); - SendMessageW(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon); - - if (window->win32.bigIcon) - DestroyIcon(window->win32.bigIcon); - - if (window->win32.smallIcon) - DestroyIcon(window->win32.smallIcon); - - if (count) - { - window->win32.bigIcon = bigIcon; - window->win32.smallIcon = smallIcon; - } -} - -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) -{ - POINT pos = { 0, 0 }; - ClientToScreen(window->win32.handle, &pos); - - if (xpos) - *xpos = pos.x; - if (ypos) - *ypos = pos.y; -} - -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) -{ - RECT rect = { xpos, ypos, xpos, ypos }; - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - { - AdjustWindowRectExForDpi(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window), - GetDpiForWindow(window->win32.handle)); - } - else - { - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); - } - - SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0, - SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE); -} - -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) -{ - RECT area; - GetClientRect(window->win32.handle, &area); - - if (width) - *width = area.right; - if (height) - *height = area.bottom; -} - -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) -{ - if (window->monitor) - { - if (window->monitor->window == window) - { - acquireMonitor(window); - fitToMonitor(window); - } - } - else - { - RECT rect = { 0, 0, width, height }; - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - { - AdjustWindowRectExForDpi(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window), - GetDpiForWindow(window->win32.handle)); - } - else - { - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); - } - - SetWindowPos(window->win32.handle, HWND_TOP, - 0, 0, rect.right - rect.left, rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER); - } -} - -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) -{ - RECT area; - - if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) && - (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)) - { - return; - } - - GetWindowRect(window->win32.handle, &area); - MoveWindow(window->win32.handle, - area.left, area.top, - area.right - area.left, - area.bottom - area.top, TRUE); -} - -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) -{ - RECT area; - - if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) - return; - - GetWindowRect(window->win32.handle, &area); - applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area); - MoveWindow(window->win32.handle, - area.left, area.top, - area.right - area.left, - area.bottom - area.top, TRUE); -} - -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) -{ - _glfwPlatformGetWindowSize(window, width, height); -} - -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) -{ - RECT rect; - int width, height; - - _glfwPlatformGetWindowSize(window, &width, &height); - SetRect(&rect, 0, 0, width, height); - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - { - AdjustWindowRectExForDpi(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window), - GetDpiForWindow(window->win32.handle)); - } - else - { - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); - } - - if (left) - *left = -rect.left; - if (top) - *top = -rect.top; - if (right) - *right = rect.right - width; - if (bottom) - *bottom = rect.bottom - height; -} - -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) -{ - const HANDLE handle = MonitorFromWindow(window->win32.handle, - MONITOR_DEFAULTTONEAREST); - _glfwGetMonitorContentScaleWin32(handle, xscale, yscale); -} - -void _glfwPlatformIconifyWindow(_GLFWwindow* window) -{ - ShowWindow(window->win32.handle, SW_MINIMIZE); -} - -void _glfwPlatformRestoreWindow(_GLFWwindow* window) -{ - ShowWindow(window->win32.handle, SW_RESTORE); -} - -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) -{ - if (IsWindowVisible(window->win32.handle)) - ShowWindow(window->win32.handle, SW_MAXIMIZE); - else - maximizeWindowManually(window); -} - -void _glfwPlatformShowWindow(_GLFWwindow* window) -{ - ShowWindow(window->win32.handle, SW_SHOWNA); -} - -void _glfwPlatformHideWindow(_GLFWwindow* window) -{ - ShowWindow(window->win32.handle, SW_HIDE); -} - -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) -{ - FlashWindow(window->win32.handle, TRUE); -} - -void _glfwPlatformFocusWindow(_GLFWwindow* window) -{ - BringWindowToTop(window->win32.handle); - SetForegroundWindow(window->win32.handle); - SetFocus(window->win32.handle); -} - -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) -{ - if (window->monitor == monitor) - { - if (monitor) - { - if (monitor->window == window) - { - acquireMonitor(window); - fitToMonitor(window); - } - } - else - { - RECT rect = { xpos, ypos, xpos + width, ypos + height }; - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - { - AdjustWindowRectExForDpi(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window), - GetDpiForWindow(window->win32.handle)); - } - else - { - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); - } - - SetWindowPos(window->win32.handle, HWND_TOP, - rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER); - } - - return; - } - - if (window->monitor) - releaseMonitor(window); - - _glfwInputWindowMonitor(window, monitor); - - if (window->monitor) - { - MONITORINFO mi = { sizeof(mi) }; - UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS; - - if (window->decorated) - { - DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); - style &= ~WS_OVERLAPPEDWINDOW; - style |= getWindowStyle(window); - SetWindowLongW(window->win32.handle, GWL_STYLE, style); - flags |= SWP_FRAMECHANGED; - } - - acquireMonitor(window); - - GetMonitorInfoW(window->monitor->win32.handle, &mi); - SetWindowPos(window->win32.handle, HWND_TOPMOST, - mi.rcMonitor.left, - mi.rcMonitor.top, - mi.rcMonitor.right - mi.rcMonitor.left, - mi.rcMonitor.bottom - mi.rcMonitor.top, - flags); - } - else - { - HWND after; - RECT rect = { xpos, ypos, xpos + width, ypos + height }; - DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); - UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS; - - if (window->decorated) - { - style &= ~WS_POPUP; - style |= getWindowStyle(window); - SetWindowLongW(window->win32.handle, GWL_STYLE, style); - - flags |= SWP_FRAMECHANGED; - } - - if (window->floating) - after = HWND_TOPMOST; - else - after = HWND_NOTOPMOST; - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - { - AdjustWindowRectExForDpi(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window), - GetDpiForWindow(window->win32.handle)); - } - else - { - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); - } - - SetWindowPos(window->win32.handle, after, - rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - flags); - } -} - -int _glfwPlatformWindowFocused(_GLFWwindow* window) -{ - return window->win32.handle == GetActiveWindow(); -} - -int _glfwPlatformWindowIconified(_GLFWwindow* window) -{ - return IsIconic(window->win32.handle); -} - -int _glfwPlatformWindowVisible(_GLFWwindow* window) -{ - return IsWindowVisible(window->win32.handle); -} - -int _glfwPlatformWindowMaximized(_GLFWwindow* window) -{ - return IsZoomed(window->win32.handle); -} - -int _glfwPlatformWindowHovered(_GLFWwindow* window) -{ - return cursorInContentArea(window); -} - -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) -{ - BOOL composition, opaque; - DWORD color; - - if (!window->win32.transparent) - return GLFW_FALSE; - - if (!IsWindowsVistaOrGreater()) - return GLFW_FALSE; - - if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition) - return GLFW_FALSE; - - if (!IsWindows8OrGreater()) - { - // HACK: Disable framebuffer transparency on Windows 7 when the - // colorization color is opaque, because otherwise the window - // contents is blended additively with the previous frame instead - // of replacing it - if (FAILED(DwmGetColorizationColor(&color, &opaque)) || opaque) - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) -{ - updateWindowStyles(window); -} - -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) -{ - updateWindowStyles(window); -} - -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) -{ - const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST; - SetWindowPos(window->win32.handle, after, 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); -} - -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) -{ - BYTE alpha; - DWORD flags; - - if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) && - GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags)) - { - if (flags & LWA_ALPHA) - return alpha / 255.f; - } - - return 1.f; -} - -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) -{ - if (opacity < 1.f) - { - const BYTE alpha = (BYTE) (255 * opacity); - DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); - style |= WS_EX_LAYERED; - SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); - SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA); - } - else - { - DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); - style &= ~WS_EX_LAYERED; - SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); - } -} - -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) -{ - if (_glfw.win32.disabledCursorWindow != window) - return; - - if (enabled) - enableRawMouseMotion(window); - else - disableRawMouseMotion(window); -} - -GLFWbool _glfwPlatformRawMouseMotionSupported(void) -{ - return GLFW_TRUE; -} - -void _glfwPlatformPollEvents(void) -{ - MSG msg; - HWND handle; - _GLFWwindow* window; - - while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) - { - if (msg.message == WM_QUIT) - { - // NOTE: While GLFW does not itself post WM_QUIT, other processes - // may post it to this one, for example Task Manager - // HACK: Treat WM_QUIT as a close on all windows - - window = _glfw.windowListHead; - while (window) - { - _glfwInputWindowCloseRequest(window); - window = window->next; - } - } - else - { - TranslateMessage(&msg); - DispatchMessageW(&msg); - } - } - - // HACK: Release modifier keys that the system did not emit KEYUP for - // NOTE: Shift keys on Windows tend to "stick" when both are pressed as - // no key up message is generated by the first key release - // NOTE: Windows key is not reported as released by the Win+V hotkey - // Other Win hotkeys are handled implicitly by _glfwInputWindowFocus - // because they change the input focus - // NOTE: The other half of this is in the WM_*KEY* handler in windowProc - handle = GetActiveWindow(); - if (handle) - { - window = GetPropW(handle, L"GLFW"); - if (window) - { - int i; - const int keys[4][2] = - { - { VK_LSHIFT, GLFW_KEY_LEFT_SHIFT }, - { VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT }, - { VK_LWIN, GLFW_KEY_LEFT_SUPER }, - { VK_RWIN, GLFW_KEY_RIGHT_SUPER } - }; - - for (i = 0; i < 4; i++) - { - const int vk = keys[i][0]; - const int key = keys[i][1]; - const int scancode = _glfw.win32.scancodes[key]; - - if ((GetKeyState(vk) & 0x8000)) - continue; - if (window->keys[key] != GLFW_PRESS) - continue; - - _glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods()); - } - } - } - - window = _glfw.win32.disabledCursorWindow; - if (window) - { - int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - - // NOTE: Re-center the cursor only if it has moved since the last call, - // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE - if (window->win32.lastCursorPosX != width / 2 || - window->win32.lastCursorPosY != height / 2) - { - _glfwPlatformSetCursorPos(window, width / 2, height / 2); - } - } -} - -void _glfwPlatformWaitEvents(void) -{ - WaitMessage(); - - _glfwPlatformPollEvents(); -} - -void _glfwPlatformWaitEventsTimeout(double timeout) -{ - MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS); - - _glfwPlatformPollEvents(); -} - -void _glfwPlatformPostEmptyEvent(void) -{ - PostMessageW(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); -} - -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) -{ - POINT pos; - - if (GetCursorPos(&pos)) - { - ScreenToClient(window->win32.handle, &pos); - - if (xpos) - *xpos = pos.x; - if (ypos) - *ypos = pos.y; - } -} - -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) -{ - POINT pos = { (int) xpos, (int) ypos }; - - // Store the new position so it can be recognized later - window->win32.lastCursorPosX = pos.x; - window->win32.lastCursorPosY = pos.y; - - ClientToScreen(window->win32.handle, &pos); - SetCursorPos(pos.x, pos.y); -} - -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) -{ - if (mode == GLFW_CURSOR_DISABLED) - { - if (_glfwPlatformWindowFocused(window)) - disableCursor(window); - } - else if (_glfw.win32.disabledCursorWindow == window) - enableCursor(window); - else if (cursorInContentArea(window)) - updateCursorImage(window); -} - -const char* _glfwPlatformGetScancodeName(int scancode) -{ - if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) || - _glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN) - { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); - return NULL; - } - - return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]]; -} - -int _glfwPlatformGetKeyScancode(int key) -{ - return _glfw.win32.scancodes[key]; -} - -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) -{ - cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE); - if (!cursor->win32.handle) - return GLFW_FALSE; - - return GLFW_TRUE; -} - -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) -{ - int id = 0; - - if (shape == GLFW_ARROW_CURSOR) - id = OCR_NORMAL; - else if (shape == GLFW_IBEAM_CURSOR) - id = OCR_IBEAM; - else if (shape == GLFW_CROSSHAIR_CURSOR) - id = OCR_CROSS; - else if (shape == GLFW_HAND_CURSOR) - id = OCR_HAND; - else if (shape == GLFW_HRESIZE_CURSOR) - id = OCR_SIZEWE; - else if (shape == GLFW_VRESIZE_CURSOR) - id = OCR_SIZENS; - else - return GLFW_FALSE; - - cursor->win32.handle = LoadImageW(NULL, - MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, - LR_DEFAULTSIZE | LR_SHARED); - if (!cursor->win32.handle) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to create standard cursor"); - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) -{ - if (cursor->win32.handle) - DestroyIcon((HICON) cursor->win32.handle); -} - -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) -{ - if (cursorInContentArea(window)) - updateCursorImage(window); -} - -void _glfwPlatformSetClipboardString(const char* string) -{ - int characterCount; - HANDLE object; - WCHAR* buffer; - - characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0); - if (!characterCount) - return; - - object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR)); - if (!object) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to allocate global handle for clipboard"); - return; - } - - buffer = GlobalLock(object); - if (!buffer) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to lock global handle"); - GlobalFree(object); - return; - } - - MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount); - GlobalUnlock(object); - - if (!OpenClipboard(_glfw.win32.helperWindowHandle)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to open clipboard"); - GlobalFree(object); - return; - } - - EmptyClipboard(); - SetClipboardData(CF_UNICODETEXT, object); - CloseClipboard(); -} - -const char* _glfwPlatformGetClipboardString(void) -{ - HANDLE object; - WCHAR* buffer; - - if (!OpenClipboard(_glfw.win32.helperWindowHandle)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to open clipboard"); - return NULL; - } - - object = GetClipboardData(CF_UNICODETEXT); - if (!object) - { - _glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE, - "Win32: Failed to convert clipboard to string"); - CloseClipboard(); - return NULL; - } - - buffer = GlobalLock(object); - if (!buffer) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to lock global handle"); - CloseClipboard(); - return NULL; - } - - free(_glfw.win32.clipboardString); - _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer); - - GlobalUnlock(object); - CloseClipboard(); - - return _glfw.win32.clipboardString; -} - -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) -{ - if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface) - return; - - extensions[0] = "VK_KHR_surface"; - extensions[1] = "VK_KHR_win32_surface"; -} - -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) -{ - PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR - vkGetPhysicalDeviceWin32PresentationSupportKHR = - (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); - if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); - return GLFW_FALSE; - } - - return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily); -} - -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) -{ - VkResult err; - VkWin32SurfaceCreateInfoKHR sci; - PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; - - vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) - vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"); - if (!vkCreateWin32SurfaceKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - memset(&sci, 0, sizeof(sci)); - sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - sci.hinstance = _glfw.win32.instance; - sci.hwnd = window->win32.handle; - - err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface); - if (err) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to create Vulkan surface: %s", - _glfwGetVulkanResultString(err)); - } - - return err; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return window->win32.handle; -} - diff --git a/libs/zglfw/libs/glfw/src/window.c b/libs/zglfw/libs/glfw/src/window.c deleted file mode 100644 index 5d80e4365..000000000 --- a/libs/zglfw/libs/glfw/src/window.c +++ /dev/null @@ -1,1101 +0,0 @@ -//======================================================================== -// GLFW 3.3 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// Copyright (c) 2012 Torsten Walluhn -// -// 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. -// -//======================================================================== -// Please use C89 style variable declarations in this file because VS 2010 -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW event API ////// -////////////////////////////////////////////////////////////////////////// - -// Notifies shared code that a window has lost or received input focus -// -void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) -{ - if (window->callbacks.focus) - window->callbacks.focus((GLFWwindow*) window, focused); - - if (!focused) - { - int key, button; - - for (key = 0; key <= GLFW_KEY_LAST; key++) - { - if (window->keys[key] == GLFW_PRESS) - { - const int scancode = _glfwPlatformGetKeyScancode(key); - _glfwInputKey(window, key, scancode, GLFW_RELEASE, 0); - } - } - - for (button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++) - { - if (window->mouseButtons[button] == GLFW_PRESS) - _glfwInputMouseClick(window, button, GLFW_RELEASE, 0); - } - } -} - -// Notifies shared code that a window has moved -// The position is specified in content area relative screen coordinates -// -void _glfwInputWindowPos(_GLFWwindow* window, int x, int y) -{ - if (window->callbacks.pos) - window->callbacks.pos((GLFWwindow*) window, x, y); -} - -// Notifies shared code that a window has been resized -// The size is specified in screen coordinates -// -void _glfwInputWindowSize(_GLFWwindow* window, int width, int height) -{ - if (window->callbacks.size) - window->callbacks.size((GLFWwindow*) window, width, height); -} - -// Notifies shared code that a window has been iconified or restored -// -void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified) -{ - if (window->callbacks.iconify) - window->callbacks.iconify((GLFWwindow*) window, iconified); -} - -// Notifies shared code that a window has been maximized or restored -// -void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized) -{ - if (window->callbacks.maximize) - window->callbacks.maximize((GLFWwindow*) window, maximized); -} - -// Notifies shared code that a window framebuffer has been resized -// The size is specified in pixels -// -void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height) -{ - if (window->callbacks.fbsize) - window->callbacks.fbsize((GLFWwindow*) window, width, height); -} - -// Notifies shared code that a window content scale has changed -// The scale is specified as the ratio between the current and default DPI -// -void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale) -{ - if (window->callbacks.scale) - window->callbacks.scale((GLFWwindow*) window, xscale, yscale); -} - -// Notifies shared code that the window contents needs updating -// -void _glfwInputWindowDamage(_GLFWwindow* window) -{ - if (window->callbacks.refresh) - window->callbacks.refresh((GLFWwindow*) window); -} - -// Notifies shared code that the user wishes to close a window -// -void _glfwInputWindowCloseRequest(_GLFWwindow* window) -{ - window->shouldClose = GLFW_TRUE; - - if (window->callbacks.close) - window->callbacks.close((GLFWwindow*) window); -} - -// Notifies shared code that a window has changed its desired monitor -// -void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor) -{ - window->monitor = monitor; -} - -////////////////////////////////////////////////////////////////////////// -////// GLFW public API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, - const char* title, - GLFWmonitor* monitor, - GLFWwindow* share) -{ - _GLFWfbconfig fbconfig; - _GLFWctxconfig ctxconfig; - _GLFWwndconfig wndconfig; - _GLFWwindow* window; - - assert(title != NULL); - assert(width >= 0); - assert(height >= 0); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - - if (width <= 0 || height <= 0) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid window size %ix%i", - width, height); - - return NULL; - } - - fbconfig = _glfw.hints.framebuffer; - ctxconfig = _glfw.hints.context; - wndconfig = _glfw.hints.window; - - wndconfig.width = width; - wndconfig.height = height; - wndconfig.title = title; - ctxconfig.share = (_GLFWwindow*) share; - - if (!_glfwIsValidContextConfig(&ctxconfig)) - return NULL; - - window = calloc(1, sizeof(_GLFWwindow)); - window->next = _glfw.windowListHead; - _glfw.windowListHead = window; - - window->videoMode.width = width; - window->videoMode.height = height; - window->videoMode.redBits = fbconfig.redBits; - window->videoMode.greenBits = fbconfig.greenBits; - window->videoMode.blueBits = fbconfig.blueBits; - window->videoMode.refreshRate = _glfw.hints.refreshRate; - - window->monitor = (_GLFWmonitor*) monitor; - window->resizable = wndconfig.resizable; - window->decorated = wndconfig.decorated; - window->autoIconify = wndconfig.autoIconify; - window->floating = wndconfig.floating; - window->focusOnShow = wndconfig.focusOnShow; - window->cursorMode = GLFW_CURSOR_NORMAL; - - window->doublebuffer = fbconfig.doublebuffer; - - window->minwidth = GLFW_DONT_CARE; - window->minheight = GLFW_DONT_CARE; - window->maxwidth = GLFW_DONT_CARE; - window->maxheight = GLFW_DONT_CARE; - window->numer = GLFW_DONT_CARE; - window->denom = GLFW_DONT_CARE; - - // Open the actual window and create its context - if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig)) - { - glfwDestroyWindow((GLFWwindow*) window); - return NULL; - } - - return (GLFWwindow*) window; -} - -void glfwDefaultWindowHints(void) -{ - _GLFW_REQUIRE_INIT(); - - // The default is OpenGL with minimum version 1.0 - memset(&_glfw.hints.context, 0, sizeof(_glfw.hints.context)); - _glfw.hints.context.client = GLFW_OPENGL_API; - _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API; - _glfw.hints.context.major = 1; - _glfw.hints.context.minor = 0; - - // The default is a focused, visible, resizable window with decorations - memset(&_glfw.hints.window, 0, sizeof(_glfw.hints.window)); - _glfw.hints.window.resizable = GLFW_TRUE; - _glfw.hints.window.visible = GLFW_TRUE; - _glfw.hints.window.decorated = GLFW_TRUE; - _glfw.hints.window.focused = GLFW_TRUE; - _glfw.hints.window.autoIconify = GLFW_TRUE; - _glfw.hints.window.centerCursor = GLFW_TRUE; - _glfw.hints.window.focusOnShow = GLFW_TRUE; - - // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, - // double buffered - memset(&_glfw.hints.framebuffer, 0, sizeof(_glfw.hints.framebuffer)); - _glfw.hints.framebuffer.redBits = 8; - _glfw.hints.framebuffer.greenBits = 8; - _glfw.hints.framebuffer.blueBits = 8; - _glfw.hints.framebuffer.alphaBits = 8; - _glfw.hints.framebuffer.depthBits = 24; - _glfw.hints.framebuffer.stencilBits = 8; - _glfw.hints.framebuffer.doublebuffer = GLFW_TRUE; - - // The default is to select the highest available refresh rate - _glfw.hints.refreshRate = GLFW_DONT_CARE; - - // The default is to use full Retina resolution framebuffers - _glfw.hints.window.ns.retina = GLFW_TRUE; -} - -GLFWAPI void glfwWindowHint(int hint, int value) -{ - _GLFW_REQUIRE_INIT(); - - switch (hint) - { - case GLFW_RED_BITS: - _glfw.hints.framebuffer.redBits = value; - return; - case GLFW_GREEN_BITS: - _glfw.hints.framebuffer.greenBits = value; - return; - case GLFW_BLUE_BITS: - _glfw.hints.framebuffer.blueBits = value; - return; - case GLFW_ALPHA_BITS: - _glfw.hints.framebuffer.alphaBits = value; - return; - case GLFW_DEPTH_BITS: - _glfw.hints.framebuffer.depthBits = value; - return; - case GLFW_STENCIL_BITS: - _glfw.hints.framebuffer.stencilBits = value; - return; - case GLFW_ACCUM_RED_BITS: - _glfw.hints.framebuffer.accumRedBits = value; - return; - case GLFW_ACCUM_GREEN_BITS: - _glfw.hints.framebuffer.accumGreenBits = value; - return; - case GLFW_ACCUM_BLUE_BITS: - _glfw.hints.framebuffer.accumBlueBits = value; - return; - case GLFW_ACCUM_ALPHA_BITS: - _glfw.hints.framebuffer.accumAlphaBits = value; - return; - case GLFW_AUX_BUFFERS: - _glfw.hints.framebuffer.auxBuffers = value; - return; - case GLFW_STEREO: - _glfw.hints.framebuffer.stereo = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_DOUBLEBUFFER: - _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_TRANSPARENT_FRAMEBUFFER: - _glfw.hints.framebuffer.transparent = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_SAMPLES: - _glfw.hints.framebuffer.samples = value; - return; - case GLFW_SRGB_CAPABLE: - _glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_RESIZABLE: - _glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_DECORATED: - _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_FOCUSED: - _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_AUTO_ICONIFY: - _glfw.hints.window.autoIconify = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_FLOATING: - _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_MAXIMIZED: - _glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_VISIBLE: - _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_COCOA_RETINA_FRAMEBUFFER: - _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_COCOA_GRAPHICS_SWITCHING: - _glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_SCALE_TO_MONITOR: - _glfw.hints.window.scaleToMonitor = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_CENTER_CURSOR: - _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_FOCUS_ON_SHOW: - _glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_CLIENT_API: - _glfw.hints.context.client = value; - return; - case GLFW_CONTEXT_CREATION_API: - _glfw.hints.context.source = value; - return; - case GLFW_CONTEXT_VERSION_MAJOR: - _glfw.hints.context.major = value; - return; - case GLFW_CONTEXT_VERSION_MINOR: - _glfw.hints.context.minor = value; - return; - case GLFW_CONTEXT_ROBUSTNESS: - _glfw.hints.context.robustness = value; - return; - case GLFW_OPENGL_FORWARD_COMPAT: - _glfw.hints.context.forward = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_OPENGL_DEBUG_CONTEXT: - _glfw.hints.context.debug = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_CONTEXT_NO_ERROR: - _glfw.hints.context.noerror = value ? GLFW_TRUE : GLFW_FALSE; - return; - case GLFW_OPENGL_PROFILE: - _glfw.hints.context.profile = value; - return; - case GLFW_CONTEXT_RELEASE_BEHAVIOR: - _glfw.hints.context.release = value; - return; - case GLFW_REFRESH_RATE: - _glfw.hints.refreshRate = value; - return; - } - - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint 0x%08X", hint); -} - -GLFWAPI void glfwWindowHintString(int hint, const char* value) -{ - assert(value != NULL); - - _GLFW_REQUIRE_INIT(); - - switch (hint) - { - case GLFW_COCOA_FRAME_NAME: - strncpy(_glfw.hints.window.ns.frameName, value, - sizeof(_glfw.hints.window.ns.frameName) - 1); - return; - case GLFW_X11_CLASS_NAME: - strncpy(_glfw.hints.window.x11.className, value, - sizeof(_glfw.hints.window.x11.className) - 1); - return; - case GLFW_X11_INSTANCE_NAME: - strncpy(_glfw.hints.window.x11.instanceName, value, - sizeof(_glfw.hints.window.x11.instanceName) - 1); - return; - } - - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint string 0x%08X", hint); -} - -GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - - _GLFW_REQUIRE_INIT(); - - // Allow closing of NULL (to match the behavior of free) - if (window == NULL) - return; - - // Clear all callbacks to avoid exposing a half torn-down window object - memset(&window->callbacks, 0, sizeof(window->callbacks)); - - // The window's context must not be current on another thread when the - // window is destroyed - if (window == _glfwPlatformGetTls(&_glfw.contextSlot)) - glfwMakeContextCurrent(NULL); - - _glfwPlatformDestroyWindow(window); - - // Unlink window from global linked list - { - _GLFWwindow** prev = &_glfw.windowListHead; - - while (*prev != window) - prev = &((*prev)->next); - - *prev = window->next; - } - - free(window); -} - -GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(0); - return window->shouldClose; -} - -GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - window->shouldClose = value; -} - -GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - assert(title != NULL); - - _GLFW_REQUIRE_INIT(); - _glfwPlatformSetWindowTitle(window, title); -} - -GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle, - int count, const GLFWimage* images) -{ - int i; - _GLFWwindow* window = (_GLFWwindow*) handle; - - assert(window != NULL); - assert(count >= 0); - assert(count == 0 || images != NULL); - - _GLFW_REQUIRE_INIT(); - - if (count < 0) - { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid image count for window icon"); - return; - } - - for (i = 0; i < count; i++) - { - assert(images[i].pixels != NULL); - - if (images[i].width <= 0 || images[i].height <= 0) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid image dimensions for window icon"); - return; - } - } - - _glfwPlatformSetWindowIcon(window, count, images); -} - -GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - if (xpos) - *xpos = 0; - if (ypos) - *ypos = 0; - - _GLFW_REQUIRE_INIT(); - _glfwPlatformGetWindowPos(window, xpos, ypos); -} - -GLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - if (window->monitor) - return; - - _glfwPlatformSetWindowPos(window, xpos, ypos); -} - -GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - if (width) - *width = 0; - if (height) - *height = 0; - - _GLFW_REQUIRE_INIT(); - _glfwPlatformGetWindowSize(window, width, height); -} - -GLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - assert(width >= 0); - assert(height >= 0); - - _GLFW_REQUIRE_INIT(); - - window->videoMode.width = width; - window->videoMode.height = height; - - _glfwPlatformSetWindowSize(window, width, height); -} - -GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* handle, - int minwidth, int minheight, - int maxwidth, int maxheight) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - if (minwidth != GLFW_DONT_CARE && minheight != GLFW_DONT_CARE) - { - if (minwidth < 0 || minheight < 0) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid window minimum size %ix%i", - minwidth, minheight); - return; - } - } - - if (maxwidth != GLFW_DONT_CARE && maxheight != GLFW_DONT_CARE) - { - if (maxwidth < 0 || maxheight < 0 || - maxwidth < minwidth || maxheight < minheight) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid window maximum size %ix%i", - maxwidth, maxheight); - return; - } - } - - window->minwidth = minwidth; - window->minheight = minheight; - window->maxwidth = maxwidth; - window->maxheight = maxheight; - - if (window->monitor || !window->resizable) - return; - - _glfwPlatformSetWindowSizeLimits(window, - minwidth, minheight, - maxwidth, maxheight); -} - -GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - assert(numer != 0); - assert(denom != 0); - - _GLFW_REQUIRE_INIT(); - - if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE) - { - if (numer <= 0 || denom <= 0) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid window aspect ratio %i:%i", - numer, denom); - return; - } - } - - window->numer = numer; - window->denom = denom; - - if (window->monitor || !window->resizable) - return; - - _glfwPlatformSetWindowAspectRatio(window, numer, denom); -} - -GLFWAPI void glfwGetFramebufferSize(GLFWwindow* handle, int* width, int* height) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - if (width) - *width = 0; - if (height) - *height = 0; - - _GLFW_REQUIRE_INIT(); - _glfwPlatformGetFramebufferSize(window, width, height); -} - -GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, - int* left, int* top, - int* right, int* bottom) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - if (left) - *left = 0; - if (top) - *top = 0; - if (right) - *right = 0; - if (bottom) - *bottom = 0; - - _GLFW_REQUIRE_INIT(); - _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); -} - -GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle, - float* xscale, float* yscale) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - if (xscale) - *xscale = 0.f; - if (yscale) - *yscale = 0.f; - - _GLFW_REQUIRE_INIT(); - _glfwPlatformGetWindowContentScale(window, xscale, yscale); -} - -GLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(1.f); - return _glfwPlatformGetWindowOpacity(window); -} - -GLFWAPI void glfwSetWindowOpacity(GLFWwindow* handle, float opacity) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - assert(opacity == opacity); - assert(opacity >= 0.f); - assert(opacity <= 1.f); - - _GLFW_REQUIRE_INIT(); - - if (opacity != opacity || opacity < 0.f || opacity > 1.f) - { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid window opacity %f", opacity); - return; - } - - _glfwPlatformSetWindowOpacity(window, opacity); -} - -GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - _glfwPlatformIconifyWindow(window); -} - -GLFWAPI void glfwRestoreWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - _glfwPlatformRestoreWindow(window); -} - -GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - if (window->monitor) - return; - - _glfwPlatformMaximizeWindow(window); -} - -GLFWAPI void glfwShowWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - if (window->monitor) - return; - - _glfwPlatformShowWindow(window); - - if (window->focusOnShow) - _glfwPlatformFocusWindow(window); -} - -GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - _glfwPlatformRequestWindowAttention(window); -} - -GLFWAPI void glfwHideWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - if (window->monitor) - return; - - _glfwPlatformHideWindow(window); -} - -GLFWAPI void glfwFocusWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - _glfwPlatformFocusWindow(window); -} - -GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(0); - - switch (attrib) - { - case GLFW_FOCUSED: - return _glfwPlatformWindowFocused(window); - case GLFW_ICONIFIED: - return _glfwPlatformWindowIconified(window); - case GLFW_VISIBLE: - return _glfwPlatformWindowVisible(window); - case GLFW_MAXIMIZED: - return _glfwPlatformWindowMaximized(window); - case GLFW_HOVERED: - return _glfwPlatformWindowHovered(window); - case GLFW_FOCUS_ON_SHOW: - return window->focusOnShow; - case GLFW_TRANSPARENT_FRAMEBUFFER: - return _glfwPlatformFramebufferTransparent(window); - case GLFW_RESIZABLE: - return window->resizable; - case GLFW_DECORATED: - return window->decorated; - case GLFW_FLOATING: - return window->floating; - case GLFW_AUTO_ICONIFY: - return window->autoIconify; - case GLFW_CLIENT_API: - return window->context.client; - case GLFW_CONTEXT_CREATION_API: - return window->context.source; - case GLFW_CONTEXT_VERSION_MAJOR: - return window->context.major; - case GLFW_CONTEXT_VERSION_MINOR: - return window->context.minor; - case GLFW_CONTEXT_REVISION: - return window->context.revision; - case GLFW_CONTEXT_ROBUSTNESS: - return window->context.robustness; - case GLFW_OPENGL_FORWARD_COMPAT: - return window->context.forward; - case GLFW_OPENGL_DEBUG_CONTEXT: - return window->context.debug; - case GLFW_OPENGL_PROFILE: - return window->context.profile; - case GLFW_CONTEXT_RELEASE_BEHAVIOR: - return window->context.release; - case GLFW_CONTEXT_NO_ERROR: - return window->context.noerror; - } - - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); - return 0; -} - -GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - - value = value ? GLFW_TRUE : GLFW_FALSE; - - if (attrib == GLFW_AUTO_ICONIFY) - window->autoIconify = value; - else if (attrib == GLFW_RESIZABLE) - { - if (window->resizable == value) - return; - - window->resizable = value; - if (!window->monitor) - _glfwPlatformSetWindowResizable(window, value); - } - else if (attrib == GLFW_DECORATED) - { - if (window->decorated == value) - return; - - window->decorated = value; - if (!window->monitor) - _glfwPlatformSetWindowDecorated(window, value); - } - else if (attrib == GLFW_FLOATING) - { - if (window->floating == value) - return; - - window->floating = value; - if (!window->monitor) - _glfwPlatformSetWindowFloating(window, value); - } - else if (attrib == GLFW_FOCUS_ON_SHOW) - window->focusOnShow = value; - else - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); -} - -GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return (GLFWmonitor*) window->monitor; -} - -GLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh, - GLFWmonitor* mh, - int xpos, int ypos, - int width, int height, - int refreshRate) -{ - _GLFWwindow* window = (_GLFWwindow*) wh; - _GLFWmonitor* monitor = (_GLFWmonitor*) mh; - assert(window != NULL); - assert(width >= 0); - assert(height >= 0); - - _GLFW_REQUIRE_INIT(); - - if (width <= 0 || height <= 0) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid window size %ix%i", - width, height); - return; - } - - if (refreshRate < 0 && refreshRate != GLFW_DONT_CARE) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid refresh rate %i", - refreshRate); - return; - } - - window->videoMode.width = width; - window->videoMode.height = height; - window->videoMode.refreshRate = refreshRate; - - _glfwPlatformSetWindowMonitor(window, monitor, - xpos, ypos, width, height, - refreshRate); -} - -GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT(); - window->userPointer = pointer; -} - -GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return window->userPointer; -} - -GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* handle, - GLFWwindowposfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.pos, cbfun); - return cbfun; -} - -GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* handle, - GLFWwindowsizefun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.size, cbfun); - return cbfun; -} - -GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* handle, - GLFWwindowclosefun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.close, cbfun); - return cbfun; -} - -GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* handle, - GLFWwindowrefreshfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.refresh, cbfun); - return cbfun; -} - -GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* handle, - GLFWwindowfocusfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.focus, cbfun); - return cbfun; -} - -GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* handle, - GLFWwindowiconifyfun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.iconify, cbfun); - return cbfun; -} - -GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* handle, - GLFWwindowmaximizefun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.maximize, cbfun); - return cbfun; -} - -GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle, - GLFWframebuffersizefun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.fbsize, cbfun); - return cbfun; -} - -GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* handle, - GLFWwindowcontentscalefun cbfun) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.scale, cbfun); - return cbfun; -} - -GLFWAPI void glfwPollEvents(void) -{ - _GLFW_REQUIRE_INIT(); - _glfwPlatformPollEvents(); -} - -GLFWAPI void glfwWaitEvents(void) -{ - _GLFW_REQUIRE_INIT(); - _glfwPlatformWaitEvents(); -} - -GLFWAPI void glfwWaitEventsTimeout(double timeout) -{ - _GLFW_REQUIRE_INIT(); - assert(timeout == timeout); - assert(timeout >= 0.0); - assert(timeout <= DBL_MAX); - - if (timeout != timeout || timeout < 0.0 || timeout > DBL_MAX) - { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", timeout); - return; - } - - _glfwPlatformWaitEventsTimeout(timeout); -} - -GLFWAPI void glfwPostEmptyEvent(void) -{ - _GLFW_REQUIRE_INIT(); - _glfwPlatformPostEmptyEvent(); -} - diff --git a/libs/zglfw/libs/glfw/src/wl_init.c b/libs/zglfw/libs/glfw/src/wl_init.c deleted file mode 100644 index e041d2614..000000000 --- a/libs/zglfw/libs/glfw/src/wl_init.c +++ /dev/null @@ -1,595 +0,0 @@ -//======================================================================== -// GLFW 3.3 Wayland - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014 Jonas Ã…dahl -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#define _POSIX_C_SOURCE 200809L - -#include "internal.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void wmBaseHandlePing(void* userData, - struct xdg_wm_base* wmBase, - uint32_t serial) -{ - xdg_wm_base_pong(wmBase, serial); -} - -static const struct xdg_wm_base_listener wmBaseListener = -{ - wmBaseHandlePing -}; - -static void registryHandleGlobal(void* userData, - struct wl_registry* registry, - uint32_t name, - const char* interface, - uint32_t version) -{ - if (strcmp(interface, "wl_compositor") == 0) - { - _glfw.wl.compositorVersion = _glfw_min(3, version); - _glfw.wl.compositor = - wl_registry_bind(registry, name, &wl_compositor_interface, - _glfw.wl.compositorVersion); - } - else if (strcmp(interface, "wl_subcompositor") == 0) - { - _glfw.wl.subcompositor = - wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); - } - else if (strcmp(interface, "wl_shm") == 0) - { - _glfw.wl.shm = - wl_registry_bind(registry, name, &wl_shm_interface, 1); - } - else if (strcmp(interface, "wl_output") == 0) - { - _glfwAddOutputWayland(name, version); - } - else if (strcmp(interface, "wl_seat") == 0) - { - if (!_glfw.wl.seat) - { - _glfw.wl.seatVersion = _glfw_min(4, version); - _glfw.wl.seat = - wl_registry_bind(registry, name, &wl_seat_interface, - _glfw.wl.seatVersion); - _glfwAddSeatListenerWayland(_glfw.wl.seat); - } - } - else if (strcmp(interface, "wl_data_device_manager") == 0) - { - if (!_glfw.wl.dataDeviceManager) - { - _glfw.wl.dataDeviceManager = - wl_registry_bind(registry, name, - &wl_data_device_manager_interface, 1); - } - } - else if (strcmp(interface, "xdg_wm_base") == 0) - { - _glfw.wl.wmBase = - wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); - xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL); - } - else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) - { - _glfw.wl.decorationManager = - wl_registry_bind(registry, name, - &zxdg_decoration_manager_v1_interface, - 1); - } - else if (strcmp(interface, "wp_viewporter") == 0) - { - _glfw.wl.viewporter = - wl_registry_bind(registry, name, &wp_viewporter_interface, 1); - } - else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) - { - _glfw.wl.relativePointerManager = - wl_registry_bind(registry, name, - &zwp_relative_pointer_manager_v1_interface, - 1); - } - else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) - { - _glfw.wl.pointerConstraints = - wl_registry_bind(registry, name, - &zwp_pointer_constraints_v1_interface, - 1); - } - else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) - { - _glfw.wl.idleInhibitManager = - wl_registry_bind(registry, name, - &zwp_idle_inhibit_manager_v1_interface, - 1); - } -} - -static void registryHandleGlobalRemove(void* userData, - struct wl_registry* registry, - uint32_t name) -{ - int i; - - for (i = 0; i < _glfw.monitorCount; ++i) - { - _GLFWmonitor* monitor = _glfw.monitors[i]; - if (monitor->wl.name == name) - { - _glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0); - return; - } - } -} - - -static const struct wl_registry_listener registryListener = -{ - registryHandleGlobal, - registryHandleGlobalRemove -}; - -// Create key code translation tables -// -static void createKeyTables(void) -{ - int scancode; - - memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes)); - memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes)); - - _glfw.wl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; - _glfw.wl.keycodes[KEY_1] = GLFW_KEY_1; - _glfw.wl.keycodes[KEY_2] = GLFW_KEY_2; - _glfw.wl.keycodes[KEY_3] = GLFW_KEY_3; - _glfw.wl.keycodes[KEY_4] = GLFW_KEY_4; - _glfw.wl.keycodes[KEY_5] = GLFW_KEY_5; - _glfw.wl.keycodes[KEY_6] = GLFW_KEY_6; - _glfw.wl.keycodes[KEY_7] = GLFW_KEY_7; - _glfw.wl.keycodes[KEY_8] = GLFW_KEY_8; - _glfw.wl.keycodes[KEY_9] = GLFW_KEY_9; - _glfw.wl.keycodes[KEY_0] = GLFW_KEY_0; - _glfw.wl.keycodes[KEY_SPACE] = GLFW_KEY_SPACE; - _glfw.wl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; - _glfw.wl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; - _glfw.wl.keycodes[KEY_Q] = GLFW_KEY_Q; - _glfw.wl.keycodes[KEY_W] = GLFW_KEY_W; - _glfw.wl.keycodes[KEY_E] = GLFW_KEY_E; - _glfw.wl.keycodes[KEY_R] = GLFW_KEY_R; - _glfw.wl.keycodes[KEY_T] = GLFW_KEY_T; - _glfw.wl.keycodes[KEY_Y] = GLFW_KEY_Y; - _glfw.wl.keycodes[KEY_U] = GLFW_KEY_U; - _glfw.wl.keycodes[KEY_I] = GLFW_KEY_I; - _glfw.wl.keycodes[KEY_O] = GLFW_KEY_O; - _glfw.wl.keycodes[KEY_P] = GLFW_KEY_P; - _glfw.wl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; - _glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; - _glfw.wl.keycodes[KEY_A] = GLFW_KEY_A; - _glfw.wl.keycodes[KEY_S] = GLFW_KEY_S; - _glfw.wl.keycodes[KEY_D] = GLFW_KEY_D; - _glfw.wl.keycodes[KEY_F] = GLFW_KEY_F; - _glfw.wl.keycodes[KEY_G] = GLFW_KEY_G; - _glfw.wl.keycodes[KEY_H] = GLFW_KEY_H; - _glfw.wl.keycodes[KEY_J] = GLFW_KEY_J; - _glfw.wl.keycodes[KEY_K] = GLFW_KEY_K; - _glfw.wl.keycodes[KEY_L] = GLFW_KEY_L; - _glfw.wl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; - _glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; - _glfw.wl.keycodes[KEY_Z] = GLFW_KEY_Z; - _glfw.wl.keycodes[KEY_X] = GLFW_KEY_X; - _glfw.wl.keycodes[KEY_C] = GLFW_KEY_C; - _glfw.wl.keycodes[KEY_V] = GLFW_KEY_V; - _glfw.wl.keycodes[KEY_B] = GLFW_KEY_B; - _glfw.wl.keycodes[KEY_N] = GLFW_KEY_N; - _glfw.wl.keycodes[KEY_M] = GLFW_KEY_M; - _glfw.wl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; - _glfw.wl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; - _glfw.wl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; - _glfw.wl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; - _glfw.wl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; - _glfw.wl.keycodes[KEY_TAB] = GLFW_KEY_TAB; - _glfw.wl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; - _glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; - _glfw.wl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; - _glfw.wl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; - _glfw.wl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; - _glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; - _glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; - _glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; - _glfw.wl.keycodes[KEY_COMPOSE] = GLFW_KEY_MENU; - _glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; - _glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; - _glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; - _glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; - _glfw.wl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; - _glfw.wl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; - _glfw.wl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; - _glfw.wl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; - _glfw.wl.keycodes[KEY_HOME] = GLFW_KEY_HOME; - _glfw.wl.keycodes[KEY_END] = GLFW_KEY_END; - _glfw.wl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; - _glfw.wl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; - _glfw.wl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; - _glfw.wl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; - _glfw.wl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; - _glfw.wl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; - _glfw.wl.keycodes[KEY_UP] = GLFW_KEY_UP; - _glfw.wl.keycodes[KEY_F1] = GLFW_KEY_F1; - _glfw.wl.keycodes[KEY_F2] = GLFW_KEY_F2; - _glfw.wl.keycodes[KEY_F3] = GLFW_KEY_F3; - _glfw.wl.keycodes[KEY_F4] = GLFW_KEY_F4; - _glfw.wl.keycodes[KEY_F5] = GLFW_KEY_F5; - _glfw.wl.keycodes[KEY_F6] = GLFW_KEY_F6; - _glfw.wl.keycodes[KEY_F7] = GLFW_KEY_F7; - _glfw.wl.keycodes[KEY_F8] = GLFW_KEY_F8; - _glfw.wl.keycodes[KEY_F9] = GLFW_KEY_F9; - _glfw.wl.keycodes[KEY_F10] = GLFW_KEY_F10; - _glfw.wl.keycodes[KEY_F11] = GLFW_KEY_F11; - _glfw.wl.keycodes[KEY_F12] = GLFW_KEY_F12; - _glfw.wl.keycodes[KEY_F13] = GLFW_KEY_F13; - _glfw.wl.keycodes[KEY_F14] = GLFW_KEY_F14; - _glfw.wl.keycodes[KEY_F15] = GLFW_KEY_F15; - _glfw.wl.keycodes[KEY_F16] = GLFW_KEY_F16; - _glfw.wl.keycodes[KEY_F17] = GLFW_KEY_F17; - _glfw.wl.keycodes[KEY_F18] = GLFW_KEY_F18; - _glfw.wl.keycodes[KEY_F19] = GLFW_KEY_F19; - _glfw.wl.keycodes[KEY_F20] = GLFW_KEY_F20; - _glfw.wl.keycodes[KEY_F21] = GLFW_KEY_F21; - _glfw.wl.keycodes[KEY_F22] = GLFW_KEY_F22; - _glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23; - _glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24; - _glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; - _glfw.wl.keycodes[KEY_KPASTERISK] = GLFW_KEY_KP_MULTIPLY; - _glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; - _glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; - _glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0; - _glfw.wl.keycodes[KEY_KP1] = GLFW_KEY_KP_1; - _glfw.wl.keycodes[KEY_KP2] = GLFW_KEY_KP_2; - _glfw.wl.keycodes[KEY_KP3] = GLFW_KEY_KP_3; - _glfw.wl.keycodes[KEY_KP4] = GLFW_KEY_KP_4; - _glfw.wl.keycodes[KEY_KP5] = GLFW_KEY_KP_5; - _glfw.wl.keycodes[KEY_KP6] = GLFW_KEY_KP_6; - _glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7; - _glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8; - _glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9; - _glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_DECIMAL; - _glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; - _glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; - _glfw.wl.keycodes[KEY_102ND] = GLFW_KEY_WORLD_2; - - for (scancode = 0; scancode < 256; scancode++) - { - if (_glfw.wl.keycodes[scancode] > 0) - _glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformInit(void) -{ - const char* cursorTheme; - const char* cursorSizeStr; - char* cursorSizeEnd; - long cursorSizeLong; - int cursorSize; - - // These must be set before any failure checks - _glfw.wl.timerfd = -1; - _glfw.wl.cursorTimerfd = -1; - - _glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0"); - if (!_glfw.wl.cursor.handle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to load libwayland-cursor"); - return GLFW_FALSE; - } - - _glfw.wl.cursor.theme_load = (PFN_wl_cursor_theme_load) - _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_load"); - _glfw.wl.cursor.theme_destroy = (PFN_wl_cursor_theme_destroy) - _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_destroy"); - _glfw.wl.cursor.theme_get_cursor = (PFN_wl_cursor_theme_get_cursor) - _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor"); - _glfw.wl.cursor.image_get_buffer = (PFN_wl_cursor_image_get_buffer) - _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_image_get_buffer"); - - _glfw.wl.egl.handle = _glfw_dlopen("libwayland-egl.so.1"); - if (!_glfw.wl.egl.handle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to load libwayland-egl"); - return GLFW_FALSE; - } - - _glfw.wl.egl.window_create = (PFN_wl_egl_window_create) - _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_create"); - _glfw.wl.egl.window_destroy = (PFN_wl_egl_window_destroy) - _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_destroy"); - _glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize) - _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize"); - - _glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0"); - if (!_glfw.wl.xkb.handle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to load libxkbcommon"); - return GLFW_FALSE; - } - - _glfw.wl.xkb.context_new = (PFN_xkb_context_new) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new"); - _glfw.wl.xkb.context_unref = (PFN_xkb_context_unref) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref"); - _glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string"); - _glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref"); - _glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index"); - _glfw.wl.xkb.keymap_key_repeats = (PFN_xkb_keymap_key_repeats) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats"); - _glfw.wl.xkb.keymap_key_get_syms_by_level = (PFN_xkb_keymap_key_get_syms_by_level) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_get_syms_by_level"); - _glfw.wl.xkb.state_new = (PFN_xkb_state_new) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new"); - _glfw.wl.xkb.state_unref = (PFN_xkb_state_unref) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref"); - _glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms"); - _glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask"); - _glfw.wl.xkb.state_key_get_layout = (PFN_xkb_state_key_get_layout) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_layout"); - _glfw.wl.xkb.state_mod_index_is_active = (PFN_xkb_state_mod_index_is_active) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_mod_index_is_active"); - - _glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale"); - _glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref"); - _glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new"); - _glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref"); - _glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed"); - _glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status"); - _glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); - - _glfw.wl.display = wl_display_connect(NULL); - if (!_glfw.wl.display) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to connect to display"); - return GLFW_FALSE; - } - - _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); - wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); - - createKeyTables(); - - _glfw.wl.xkb.context = xkb_context_new(0); - if (!_glfw.wl.xkb.context) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to initialize xkb context"); - return GLFW_FALSE; - } - - // Sync so we got all registry objects - wl_display_roundtrip(_glfw.wl.display); - - // Sync so we got all initial output events - wl_display_roundtrip(_glfw.wl.display); - -#ifdef __linux__ - if (!_glfwInitJoysticksLinux()) - return GLFW_FALSE; -#endif - - _glfwInitTimerPOSIX(); - -#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION - if (_glfw.wl.seatVersion >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) - _glfw.wl.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); -#endif - - if (!_glfw.wl.wmBase) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to find xdg-shell in your compositor"); - return GLFW_FALSE; - } - - if (_glfw.wl.pointer && _glfw.wl.shm) - { - cursorTheme = getenv("XCURSOR_THEME"); - cursorSizeStr = getenv("XCURSOR_SIZE"); - cursorSize = 32; - if (cursorSizeStr) - { - errno = 0; - cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10); - if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX) - cursorSize = (int)cursorSizeLong; - } - _glfw.wl.cursorTheme = - wl_cursor_theme_load(cursorTheme, cursorSize, _glfw.wl.shm); - if (!_glfw.wl.cursorTheme) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to load default cursor theme"); - return GLFW_FALSE; - } - // If this happens to be NULL, we just fallback to the scale=1 version. - _glfw.wl.cursorThemeHiDPI = - wl_cursor_theme_load(cursorTheme, 2 * cursorSize, _glfw.wl.shm); - _glfw.wl.cursorSurface = - wl_compositor_create_surface(_glfw.wl.compositor); - _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); - } - - if (_glfw.wl.seat && _glfw.wl.dataDeviceManager) - { - _glfw.wl.dataDevice = - wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, - _glfw.wl.seat); - _glfwAddDataDeviceListenerWayland(_glfw.wl.dataDevice); - } - - return GLFW_TRUE; -} - -void _glfwPlatformTerminate(void) -{ -#ifdef __linux__ - _glfwTerminateJoysticksLinux(); -#endif - _glfwTerminateEGL(); - _glfwTerminateOSMesa(); - - if (_glfw.wl.egl.handle) - { - _glfw_dlclose(_glfw.wl.egl.handle); - _glfw.wl.egl.handle = NULL; - } - - if (_glfw.wl.xkb.composeState) - xkb_compose_state_unref(_glfw.wl.xkb.composeState); - if (_glfw.wl.xkb.keymap) - xkb_keymap_unref(_glfw.wl.xkb.keymap); - if (_glfw.wl.xkb.state) - xkb_state_unref(_glfw.wl.xkb.state); - if (_glfw.wl.xkb.context) - xkb_context_unref(_glfw.wl.xkb.context); - if (_glfw.wl.xkb.handle) - { - _glfw_dlclose(_glfw.wl.xkb.handle); - _glfw.wl.xkb.handle = NULL; - } - - if (_glfw.wl.cursorTheme) - wl_cursor_theme_destroy(_glfw.wl.cursorTheme); - if (_glfw.wl.cursorThemeHiDPI) - wl_cursor_theme_destroy(_glfw.wl.cursorThemeHiDPI); - if (_glfw.wl.cursor.handle) - { - _glfw_dlclose(_glfw.wl.cursor.handle); - _glfw.wl.cursor.handle = NULL; - } - - for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) - wl_data_offer_destroy(_glfw.wl.offers[i].offer); - - free(_glfw.wl.offers); - - if (_glfw.wl.cursorSurface) - wl_surface_destroy(_glfw.wl.cursorSurface); - if (_glfw.wl.subcompositor) - wl_subcompositor_destroy(_glfw.wl.subcompositor); - if (_glfw.wl.compositor) - wl_compositor_destroy(_glfw.wl.compositor); - if (_glfw.wl.shm) - wl_shm_destroy(_glfw.wl.shm); - if (_glfw.wl.viewporter) - wp_viewporter_destroy(_glfw.wl.viewporter); - if (_glfw.wl.decorationManager) - zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager); - if (_glfw.wl.wmBase) - xdg_wm_base_destroy(_glfw.wl.wmBase); - if (_glfw.wl.selectionOffer) - wl_data_offer_destroy(_glfw.wl.selectionOffer); - if (_glfw.wl.dragOffer) - wl_data_offer_destroy(_glfw.wl.dragOffer); - if (_glfw.wl.selectionSource) - wl_data_source_destroy(_glfw.wl.selectionSource); - if (_glfw.wl.dataDevice) - wl_data_device_destroy(_glfw.wl.dataDevice); - if (_glfw.wl.dataDeviceManager) - wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager); - if (_glfw.wl.pointer) - wl_pointer_destroy(_glfw.wl.pointer); - if (_glfw.wl.keyboard) - wl_keyboard_destroy(_glfw.wl.keyboard); - if (_glfw.wl.seat) - wl_seat_destroy(_glfw.wl.seat); - if (_glfw.wl.relativePointerManager) - zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager); - if (_glfw.wl.pointerConstraints) - zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints); - if (_glfw.wl.idleInhibitManager) - zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager); - if (_glfw.wl.registry) - wl_registry_destroy(_glfw.wl.registry); - if (_glfw.wl.display) - { - wl_display_flush(_glfw.wl.display); - wl_display_disconnect(_glfw.wl.display); - } - - if (_glfw.wl.timerfd >= 0) - close(_glfw.wl.timerfd); - if (_glfw.wl.cursorTimerfd >= 0) - close(_glfw.wl.cursorTimerfd); - - free(_glfw.wl.clipboardString); -} - -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " Wayland EGL OSMesa" -#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) - " clock_gettime" -#else - " gettimeofday" -#endif - " evdev" -#if defined(_GLFW_BUILD_DLL) - " shared" -#endif - ; -} diff --git a/libs/zglfw/libs/glfw/src/wl_monitor.c b/libs/zglfw/libs/glfw/src/wl_monitor.c deleted file mode 100644 index 99de89333..000000000 --- a/libs/zglfw/libs/glfw/src/wl_monitor.c +++ /dev/null @@ -1,271 +0,0 @@ -//======================================================================== -// GLFW 3.3 Wayland - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014 Jonas Ã…dahl -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include -#include - - -static void outputHandleGeometry(void* userData, - struct wl_output* output, - int32_t x, - int32_t y, - int32_t physicalWidth, - int32_t physicalHeight, - int32_t subpixel, - const char* make, - const char* model, - int32_t transform) -{ - struct _GLFWmonitor* monitor = userData; - - monitor->wl.x = x; - monitor->wl.y = y; - monitor->widthMM = physicalWidth; - monitor->heightMM = physicalHeight; - - if (strlen(monitor->name) == 0) - snprintf(monitor->name, sizeof(monitor->name), "%s %s", make, model); -} - -static void outputHandleMode(void* userData, - struct wl_output* output, - uint32_t flags, - int32_t width, - int32_t height, - int32_t refresh) -{ - struct _GLFWmonitor* monitor = userData; - GLFWvidmode mode; - - mode.width = width; - mode.height = height; - mode.redBits = 8; - mode.greenBits = 8; - mode.blueBits = 8; - mode.refreshRate = (int) round(refresh / 1000.0); - - monitor->modeCount++; - monitor->modes = - realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode)); - monitor->modes[monitor->modeCount - 1] = mode; - - if (flags & WL_OUTPUT_MODE_CURRENT) - monitor->wl.currentMode = monitor->modeCount - 1; -} - -static void outputHandleDone(void* userData, struct wl_output* output) -{ - struct _GLFWmonitor* monitor = userData; - - if (monitor->widthMM <= 0 || monitor->heightMM <= 0) - { - // If Wayland does not provide a physical size, assume the default 96 DPI - const GLFWvidmode* mode = &monitor->modes[monitor->wl.currentMode]; - monitor->widthMM = (int) (mode->width * 25.4f / 96.f); - monitor->heightMM = (int) (mode->height * 25.4f / 96.f); - } - - for (int i = 0; i < _glfw.monitorCount; i++) - { - if (_glfw.monitors[i] == monitor) - return; - } - - _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); -} - -static void outputHandleScale(void* userData, - struct wl_output* output, - int32_t factor) -{ - struct _GLFWmonitor* monitor = userData; - - monitor->wl.scale = factor; - - for (_GLFWwindow* window = _glfw.windowListHead; window; window = window->next) - { - for (int i = 0; i < window->wl.monitorsCount; i++) - { - if (window->wl.monitors[i] == monitor) - { - _glfwUpdateContentScaleWayland(window); - break; - } - } - } -} - -#ifdef WL_OUTPUT_NAME_SINCE_VERSION - -void outputHandleName(void* userData, struct wl_output* wl_output, const char* name) -{ - struct _GLFWmonitor* monitor = userData; - - strncpy(monitor->name, name, sizeof(monitor->name) - 1); -} - -void outputHandleDescription(void* userData, - struct wl_output* wl_output, - const char* description) -{ -} - -#endif // WL_OUTPUT_NAME_SINCE_VERSION - -static const struct wl_output_listener outputListener = -{ - outputHandleGeometry, - outputHandleMode, - outputHandleDone, - outputHandleScale, -#ifdef WL_OUTPUT_NAME_SINCE_VERSION - outputHandleName, - outputHandleDescription, -#endif -}; - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwAddOutputWayland(uint32_t name, uint32_t version) -{ - if (version < 2) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Unsupported output interface version"); - return; - } - -#ifdef WL_OUTPUT_NAME_SINCE_VERSION - version = _glfw_min(version, WL_OUTPUT_NAME_SINCE_VERSION); -#else - version = 2; -#endif - - struct wl_output* output = wl_registry_bind(_glfw.wl.registry, - name, - &wl_output_interface, - version); - if (!output) - return; - - // The actual name of this output will be set in the geometry handler - _GLFWmonitor* monitor = _glfwAllocMonitor("", 0, 0); - monitor->wl.scale = 1; - monitor->wl.output = output; - monitor->wl.name = name; - - wl_output_add_listener(output, &outputListener, monitor); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) -{ - if (monitor->wl.output) - wl_output_destroy(monitor->wl.output); -} - -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) -{ - if (xpos) - *xpos = monitor->wl.x; - if (ypos) - *ypos = monitor->wl.y; -} - -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) -{ - if (xscale) - *xscale = (float) monitor->wl.scale; - if (yscale) - *yscale = (float) monitor->wl.scale; -} - -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, - int* xpos, int* ypos, - int* width, int* height) -{ - if (xpos) - *xpos = monitor->wl.x; - if (ypos) - *ypos = monitor->wl.y; - if (width) - *width = monitor->modes[monitor->wl.currentMode].width; - if (height) - *height = monitor->modes[monitor->wl.currentMode].height; -} - -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) -{ - *found = monitor->modeCount; - return monitor->modes; -} - -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) -{ - *mode = monitor->modes[monitor->wl.currentMode]; -} - -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Gamma ramp access is not available"); - return GLFW_FALSE; -} - -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, - const GLFWgammaramp* ramp) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Gamma ramp access is not available"); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return monitor->wl.output; -} - diff --git a/libs/zglfw/libs/glfw/src/wl_platform.h b/libs/zglfw/libs/glfw/src/wl_platform.h deleted file mode 100644 index 2146e2ad0..000000000 --- a/libs/zglfw/libs/glfw/src/wl_platform.h +++ /dev/null @@ -1,373 +0,0 @@ -//======================================================================== -// GLFW 3.3 Wayland - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014 Jonas Ã…dahl -// -// 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. -// -//======================================================================== - -#include -#include -#include -#include - -typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; - -typedef struct VkWaylandSurfaceCreateInfoKHR -{ - VkStructureType sType; - const void* pNext; - VkWaylandSurfaceCreateFlagsKHR flags; - struct wl_display* display; - struct wl_surface* surface; -} VkWaylandSurfaceCreateInfoKHR; - -typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); -typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*); - -#include "posix_thread.h" -#include "posix_time.h" -#ifdef __linux__ -#include "linux_joystick.h" -#else -#include "null_joystick.h" -#endif -#include "xkb_unicode.h" -#include "egl_context.h" -#include "osmesa_context.h" - -#include "wayland-xdg-shell-client-protocol.h" -#include "wayland-xdg-decoration-client-protocol.h" -#include "wayland-viewporter-client-protocol.h" -#include "wayland-relative-pointer-unstable-v1-client-protocol.h" -#include "wayland-pointer-constraints-unstable-v1-client-protocol.h" -#include "wayland-idle-inhibit-unstable-v1-client-protocol.h" - -#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) -#define _glfw_dlclose(handle) dlclose(handle) -#define _glfw_dlsym(handle, name) dlsym(handle, name) - -#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->wl.egl.window) -#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.wl.display) - -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWayland wl -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWayland wl -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWayland wl - -#define _GLFW_PLATFORM_CONTEXT_STATE struct { int dummyContext; } -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; } - -struct wl_cursor_image { - uint32_t width; - uint32_t height; - uint32_t hotspot_x; - uint32_t hotspot_y; - uint32_t delay; -}; -struct wl_cursor { - unsigned int image_count; - struct wl_cursor_image** images; - char* name; -}; -typedef struct wl_cursor_theme* (* PFN_wl_cursor_theme_load)(const char*, int, struct wl_shm*); -typedef void (* PFN_wl_cursor_theme_destroy)(struct wl_cursor_theme*); -typedef struct wl_cursor* (* PFN_wl_cursor_theme_get_cursor)(struct wl_cursor_theme*, const char*); -typedef struct wl_buffer* (* PFN_wl_cursor_image_get_buffer)(struct wl_cursor_image*); -#define wl_cursor_theme_load _glfw.wl.cursor.theme_load -#define wl_cursor_theme_destroy _glfw.wl.cursor.theme_destroy -#define wl_cursor_theme_get_cursor _glfw.wl.cursor.theme_get_cursor -#define wl_cursor_image_get_buffer _glfw.wl.cursor.image_get_buffer - -typedef struct wl_egl_window* (* PFN_wl_egl_window_create)(struct wl_surface*, int, int); -typedef void (* PFN_wl_egl_window_destroy)(struct wl_egl_window*); -typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int, int); -#define wl_egl_window_create _glfw.wl.egl.window_create -#define wl_egl_window_destroy _glfw.wl.egl.window_destroy -#define wl_egl_window_resize _glfw.wl.egl.window_resize - -typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags); -typedef void (* PFN_xkb_context_unref)(struct xkb_context*); -typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags); -typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*); -typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*); -typedef int (* PFN_xkb_keymap_key_repeats)(struct xkb_keymap*, xkb_keycode_t); -typedef int (* PFN_xkb_keymap_key_get_syms_by_level)(struct xkb_keymap*,xkb_keycode_t,xkb_layout_index_t,xkb_level_index_t,const xkb_keysym_t**); -typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*); -typedef void (* PFN_xkb_state_unref)(struct xkb_state*); -typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**); -typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t); -typedef xkb_layout_index_t (* PFN_xkb_state_key_get_layout)(struct xkb_state*,xkb_keycode_t); -typedef int (* PFN_xkb_state_mod_index_is_active)(struct xkb_state*,xkb_mod_index_t,enum xkb_state_component); -#define xkb_context_new _glfw.wl.xkb.context_new -#define xkb_context_unref _glfw.wl.xkb.context_unref -#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string -#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref -#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index -#define xkb_keymap_key_repeats _glfw.wl.xkb.keymap_key_repeats -#define xkb_keymap_key_get_syms_by_level _glfw.wl.xkb.keymap_key_get_syms_by_level -#define xkb_state_new _glfw.wl.xkb.state_new -#define xkb_state_unref _glfw.wl.xkb.state_unref -#define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms -#define xkb_state_update_mask _glfw.wl.xkb.state_update_mask -#define xkb_state_key_get_layout _glfw.wl.xkb.state_key_get_layout -#define xkb_state_mod_index_is_active _glfw.wl.xkb.state_mod_index_is_active - -typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags); -typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*); -typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags); -typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*); -typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t); -typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*); -typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*); -#define xkb_compose_table_new_from_locale _glfw.wl.xkb.compose_table_new_from_locale -#define xkb_compose_table_unref _glfw.wl.xkb.compose_table_unref -#define xkb_compose_state_new _glfw.wl.xkb.compose_state_new -#define xkb_compose_state_unref _glfw.wl.xkb.compose_state_unref -#define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed -#define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status -#define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym - -typedef enum _GLFWdecorationSideWayland -{ - mainWindow, - topDecoration, - leftDecoration, - rightDecoration, - bottomDecoration, -} _GLFWdecorationSideWayland; - -typedef struct _GLFWdecorationWayland -{ - struct wl_surface* surface; - struct wl_subsurface* subsurface; - struct wp_viewport* viewport; -} _GLFWdecorationWayland; - -typedef struct _GLFWofferWayland -{ - struct wl_data_offer* offer; - GLFWbool text_plain_utf8; - GLFWbool text_uri_list; -} _GLFWofferWayland; - -// Wayland-specific per-window data -// -typedef struct _GLFWwindowWayland -{ - int width, height; - GLFWbool visible; - GLFWbool maximized; - GLFWbool activated; - GLFWbool fullscreen; - GLFWbool hovered; - GLFWbool transparent; - struct wl_surface* surface; - struct wl_callback* callback; - - struct { - struct wl_egl_window* window; - } egl; - - struct { - int width, height; - GLFWbool maximized; - GLFWbool iconified; - GLFWbool activated; - GLFWbool fullscreen; - } pending; - - struct { - struct xdg_surface* surface; - struct xdg_toplevel* toplevel; - struct zxdg_toplevel_decoration_v1* decoration; - uint32_t decorationMode; - } xdg; - - _GLFWcursor* currentCursor; - double cursorPosX, cursorPosY; - - char* title; - - // We need to track the monitors the window spans on to calculate the - // optimal scaling factor. - int scale; - _GLFWmonitor** monitors; - int monitorsCount; - int monitorsSize; - - struct { - struct zwp_relative_pointer_v1* relativePointer; - struct zwp_locked_pointer_v1* lockedPointer; - } pointerLock; - - struct zwp_idle_inhibitor_v1* idleInhibitor; - - struct { - struct wl_buffer* buffer; - _GLFWdecorationWayland top, left, right, bottom; - _GLFWdecorationSideWayland focus; - } decorations; -} _GLFWwindowWayland; - -// Wayland-specific global data -// -typedef struct _GLFWlibraryWayland -{ - struct wl_display* display; - struct wl_registry* registry; - struct wl_compositor* compositor; - struct wl_subcompositor* subcompositor; - struct wl_shm* shm; - struct wl_seat* seat; - struct wl_pointer* pointer; - struct wl_keyboard* keyboard; - struct wl_data_device_manager* dataDeviceManager; - struct wl_data_device* dataDevice; - struct xdg_wm_base* wmBase; - struct zxdg_decoration_manager_v1* decorationManager; - struct wp_viewporter* viewporter; - struct zwp_relative_pointer_manager_v1* relativePointerManager; - struct zwp_pointer_constraints_v1* pointerConstraints; - struct zwp_idle_inhibit_manager_v1* idleInhibitManager; - - _GLFWofferWayland* offers; - unsigned int offerCount; - - struct wl_data_offer* selectionOffer; - struct wl_data_source* selectionSource; - - struct wl_data_offer* dragOffer; - _GLFWwindow* dragFocus; - uint32_t dragSerial; - - int compositorVersion; - int seatVersion; - - struct wl_cursor_theme* cursorTheme; - struct wl_cursor_theme* cursorThemeHiDPI; - struct wl_surface* cursorSurface; - const char* cursorPreviousName; - int cursorTimerfd; - uint32_t serial; - uint32_t pointerEnterSerial; - - int32_t keyboardRepeatRate; - int32_t keyboardRepeatDelay; - int keyboardLastKey; - int keyboardLastScancode; - char* clipboardString; - int timerfd; - short int keycodes[256]; - short int scancodes[GLFW_KEY_LAST + 1]; - char keynames[GLFW_KEY_LAST + 1][5]; - - struct { - void* handle; - struct xkb_context* context; - struct xkb_keymap* keymap; - struct xkb_state* state; - struct xkb_compose_state* composeState; - - xkb_mod_index_t controlIndex; - xkb_mod_index_t altIndex; - xkb_mod_index_t shiftIndex; - xkb_mod_index_t superIndex; - xkb_mod_index_t capsLockIndex; - xkb_mod_index_t numLockIndex; - unsigned int modifiers; - - PFN_xkb_context_new context_new; - PFN_xkb_context_unref context_unref; - PFN_xkb_keymap_new_from_string keymap_new_from_string; - PFN_xkb_keymap_unref keymap_unref; - PFN_xkb_keymap_mod_get_index keymap_mod_get_index; - PFN_xkb_keymap_key_repeats keymap_key_repeats; - PFN_xkb_keymap_key_get_syms_by_level keymap_key_get_syms_by_level; - PFN_xkb_state_new state_new; - PFN_xkb_state_unref state_unref; - PFN_xkb_state_key_get_syms state_key_get_syms; - PFN_xkb_state_update_mask state_update_mask; - PFN_xkb_state_key_get_layout state_key_get_layout; - PFN_xkb_state_mod_index_is_active state_mod_index_is_active; - - PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale; - PFN_xkb_compose_table_unref compose_table_unref; - PFN_xkb_compose_state_new compose_state_new; - PFN_xkb_compose_state_unref compose_state_unref; - PFN_xkb_compose_state_feed compose_state_feed; - PFN_xkb_compose_state_get_status compose_state_get_status; - PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym; - } xkb; - - _GLFWwindow* pointerFocus; - _GLFWwindow* keyboardFocus; - - struct { - void* handle; - - PFN_wl_cursor_theme_load theme_load; - PFN_wl_cursor_theme_destroy theme_destroy; - PFN_wl_cursor_theme_get_cursor theme_get_cursor; - PFN_wl_cursor_image_get_buffer image_get_buffer; - } cursor; - - struct { - void* handle; - - PFN_wl_egl_window_create window_create; - PFN_wl_egl_window_destroy window_destroy; - PFN_wl_egl_window_resize window_resize; - } egl; -} _GLFWlibraryWayland; - -// Wayland-specific per-monitor data -// -typedef struct _GLFWmonitorWayland -{ - struct wl_output* output; - uint32_t name; - int currentMode; - - int x; - int y; - int scale; -} _GLFWmonitorWayland; - -// Wayland-specific per-cursor data -// -typedef struct _GLFWcursorWayland -{ - struct wl_cursor* cursor; - struct wl_cursor* cursorHiDPI; - struct wl_buffer* buffer; - int width, height; - int xhot, yhot; - int currentImage; -} _GLFWcursorWayland; - -void _glfwAddOutputWayland(uint32_t name, uint32_t version); -void _glfwUpdateContentScaleWayland(_GLFWwindow* window); -GLFWbool _glfwInputTextWayland(_GLFWwindow* window, uint32_t scancode); - -void _glfwAddSeatListenerWayland(struct wl_seat* seat); -void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device); - diff --git a/libs/zglfw/libs/glfw/src/wl_window.c b/libs/zglfw/libs/glfw/src/wl_window.c deleted file mode 100644 index 53cbd33b1..000000000 --- a/libs/zglfw/libs/glfw/src/wl_window.c +++ /dev/null @@ -1,2794 +0,0 @@ -//======================================================================== -// GLFW 3.3 Wayland - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014 Jonas Ã…dahl -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#define _GNU_SOURCE - -#include "internal.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GLFW_BORDER_SIZE 4 -#define GLFW_CAPTION_HEIGHT 24 - -static int createTmpfileCloexec(char* tmpname) -{ - int fd; - - fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0) - unlink(tmpname); - - return fd; -} - -/* - * Create a new, unique, anonymous file of the given size, and - * return the file descriptor for it. The file descriptor is set - * CLOEXEC. The file is immediately suitable for mmap()'ing - * the given size at offset zero. - * - * The file should not have a permanent backing store like a disk, - * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. - * - * The file name is deleted from the file system. - * - * The file is suitable for buffer sharing between processes by - * transmitting the file descriptor over Unix sockets using the - * SCM_RIGHTS methods. - * - * posix_fallocate() is used to guarantee that disk space is available - * for the file at the given size. If disk space is insufficient, errno - * is set to ENOSPC. If posix_fallocate() is not supported, program may - * receive SIGBUS on accessing mmap()'ed file contents instead. - */ -static int createAnonymousFile(off_t size) -{ - static const char template[] = "/glfw-shared-XXXXXX"; - const char* path; - char* name; - int fd; - int ret; - -#ifdef HAVE_MEMFD_CREATE - fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING); - if (fd >= 0) - { - // We can add this seal before calling posix_fallocate(), as the file - // is currently zero-sized anyway. - // - // There is also no need to check for the return value, we couldn’t do - // anything with it anyway. - fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); - } - else -#elif defined(SHM_ANON) - fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600); - if (fd < 0) -#endif - { - path = getenv("XDG_RUNTIME_DIR"); - if (!path) - { - errno = ENOENT; - return -1; - } - - name = calloc(strlen(path) + sizeof(template), 1); - strcpy(name, path); - strcat(name, template); - - fd = createTmpfileCloexec(name); - free(name); - if (fd < 0) - return -1; - } - -#if defined(SHM_ANON) - // posix_fallocate does not work on SHM descriptors - ret = ftruncate(fd, size); -#else - ret = posix_fallocate(fd, 0, size); -#endif - if (ret != 0) - { - close(fd); - errno = ret; - return -1; - } - return fd; -} - -static struct wl_buffer* createShmBuffer(const GLFWimage* image) -{ - struct wl_shm_pool* pool; - struct wl_buffer* buffer; - int stride = image->width * 4; - int length = image->width * image->height * 4; - void* data; - int fd, i; - - fd = createAnonymousFile(length); - if (fd < 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create buffer file of size %d: %s", - length, strerror(errno)); - return NULL; - } - - data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to map file: %s", strerror(errno)); - close(fd); - return NULL; - } - - pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); - - close(fd); - unsigned char* source = (unsigned char*) image->pixels; - unsigned char* target = data; - for (i = 0; i < image->width * image->height; i++, source += 4) - { - unsigned int alpha = source[3]; - - *target++ = (unsigned char) ((source[2] * alpha) / 255); - *target++ = (unsigned char) ((source[1] * alpha) / 255); - *target++ = (unsigned char) ((source[0] * alpha) / 255); - *target++ = (unsigned char) alpha; - } - - buffer = - wl_shm_pool_create_buffer(pool, 0, - image->width, - image->height, - stride, WL_SHM_FORMAT_ARGB8888); - munmap(data, length); - wl_shm_pool_destroy(pool); - - return buffer; -} - -// Wait for data to arrive on any of the specified file descriptors -// -static GLFWbool waitForData(struct pollfd* fds, nfds_t count, double* timeout) -{ - for (;;) - { - if (timeout) - { - const uint64_t base = _glfwPlatformGetTimerValue(); - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) - const time_t seconds = (time_t) *timeout; - const long nanoseconds = (long) ((*timeout - seconds) * 1e9); - const struct timespec ts = { seconds, nanoseconds }; - const int result = ppoll(fds, count, &ts, NULL); -#elif defined(__NetBSD__) - const time_t seconds = (time_t) *timeout; - const long nanoseconds = (long) ((*timeout - seconds) * 1e9); - const struct timespec ts = { seconds, nanoseconds }; - const int result = pollts(fds, count, &ts, NULL); -#else - const int milliseconds = (int) (*timeout * 1e3); - const int result = poll(fds, count, milliseconds); -#endif - const int error = errno; // clock_gettime may overwrite our error - - *timeout -= (_glfwPlatformGetTimerValue() - base) / - (double) _glfwPlatformGetTimerFrequency(); - - if (result > 0) - return GLFW_TRUE; - else if (result == -1 && error != EINTR && error != EAGAIN) - return GLFW_FALSE; - else if (*timeout <= 0.0) - return GLFW_FALSE; - } - else - { - const int result = poll(fds, count, -1); - if (result > 0) - return GLFW_TRUE; - else if (result == -1 && errno != EINTR && errno != EAGAIN) - return GLFW_FALSE; - } - } -} - -static void createFallbackDecoration(_GLFWdecorationWayland* decoration, - struct wl_surface* parent, - struct wl_buffer* buffer, - int x, int y, - int width, int height) -{ - decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor); - decoration->subsurface = - wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, - decoration->surface, parent); - wl_subsurface_set_position(decoration->subsurface, x, y); - decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter, - decoration->surface); - wp_viewport_set_destination(decoration->viewport, width, height); - wl_surface_attach(decoration->surface, buffer, 0, 0); - - struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor); - wl_region_add(region, 0, 0, width, height); - wl_surface_set_opaque_region(decoration->surface, region); - wl_surface_commit(decoration->surface); - wl_region_destroy(region); -} - -static void createFallbackDecorations(_GLFWwindow* window) -{ - unsigned char data[] = { 224, 224, 224, 255 }; - const GLFWimage image = { 1, 1, data }; - - if (!_glfw.wl.viewporter) - return; - - if (!window->wl.decorations.buffer) - window->wl.decorations.buffer = createShmBuffer(&image); - if (!window->wl.decorations.buffer) - return; - - createFallbackDecoration(&window->wl.decorations.top, window->wl.surface, - window->wl.decorations.buffer, - 0, -GLFW_CAPTION_HEIGHT, - window->wl.width, GLFW_CAPTION_HEIGHT); - createFallbackDecoration(&window->wl.decorations.left, window->wl.surface, - window->wl.decorations.buffer, - -GLFW_BORDER_SIZE, -GLFW_CAPTION_HEIGHT, - GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); - createFallbackDecoration(&window->wl.decorations.right, window->wl.surface, - window->wl.decorations.buffer, - window->wl.width, -GLFW_CAPTION_HEIGHT, - GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); - createFallbackDecoration(&window->wl.decorations.bottom, window->wl.surface, - window->wl.decorations.buffer, - -GLFW_BORDER_SIZE, window->wl.height, - window->wl.width + GLFW_BORDER_SIZE * 2, GLFW_BORDER_SIZE); -} - -static void destroyFallbackDecoration(_GLFWdecorationWayland* decoration) -{ - if (decoration->subsurface) - wl_subsurface_destroy(decoration->subsurface); - if (decoration->surface) - wl_surface_destroy(decoration->surface); - if (decoration->viewport) - wp_viewport_destroy(decoration->viewport); - decoration->surface = NULL; - decoration->subsurface = NULL; - decoration->viewport = NULL; -} - -static void destroyFallbackDecorations(_GLFWwindow* window) -{ - destroyFallbackDecoration(&window->wl.decorations.top); - destroyFallbackDecoration(&window->wl.decorations.left); - destroyFallbackDecoration(&window->wl.decorations.right); - destroyFallbackDecoration(&window->wl.decorations.bottom); -} - -static void xdgDecorationHandleConfigure(void* userData, - struct zxdg_toplevel_decoration_v1* decoration, - uint32_t mode) -{ - _GLFWwindow* window = userData; - - window->wl.xdg.decorationMode = mode; - - if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) - { - if (window->decorated && !window->monitor) - createFallbackDecorations(window); - } - else - destroyFallbackDecorations(window); -} - -static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = -{ - xdgDecorationHandleConfigure, -}; - -// Makes the surface considered as XRGB instead of ARGB. -static void setContentAreaOpaque(_GLFWwindow* window) -{ - struct wl_region* region; - - region = wl_compositor_create_region(_glfw.wl.compositor); - if (!region) - return; - - wl_region_add(region, 0, 0, window->wl.width, window->wl.height); - wl_surface_set_opaque_region(window->wl.surface, region); - wl_region_destroy(region); -} - - -static void resizeWindow(_GLFWwindow* window) -{ - int scale = window->wl.scale; - int scaledWidth = window->wl.width * scale; - int scaledHeight = window->wl.height * scale; - - if (window->wl.egl.window) - wl_egl_window_resize(window->wl.egl.window, scaledWidth, scaledHeight, 0, 0); - if (!window->wl.transparent) - setContentAreaOpaque(window); - _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); - - if (!window->wl.decorations.top.surface) - return; - - wp_viewport_set_destination(window->wl.decorations.top.viewport, - window->wl.width, GLFW_CAPTION_HEIGHT); - wl_surface_commit(window->wl.decorations.top.surface); - - wp_viewport_set_destination(window->wl.decorations.left.viewport, - GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); - wl_surface_commit(window->wl.decorations.left.surface); - - wl_subsurface_set_position(window->wl.decorations.right.subsurface, - window->wl.width, -GLFW_CAPTION_HEIGHT); - wp_viewport_set_destination(window->wl.decorations.right.viewport, - GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); - wl_surface_commit(window->wl.decorations.right.surface); - - wl_subsurface_set_position(window->wl.decorations.bottom.subsurface, - -GLFW_BORDER_SIZE, window->wl.height); - wp_viewport_set_destination(window->wl.decorations.bottom.viewport, - window->wl.width + GLFW_BORDER_SIZE * 2, GLFW_BORDER_SIZE); - wl_surface_commit(window->wl.decorations.bottom.surface); -} - -void _glfwUpdateContentScaleWayland(_GLFWwindow* window) -{ - if (_glfw.wl.compositorVersion < WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) - return; - - // Get the scale factor from the highest scale monitor. - int maxScale = 1; - - for (int i = 0; i < window->wl.monitorsCount; i++) - maxScale = _glfw_max(window->wl.monitors[i]->wl.scale, maxScale); - - // Only change the framebuffer size if the scale changed. - if (window->wl.scale != maxScale) - { - window->wl.scale = maxScale; - wl_surface_set_buffer_scale(window->wl.surface, maxScale); - _glfwInputWindowContentScale(window, maxScale, maxScale); - resizeWindow(window); - } -} - -static void surfaceHandleEnter(void* userData, - struct wl_surface* surface, - struct wl_output* output) -{ - _GLFWwindow* window = userData; - _GLFWmonitor* monitor = wl_output_get_user_data(output); - - if (window->wl.monitorsCount + 1 > window->wl.monitorsSize) - { - ++window->wl.monitorsSize; - window->wl.monitors = - realloc(window->wl.monitors, - window->wl.monitorsSize * sizeof(_GLFWmonitor*)); - } - - window->wl.monitors[window->wl.monitorsCount++] = monitor; - - _glfwUpdateContentScaleWayland(window); -} - -static void surfaceHandleLeave(void* userData, - struct wl_surface* surface, - struct wl_output* output) -{ - _GLFWwindow* window = userData; - _GLFWmonitor* monitor = wl_output_get_user_data(output); - GLFWbool found; - int i; - - for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i) - { - if (monitor == window->wl.monitors[i]) - found = GLFW_TRUE; - if (found) - window->wl.monitors[i] = window->wl.monitors[i + 1]; - } - window->wl.monitors[--window->wl.monitorsCount] = NULL; - - _glfwUpdateContentScaleWayland(window); -} - -static const struct wl_surface_listener surfaceListener = -{ - surfaceHandleEnter, - surfaceHandleLeave -}; - -static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable) -{ - if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager) - { - window->wl.idleInhibitor = - zwp_idle_inhibit_manager_v1_create_inhibitor( - _glfw.wl.idleInhibitManager, window->wl.surface); - if (!window->wl.idleInhibitor) - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create idle inhibitor"); - } - else if (!enable && window->wl.idleInhibitor) - { - zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); - window->wl.idleInhibitor = NULL; - } -} - -// Make the specified window and its video mode active on its monitor -// -static void acquireMonitor(_GLFWwindow* window) -{ - if (window->wl.xdg.toplevel) - { - xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, - window->monitor->wl.output); - } - - setIdleInhibitor(window, GLFW_TRUE); - - if (window->wl.decorations.top.surface) - destroyFallbackDecorations(window); -} - -// Remove the window and restore the original video mode -// -static void releaseMonitor(_GLFWwindow* window) -{ - if (window->wl.xdg.toplevel) - xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); - - setIdleInhibitor(window, GLFW_FALSE); - - if (window->wl.xdg.decorationMode != ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) - { - if (window->decorated) - createFallbackDecorations(window); - } -} - -static void xdgToplevelHandleConfigure(void* userData, - struct xdg_toplevel* toplevel, - int32_t width, - int32_t height, - struct wl_array* states) -{ - _GLFWwindow* window = userData; - uint32_t* state; - - window->wl.pending.activated = GLFW_FALSE; - window->wl.pending.maximized = GLFW_FALSE; - window->wl.pending.fullscreen = GLFW_FALSE; - - wl_array_for_each(state, states) - { - switch (*state) - { - case XDG_TOPLEVEL_STATE_MAXIMIZED: - window->wl.pending.maximized = GLFW_TRUE; - break; - case XDG_TOPLEVEL_STATE_FULLSCREEN: - window->wl.pending.fullscreen = GLFW_TRUE; - break; - case XDG_TOPLEVEL_STATE_RESIZING: - break; - case XDG_TOPLEVEL_STATE_ACTIVATED: - window->wl.pending.activated = GLFW_TRUE; - break; - } - } - - if (width && height) - { - if (window->wl.decorations.top.surface) - { - window->wl.pending.width = _glfw_max(0, width - GLFW_BORDER_SIZE * 2); - window->wl.pending.height = - _glfw_max(0, height - GLFW_BORDER_SIZE - GLFW_CAPTION_HEIGHT); - } - else - { - window->wl.pending.width = width; - window->wl.pending.height = height; - } - } - else - { - window->wl.pending.width = window->wl.width; - window->wl.pending.height = window->wl.height; - } -} - -static void xdgToplevelHandleClose(void* userData, - struct xdg_toplevel* toplevel) -{ - _GLFWwindow* window = userData; - _glfwInputWindowCloseRequest(window); -} - -static const struct xdg_toplevel_listener xdgToplevelListener = -{ - xdgToplevelHandleConfigure, - xdgToplevelHandleClose -}; - -static void xdgSurfaceHandleConfigure(void* userData, - struct xdg_surface* surface, - uint32_t serial) -{ - _GLFWwindow* window = userData; - - xdg_surface_ack_configure(surface, serial); - - if (window->wl.activated != window->wl.pending.activated) - { - window->wl.activated = window->wl.pending.activated; - if (!window->wl.activated) - { - if (window->monitor && window->autoIconify) - xdg_toplevel_set_minimized(window->wl.xdg.toplevel); - } - } - - if (window->wl.maximized != window->wl.pending.maximized) - { - window->wl.maximized = window->wl.pending.maximized; - _glfwInputWindowMaximize(window, window->wl.maximized); - } - - window->wl.fullscreen = window->wl.pending.fullscreen; - - int width = window->wl.pending.width; - int height = window->wl.pending.height; - - if (!window->wl.maximized && !window->wl.fullscreen) - { - if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) - { - const float aspectRatio = (float) width / (float) height; - const float targetRatio = (float) window->numer / (float) window->denom; - if (aspectRatio < targetRatio) - height = width / targetRatio; - else if (aspectRatio > targetRatio) - width = height * targetRatio; - } - } - - if (width != window->wl.width || height != window->wl.height) - { - window->wl.width = width; - window->wl.height = height; - resizeWindow(window); - - _glfwInputWindowSize(window, width, height); - - if (window->wl.visible) - _glfwInputWindowDamage(window); - } - - if (!window->wl.visible) - { - // Allow the window to be mapped only if it either has no XDG - // decorations or they have already received a configure event - if (!window->wl.xdg.decoration || window->wl.xdg.decorationMode) - { - window->wl.visible = GLFW_TRUE; - _glfwInputWindowDamage(window); - } - } -} - -static const struct xdg_surface_listener xdgSurfaceListener = -{ - xdgSurfaceHandleConfigure -}; - -static GLFWbool createShellObjects(_GLFWwindow* window) -{ - window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase, - window->wl.surface); - if (!window->wl.xdg.surface) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create xdg-surface for window"); - return GLFW_FALSE; - } - - xdg_surface_add_listener(window->wl.xdg.surface, &xdgSurfaceListener, window); - - window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface); - if (!window->wl.xdg.toplevel) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create xdg-toplevel for window"); - return GLFW_FALSE; - } - - xdg_toplevel_add_listener(window->wl.xdg.toplevel, &xdgToplevelListener, window); - - if (window->wl.title) - xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title); - - if (window->monitor) - { - xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, window->monitor->wl.output); - setIdleInhibitor(window, GLFW_TRUE); - } - else - { - if (window->wl.maximized) - xdg_toplevel_set_maximized(window->wl.xdg.toplevel); - - setIdleInhibitor(window, GLFW_FALSE); - - if (_glfw.wl.decorationManager) - { - window->wl.xdg.decoration = - zxdg_decoration_manager_v1_get_toplevel_decoration( - _glfw.wl.decorationManager, window->wl.xdg.toplevel); - zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration, - &xdgDecorationListener, - window); - - uint32_t mode; - - if (window->decorated) - mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; - else - mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; - - zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode); - } - else - { - if (window->decorated) - createFallbackDecorations(window); - } - } - - if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) - { - int minwidth = window->minwidth; - int minheight = window->minheight; - - if (window->wl.decorations.top.surface) - { - minwidth += GLFW_BORDER_SIZE * 2; - minheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE; - } - - xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight); - } - - if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) - { - int maxwidth = window->maxwidth; - int maxheight = window->maxheight; - - if (window->wl.decorations.top.surface) - { - maxwidth += GLFW_BORDER_SIZE * 2; - maxheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE; - } - - xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight); - } - - wl_surface_commit(window->wl.surface); - wl_display_roundtrip(_glfw.wl.display); - - return GLFW_TRUE; -} - -static void destroyShellObjects(_GLFWwindow* window) -{ - destroyFallbackDecorations(window); - - if (window->wl.xdg.decoration) - zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration); - - if (window->wl.xdg.toplevel) - xdg_toplevel_destroy(window->wl.xdg.toplevel); - - if (window->wl.xdg.surface) - xdg_surface_destroy(window->wl.xdg.surface); - - window->wl.xdg.decoration = NULL; - window->wl.xdg.decorationMode = 0; - window->wl.xdg.toplevel = NULL; - window->wl.xdg.surface = NULL; -} - -static GLFWbool createNativeSurface(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWfbconfig* fbconfig) -{ - window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor); - if (!window->wl.surface) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create window surface"); - return GLFW_FALSE; - } - - wl_surface_add_listener(window->wl.surface, - &surfaceListener, - window); - - wl_surface_set_user_data(window->wl.surface, window); - - window->wl.width = wndconfig->width; - window->wl.height = wndconfig->height; - window->wl.scale = 1; - window->wl.title = _glfw_strdup(wndconfig->title); - - window->wl.maximized = wndconfig->maximized; - - window->wl.transparent = fbconfig->transparent; - if (!window->wl.transparent) - setContentAreaOpaque(window); - - return GLFW_TRUE; -} - -static void setCursorImage(_GLFWwindow* window, - _GLFWcursorWayland* cursorWayland) -{ - struct itimerspec timer = {}; - struct wl_cursor* wlCursor = cursorWayland->cursor; - struct wl_cursor_image* image; - struct wl_buffer* buffer; - struct wl_surface* surface = _glfw.wl.cursorSurface; - int scale = 1; - - if (!wlCursor) - buffer = cursorWayland->buffer; - else - { - if (window->wl.scale > 1 && cursorWayland->cursorHiDPI) - { - wlCursor = cursorWayland->cursorHiDPI; - scale = 2; - } - - image = wlCursor->images[cursorWayland->currentImage]; - buffer = wl_cursor_image_get_buffer(image); - if (!buffer) - return; - - timer.it_value.tv_sec = image->delay / 1000; - timer.it_value.tv_nsec = (image->delay % 1000) * 1000000; - timerfd_settime(_glfw.wl.cursorTimerfd, 0, &timer, NULL); - - cursorWayland->width = image->width; - cursorWayland->height = image->height; - cursorWayland->xhot = image->hotspot_x; - cursorWayland->yhot = image->hotspot_y; - } - - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, - surface, - cursorWayland->xhot / scale, - cursorWayland->yhot / scale); - wl_surface_set_buffer_scale(surface, scale); - wl_surface_attach(surface, buffer, 0, 0); - wl_surface_damage(surface, 0, 0, - cursorWayland->width, cursorWayland->height); - wl_surface_commit(surface); -} - -static void incrementCursorImage(_GLFWwindow* window) -{ - _GLFWcursor* cursor; - - if (!window || window->wl.decorations.focus != mainWindow) - return; - - cursor = window->wl.currentCursor; - if (cursor && cursor->wl.cursor) - { - cursor->wl.currentImage += 1; - cursor->wl.currentImage %= cursor->wl.cursor->image_count; - setCursorImage(window, &cursor->wl); - } -} - -static GLFWbool flushDisplay(void) -{ - while (wl_display_flush(_glfw.wl.display) == -1) - { - if (errno != EAGAIN) - return GLFW_FALSE; - - struct pollfd fd = { wl_display_get_fd(_glfw.wl.display), POLLOUT }; - - while (poll(&fd, 1, -1) == -1) - { - if (errno != EINTR && errno != EAGAIN) - return GLFW_FALSE; - } - } - - return GLFW_TRUE; -} - -static void handleEvents(double* timeout) -{ - GLFWbool event = GLFW_FALSE; - struct pollfd fds[] = - { - { wl_display_get_fd(_glfw.wl.display), POLLIN }, - { _glfw.wl.timerfd, POLLIN }, - { _glfw.wl.cursorTimerfd, POLLIN }, - }; - - while (!event) - { - while (wl_display_prepare_read(_glfw.wl.display) != 0) - wl_display_dispatch_pending(_glfw.wl.display); - - // If an error other than EAGAIN happens, we have likely been disconnected - // from the Wayland session; try to handle that the best we can. - if (!flushDisplay()) - { - wl_display_cancel_read(_glfw.wl.display); - - _GLFWwindow* window = _glfw.windowListHead; - while (window) - { - _glfwInputWindowCloseRequest(window); - window = window->next; - } - - return; - } - - if (!waitForData(fds, 3, timeout)) - { - wl_display_cancel_read(_glfw.wl.display); - return; - } - - if (fds[0].revents & POLLIN) - { - wl_display_read_events(_glfw.wl.display); - if (wl_display_dispatch_pending(_glfw.wl.display) > 0) - event = GLFW_TRUE; - } - else - wl_display_cancel_read(_glfw.wl.display); - - if (fds[1].revents & POLLIN) - { - uint64_t repeats; - - if (read(_glfw.wl.timerfd, &repeats, sizeof(repeats)) == 8) - { - for (uint64_t i = 0; i < repeats; i++) - { - _glfwInputKey(_glfw.wl.keyboardFocus, - _glfw.wl.keyboardLastKey, - _glfw.wl.keyboardLastScancode, - GLFW_PRESS, - _glfw.wl.xkb.modifiers); - _glfwInputTextWayland(_glfw.wl.keyboardFocus, - _glfw.wl.keyboardLastScancode); - } - - event = GLFW_TRUE; - } - } - - if (fds[2].revents & POLLIN) - { - uint64_t repeats; - - if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8) - { - incrementCursorImage(_glfw.wl.pointerFocus); - event = GLFW_TRUE; - } - } - } -} - -// Reads the specified data offer as the specified MIME type -// -static char* readDataOfferAsString(struct wl_data_offer* offer, const char* mimeType) -{ - int fds[2]; - - if (pipe2(fds, O_CLOEXEC) == -1) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create pipe for data offer: %s", - strerror(errno)); - return NULL; - } - - wl_data_offer_receive(offer, mimeType, fds[1]); - flushDisplay(); - close(fds[1]); - - char* string = NULL; - size_t size = 0; - size_t length = 0; - - for (;;) - { - const size_t readSize = 4096; - const size_t requiredSize = length + readSize + 1; - if (requiredSize > size) - { - char* longer = realloc(string, requiredSize); - if (!longer) - { - _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); - close(fds[0]); - return NULL; - } - - string = longer; - size = requiredSize; - } - - const ssize_t result = read(fds[0], string + length, readSize); - if (result == 0) - break; - else if (result == -1) - { - if (errno == EINTR) - continue; - - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to read from data offer pipe: %s", - strerror(errno)); - close(fds[0]); - return NULL; - } - - length += result; - } - - close(fds[0]); - - string[length] = '\0'; - return string; -} - -static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, - _GLFWdecorationSideWayland* which) -{ - _GLFWdecorationSideWayland focus; - _GLFWwindow* window = _glfw.windowListHead; - if (!which) - which = &focus; - while (window) - { - if (surface == window->wl.decorations.top.surface) - { - *which = topDecoration; - break; - } - if (surface == window->wl.decorations.left.surface) - { - *which = leftDecoration; - break; - } - if (surface == window->wl.decorations.right.surface) - { - *which = rightDecoration; - break; - } - if (surface == window->wl.decorations.bottom.surface) - { - *which = bottomDecoration; - break; - } - window = window->next; - } - return window; -} - -static void pointerHandleEnter(void* userData, - struct wl_pointer* pointer, - uint32_t serial, - struct wl_surface* surface, - wl_fixed_t sx, - wl_fixed_t sy) -{ - // Happens in the case we just destroyed the surface. - if (!surface) - return; - - _GLFWdecorationSideWayland focus = mainWindow; - _GLFWwindow* window = wl_surface_get_user_data(surface); - if (!window) - { - window = findWindowFromDecorationSurface(surface, &focus); - if (!window) - return; - } - - window->wl.decorations.focus = focus; - _glfw.wl.serial = serial; - _glfw.wl.pointerEnterSerial = serial; - _glfw.wl.pointerFocus = window; - - window->wl.hovered = GLFW_TRUE; - - _glfwPlatformSetCursor(window, window->wl.currentCursor); - _glfwInputCursorEnter(window, GLFW_TRUE); -} - -static void pointerHandleLeave(void* userData, - struct wl_pointer* pointer, - uint32_t serial, - struct wl_surface* surface) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - - if (!window) - return; - - window->wl.hovered = GLFW_FALSE; - - _glfw.wl.serial = serial; - _glfw.wl.pointerFocus = NULL; - _glfwInputCursorEnter(window, GLFW_FALSE); - _glfw.wl.cursorPreviousName = NULL; -} - -static void setCursor(_GLFWwindow* window, const char* name) -{ - struct wl_buffer* buffer; - struct wl_cursor* cursor; - struct wl_cursor_image* image; - struct wl_surface* surface = _glfw.wl.cursorSurface; - struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; - int scale = 1; - - if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) - { - // We only support up to scale=2 for now, since libwayland-cursor - // requires us to load a different theme for each size. - scale = 2; - theme = _glfw.wl.cursorThemeHiDPI; - } - - cursor = wl_cursor_theme_get_cursor(theme, name); - if (!cursor) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Standard cursor shape unavailable"); - return; - } - // TODO: handle animated cursors too. - image = cursor->images[0]; - - if (!image) - return; - - buffer = wl_cursor_image_get_buffer(image); - if (!buffer) - return; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, - surface, - image->hotspot_x / scale, - image->hotspot_y / scale); - wl_surface_set_buffer_scale(surface, scale); - wl_surface_attach(surface, buffer, 0, 0); - wl_surface_damage(surface, 0, 0, - image->width, image->height); - wl_surface_commit(surface); - _glfw.wl.cursorPreviousName = name; -} - -static void pointerHandleMotion(void* userData, - struct wl_pointer* pointer, - uint32_t time, - wl_fixed_t sx, - wl_fixed_t sy) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - const char* cursorName = NULL; - double x, y; - - if (!window) - return; - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - return; - x = wl_fixed_to_double(sx); - y = wl_fixed_to_double(sy); - window->wl.cursorPosX = x; - window->wl.cursorPosY = y; - - switch (window->wl.decorations.focus) - { - case mainWindow: - _glfwInputCursorPos(window, x, y); - _glfw.wl.cursorPreviousName = NULL; - return; - case topDecoration: - if (y < GLFW_BORDER_SIZE) - cursorName = "n-resize"; - else - cursorName = "left_ptr"; - break; - case leftDecoration: - if (y < GLFW_BORDER_SIZE) - cursorName = "nw-resize"; - else - cursorName = "w-resize"; - break; - case rightDecoration: - if (y < GLFW_BORDER_SIZE) - cursorName = "ne-resize"; - else - cursorName = "e-resize"; - break; - case bottomDecoration: - if (x < GLFW_BORDER_SIZE) - cursorName = "sw-resize"; - else if (x > window->wl.width + GLFW_BORDER_SIZE) - cursorName = "se-resize"; - else - cursorName = "s-resize"; - break; - default: - assert(0); - } - if (_glfw.wl.cursorPreviousName != cursorName) - setCursor(window, cursorName); -} - -static void pointerHandleButton(void* userData, - struct wl_pointer* pointer, - uint32_t serial, - uint32_t time, - uint32_t button, - uint32_t state) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - int glfwButton; - - uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE; - - if (!window) - return; - if (button == BTN_LEFT) - { - switch (window->wl.decorations.focus) - { - case mainWindow: - break; - case topDecoration: - if (window->wl.cursorPosY < GLFW_BORDER_SIZE) - edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; - else - { - xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial); - } - break; - case leftDecoration: - if (window->wl.cursorPosY < GLFW_BORDER_SIZE) - edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; - else - edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; - break; - case rightDecoration: - if (window->wl.cursorPosY < GLFW_BORDER_SIZE) - edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; - else - edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; - break; - case bottomDecoration: - if (window->wl.cursorPosX < GLFW_BORDER_SIZE) - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - else if (window->wl.cursorPosX > window->wl.width + GLFW_BORDER_SIZE) - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - else - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; - break; - default: - assert(0); - } - if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) - { - xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, - serial, edges); - return; - } - } - else if (button == BTN_RIGHT) - { - if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel) - { - xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, - _glfw.wl.seat, serial, - window->wl.cursorPosX, - window->wl.cursorPosY); - return; - } - } - - // Don’t pass the button to the user if it was related to a decoration. - if (window->wl.decorations.focus != mainWindow) - return; - - _glfw.wl.serial = serial; - - /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev - * codes. */ - glfwButton = button - BTN_LEFT; - - _glfwInputMouseClick(window, - glfwButton, - state == WL_POINTER_BUTTON_STATE_PRESSED - ? GLFW_PRESS - : GLFW_RELEASE, - _glfw.wl.xkb.modifiers); -} - -static void pointerHandleAxis(void* userData, - struct wl_pointer* pointer, - uint32_t time, - uint32_t axis, - wl_fixed_t value) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - double x = 0.0, y = 0.0; - // Wayland scroll events are in pointer motion coordinate space (think two - // finger scroll). The factor 10 is commonly used to convert to "scroll - // step means 1.0. - const double scrollFactor = 1.0 / 10.0; - - if (!window) - return; - - assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || - axis == WL_POINTER_AXIS_VERTICAL_SCROLL); - - if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) - x = -wl_fixed_to_double(value) * scrollFactor; - else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) - y = -wl_fixed_to_double(value) * scrollFactor; - - _glfwInputScroll(window, x, y); -} - -static const struct wl_pointer_listener pointerListener = -{ - pointerHandleEnter, - pointerHandleLeave, - pointerHandleMotion, - pointerHandleButton, - pointerHandleAxis, -}; - -static void keyboardHandleKeymap(void* userData, - struct wl_keyboard* keyboard, - uint32_t format, - int fd, - uint32_t size) -{ - struct xkb_keymap* keymap; - struct xkb_state* state; - struct xkb_compose_table* composeTable; - struct xkb_compose_state* composeState; - char* mapStr; - const char* locale; - - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) - { - close(fd); - return; - } - - mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (mapStr == MAP_FAILED) { - close(fd); - return; - } - - keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, - mapStr, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); - munmap(mapStr, size); - close(fd); - - if (!keymap) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to compile keymap"); - return; - } - - state = xkb_state_new(keymap); - if (!state) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create XKB state"); - xkb_keymap_unref(keymap); - return; - } - - // Look up the preferred locale, falling back to "C" as default. - locale = getenv("LC_ALL"); - if (!locale) - locale = getenv("LC_CTYPE"); - if (!locale) - locale = getenv("LANG"); - if (!locale) - locale = "C"; - - composeTable = - xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, - XKB_COMPOSE_COMPILE_NO_FLAGS); - if (composeTable) - { - composeState = - xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); - xkb_compose_table_unref(composeTable); - if (composeState) - _glfw.wl.xkb.composeState = composeState; - else - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create XKB compose state"); - } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create XKB compose table"); - } - - xkb_keymap_unref(_glfw.wl.xkb.keymap); - xkb_state_unref(_glfw.wl.xkb.state); - _glfw.wl.xkb.keymap = keymap; - _glfw.wl.xkb.state = state; - - _glfw.wl.xkb.controlIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); - _glfw.wl.xkb.altIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); - _glfw.wl.xkb.shiftIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); - _glfw.wl.xkb.superIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); - _glfw.wl.xkb.capsLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock"); - _glfw.wl.xkb.numLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2"); -} - -static void keyboardHandleEnter(void* userData, - struct wl_keyboard* keyboard, - uint32_t serial, - struct wl_surface* surface, - struct wl_array* keys) -{ - // Happens in the case we just destroyed the surface. - if (!surface) - return; - - _GLFWwindow* window = wl_surface_get_user_data(surface); - if (!window) - { - window = findWindowFromDecorationSurface(surface, NULL); - if (!window) - return; - } - - _glfw.wl.serial = serial; - _glfw.wl.keyboardFocus = window; - _glfwInputWindowFocus(window, GLFW_TRUE); -} - -static void keyboardHandleLeave(void* userData, - struct wl_keyboard* keyboard, - uint32_t serial, - struct wl_surface* surface) -{ - _GLFWwindow* window = _glfw.wl.keyboardFocus; - - if (!window) - return; - - struct itimerspec timer = {}; - timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); - - _glfw.wl.serial = serial; - _glfw.wl.keyboardFocus = NULL; - _glfwInputWindowFocus(window, GLFW_FALSE); -} - -static int translateKey(uint32_t scancode) -{ - if (scancode < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) - return _glfw.wl.keycodes[scancode]; - - return GLFW_KEY_UNKNOWN; -} - -static xkb_keysym_t composeSymbol(xkb_keysym_t sym) -{ - if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) - return sym; - if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) - != XKB_COMPOSE_FEED_ACCEPTED) - return sym; - switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) - { - case XKB_COMPOSE_COMPOSED: - return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); - case XKB_COMPOSE_COMPOSING: - case XKB_COMPOSE_CANCELLED: - return XKB_KEY_NoSymbol; - case XKB_COMPOSE_NOTHING: - default: - return sym; - } -} - -GLFWbool _glfwInputTextWayland(_GLFWwindow* window, uint32_t scancode) -{ - const xkb_keysym_t* keysyms; - const xkb_keycode_t keycode = scancode + 8; - - if (xkb_state_key_get_syms(_glfw.wl.xkb.state, keycode, &keysyms) == 1) - { - const xkb_keysym_t keysym = composeSymbol(keysyms[0]); - const uint32_t codepoint = _glfwKeySym2Unicode(keysym); - if (codepoint != GLFW_INVALID_CODEPOINT) - { - const int mods = _glfw.wl.xkb.modifiers; - const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - _glfwInputChar(window, codepoint, mods, plain); - } - } - - return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode); -} - -static void keyboardHandleKey(void* userData, - struct wl_keyboard* keyboard, - uint32_t serial, - uint32_t time, - uint32_t scancode, - uint32_t state) -{ - _GLFWwindow* window = _glfw.wl.keyboardFocus; - if (!window) - return; - - const int key = translateKey(scancode); - const int action = - state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE; - - _glfw.wl.serial = serial; - _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers); - - struct itimerspec timer = {}; - - if (action == GLFW_PRESS) - { - const GLFWbool shouldRepeat = _glfwInputTextWayland(window, scancode); - - if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0) - { - _glfw.wl.keyboardLastKey = key; - _glfw.wl.keyboardLastScancode = scancode; - if (_glfw.wl.keyboardRepeatRate > 1) - timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyboardRepeatRate; - else - timer.it_interval.tv_sec = 1; - - timer.it_value.tv_sec = _glfw.wl.keyboardRepeatDelay / 1000; - timer.it_value.tv_nsec = (_glfw.wl.keyboardRepeatDelay % 1000) * 1000000; - } - } - - timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); -} - -static void keyboardHandleModifiers(void* userData, - struct wl_keyboard* keyboard, - uint32_t serial, - uint32_t modsDepressed, - uint32_t modsLatched, - uint32_t modsLocked, - uint32_t group) -{ - _glfw.wl.serial = serial; - - if (!_glfw.wl.xkb.keymap) - return; - - xkb_state_update_mask(_glfw.wl.xkb.state, - modsDepressed, - modsLatched, - modsLocked, - 0, - 0, - group); - - _glfw.wl.xkb.modifiers = 0; - - struct - { - xkb_mod_index_t index; - unsigned int bit; - } modifiers[] = - { - { _glfw.wl.xkb.controlIndex, GLFW_MOD_CONTROL }, - { _glfw.wl.xkb.altIndex, GLFW_MOD_ALT }, - { _glfw.wl.xkb.shiftIndex, GLFW_MOD_SHIFT }, - { _glfw.wl.xkb.superIndex, GLFW_MOD_SUPER }, - { _glfw.wl.xkb.capsLockIndex, GLFW_MOD_CAPS_LOCK }, - { _glfw.wl.xkb.numLockIndex, GLFW_MOD_NUM_LOCK } - }; - - for (size_t i = 0; i < sizeof(modifiers) / sizeof(modifiers[0]); i++) - { - if (xkb_state_mod_index_is_active(_glfw.wl.xkb.state, - modifiers[i].index, - XKB_STATE_MODS_EFFECTIVE) == 1) - { - _glfw.wl.xkb.modifiers |= modifiers[i].bit; - } - } -} - -#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION -static void keyboardHandleRepeatInfo(void* userData, - struct wl_keyboard* keyboard, - int32_t rate, - int32_t delay) -{ - if (keyboard != _glfw.wl.keyboard) - return; - - _glfw.wl.keyboardRepeatRate = rate; - _glfw.wl.keyboardRepeatDelay = delay; -} -#endif - -static const struct wl_keyboard_listener keyboardListener = -{ - keyboardHandleKeymap, - keyboardHandleEnter, - keyboardHandleLeave, - keyboardHandleKey, - keyboardHandleModifiers, -#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION - keyboardHandleRepeatInfo, -#endif -}; - -static void seatHandleCapabilities(void* userData, - struct wl_seat* seat, - enum wl_seat_capability caps) -{ - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer) - { - _glfw.wl.pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL); - } - else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) - { - wl_pointer_destroy(_glfw.wl.pointer); - _glfw.wl.pointer = NULL; - } - - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard) - { - _glfw.wl.keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL); - } - else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) - { - wl_keyboard_destroy(_glfw.wl.keyboard); - _glfw.wl.keyboard = NULL; - } -} - -static void seatHandleName(void* userData, - struct wl_seat* seat, - const char* name) -{ -} - -static const struct wl_seat_listener seatListener = -{ - seatHandleCapabilities, - seatHandleName, -}; - -static void dataOfferHandleOffer(void* userData, - struct wl_data_offer* offer, - const char* mimeType) -{ - for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) - { - if (_glfw.wl.offers[i].offer == offer) - { - if (strcmp(mimeType, "text/plain;charset=utf-8") == 0) - _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE; - else if (strcmp(mimeType, "text/uri-list") == 0) - _glfw.wl.offers[i].text_uri_list = GLFW_TRUE; - - break; - } - } -} - -static const struct wl_data_offer_listener dataOfferListener = -{ - dataOfferHandleOffer -}; - -static void dataDeviceHandleDataOffer(void* userData, - struct wl_data_device* device, - struct wl_data_offer* offer) -{ - _GLFWofferWayland* offers = - realloc(_glfw.wl.offers, _glfw.wl.offerCount + 1); - if (!offers) - { - _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); - return; - } - - _glfw.wl.offers = offers; - _glfw.wl.offerCount++; - - _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer }; - wl_data_offer_add_listener(offer, &dataOfferListener, NULL); -} - -static void dataDeviceHandleEnter(void* userData, - struct wl_data_device* device, - uint32_t serial, - struct wl_surface* surface, - wl_fixed_t x, - wl_fixed_t y, - struct wl_data_offer* offer) -{ - if (_glfw.wl.dragOffer) - { - wl_data_offer_destroy(_glfw.wl.dragOffer); - _glfw.wl.dragOffer = NULL; - _glfw.wl.dragFocus = NULL; - } - - for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) - { - if (_glfw.wl.offers[i].offer == offer) - { - _GLFWwindow* window = NULL; - - if (surface) - window = wl_surface_get_user_data(surface); - - if (window && _glfw.wl.offers[i].text_uri_list) - { - _glfw.wl.dragOffer = offer; - _glfw.wl.dragFocus = window; - _glfw.wl.dragSerial = serial; - } - - _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; - _glfw.wl.offerCount--; - break; - } - } - - if (_glfw.wl.dragOffer) - wl_data_offer_accept(offer, serial, "text/uri-list"); - else - { - wl_data_offer_accept(offer, serial, NULL); - wl_data_offer_destroy(offer); - } -} - -static void dataDeviceHandleLeave(void* userData, - struct wl_data_device* device) -{ - if (_glfw.wl.dragOffer) - { - wl_data_offer_destroy(_glfw.wl.dragOffer); - _glfw.wl.dragOffer = NULL; - _glfw.wl.dragFocus = NULL; - } -} - -static void dataDeviceHandleMotion(void* userData, - struct wl_data_device* device, - uint32_t time, - wl_fixed_t x, - wl_fixed_t y) -{ -} - -static void dataDeviceHandleDrop(void* userData, - struct wl_data_device* device) -{ - if (!_glfw.wl.dragOffer) - return; - - char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list"); - if (string) - { - int count; - char** paths = _glfwParseUriList(string, &count); - if (paths) - _glfwInputDrop(_glfw.wl.dragFocus, count, (const char**) paths); - - for (int i = 0; i < count; i++) - free(paths[i]); - - free(paths); - } - - free(string); -} - -static void dataDeviceHandleSelection(void* userData, - struct wl_data_device* device, - struct wl_data_offer* offer) -{ - if (_glfw.wl.selectionOffer) - { - wl_data_offer_destroy(_glfw.wl.selectionOffer); - _glfw.wl.selectionOffer = NULL; - } - - for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) - { - if (_glfw.wl.offers[i].offer == offer) - { - if (_glfw.wl.offers[i].text_plain_utf8) - _glfw.wl.selectionOffer = offer; - else - wl_data_offer_destroy(offer); - - _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; - _glfw.wl.offerCount--; - break; - } - } -} - -const struct wl_data_device_listener dataDeviceListener = -{ - dataDeviceHandleDataOffer, - dataDeviceHandleEnter, - dataDeviceHandleLeave, - dataDeviceHandleMotion, - dataDeviceHandleDrop, - dataDeviceHandleSelection, -}; - -// Translates a GLFW standard cursor to a theme cursor name -// -static char *translateCursorShape(int shape) -{ - switch (shape) - { - case GLFW_ARROW_CURSOR: - return "left_ptr"; - case GLFW_IBEAM_CURSOR: - return "xterm"; - case GLFW_CROSSHAIR_CURSOR: - return "crosshair"; - case GLFW_HAND_CURSOR: - return "hand2"; - case GLFW_HRESIZE_CURSOR: - return "sb_h_double_arrow"; - case GLFW_VRESIZE_CURSOR: - return "sb_v_double_arrow"; - } - return NULL; -} - -void _glfwAddSeatListenerWayland(struct wl_seat* seat) -{ - wl_seat_add_listener(seat, &seatListener, NULL); -} - -void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device) -{ - wl_data_device_add_listener(device, &dataDeviceListener, NULL); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - if (!createNativeSurface(window, wndconfig, fbconfig)) - return GLFW_FALSE; - - if (ctxconfig->client != GLFW_NO_API) - { - if (ctxconfig->source == GLFW_EGL_CONTEXT_API || - ctxconfig->source == GLFW_NATIVE_CONTEXT_API) - { - window->wl.egl.window = wl_egl_window_create(window->wl.surface, - wndconfig->width, - wndconfig->height); - if (!window->wl.egl.window) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create EGL window"); - return GLFW_FALSE; - } - - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) - { - if (!_glfwInitOSMesa()) - return GLFW_FALSE; - if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - - if (!_glfwRefreshContextAttribs(window, ctxconfig)) - return GLFW_FALSE; - } - - if (window->monitor || wndconfig->visible) - { - if (!createShellObjects(window)) - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -void _glfwPlatformDestroyWindow(_GLFWwindow* window) -{ - if (window == _glfw.wl.pointerFocus) - { - _glfw.wl.pointerFocus = NULL; - _glfwInputCursorEnter(window, GLFW_FALSE); - } - if (window == _glfw.wl.keyboardFocus) - { - _glfw.wl.keyboardFocus = NULL; - _glfwInputWindowFocus(window, GLFW_FALSE); - } - - if (window->wl.idleInhibitor) - zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); - - if (window->context.destroy) - window->context.destroy(window); - - destroyShellObjects(window); - - if (window->wl.decorations.buffer) - wl_buffer_destroy(window->wl.decorations.buffer); - - if (window->wl.egl.window) - wl_egl_window_destroy(window->wl.egl.window); - - if (window->wl.surface) - wl_surface_destroy(window->wl.surface); - - free(window->wl.title); - free(window->wl.monitors); -} - -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) -{ - if (window->wl.title) - free(window->wl.title); - window->wl.title = _glfw_strdup(title); - if (window->wl.xdg.toplevel) - xdg_toplevel_set_title(window->wl.xdg.toplevel, title); -} - -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Setting window icon not supported"); -} - -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) -{ - // A Wayland client is not aware of its position, so just warn and leave it - // as (0, 0) - - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Window position retrieval not supported"); -} - -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) -{ - // A Wayland client can not set its position, so just warn - - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Window position setting not supported"); -} - -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) -{ - if (width) - *width = window->wl.width; - if (height) - *height = window->wl.height; -} - -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) -{ - if (window->monitor) - { - // Video mode setting is not available on Wayland - } - else - { - window->wl.width = width; - window->wl.height = height; - resizeWindow(window); - } -} - -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) -{ - if (window->wl.xdg.toplevel) - { - if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) - minwidth = minheight = 0; - else - { - if (window->wl.decorations.top.surface) - { - minwidth += GLFW_BORDER_SIZE * 2; - minheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE; - } - } - - if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) - maxwidth = maxheight = 0; - else - { - if (window->wl.decorations.top.surface) - { - maxwidth += GLFW_BORDER_SIZE * 2; - maxheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE; - } - } - - xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight); - xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight); - wl_surface_commit(window->wl.surface); - } -} - -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, - int numer, int denom) -{ - if (window->wl.maximized || window->wl.fullscreen) - return; - - if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE) - { - const float aspectRatio = (float) window->wl.width / (float) window->wl.height; - const float targetRatio = (float) numer / (float) denom; - if (aspectRatio < targetRatio) - window->wl.height = window->wl.width / targetRatio; - else if (aspectRatio > targetRatio) - window->wl.width = window->wl.height * targetRatio; - - resizeWindow(window); - } -} - -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, - int* width, int* height) -{ - _glfwPlatformGetWindowSize(window, width, height); - if (width) - *width *= window->wl.scale; - if (height) - *height *= window->wl.scale; -} - -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) -{ - if (window->decorated && !window->monitor && window->wl.decorations.top.surface) - { - if (top) - *top = GLFW_CAPTION_HEIGHT; - if (left) - *left = GLFW_BORDER_SIZE; - if (right) - *right = GLFW_BORDER_SIZE; - if (bottom) - *bottom = GLFW_BORDER_SIZE; - } -} - -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) -{ - if (xscale) - *xscale = (float) window->wl.scale; - if (yscale) - *yscale = (float) window->wl.scale; -} - -void _glfwPlatformIconifyWindow(_GLFWwindow* window) -{ - if (window->wl.xdg.toplevel) - xdg_toplevel_set_minimized(window->wl.xdg.toplevel); -} - -void _glfwPlatformRestoreWindow(_GLFWwindow* window) -{ - if (window->monitor) - { - // There is no way to unset minimized, or even to know if we are - // minimized, so there is nothing to do here. - } - else - { - // We assume we are not minimized and act only on maximization - - if (window->wl.maximized) - { - if (window->wl.xdg.toplevel) - xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); - else - window->wl.maximized = GLFW_FALSE; - } - } -} - -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) -{ - if (window->wl.xdg.toplevel) - xdg_toplevel_set_maximized(window->wl.xdg.toplevel); - else - window->wl.maximized = GLFW_TRUE; -} - -void _glfwPlatformShowWindow(_GLFWwindow* window) -{ - if (!window->wl.xdg.toplevel) - { - // NOTE: The XDG/shell surface is created here so command-line applications - // with off-screen windows do not appear in for example the Unity dock - createShellObjects(window); - } -} - -void _glfwPlatformHideWindow(_GLFWwindow* window) -{ - if (window->wl.visible) - { - window->wl.visible = GLFW_FALSE; - destroyShellObjects(window); - - wl_surface_attach(window->wl.surface, NULL, 0, 0); - wl_surface_commit(window->wl.surface); - } -} - -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) -{ - // TODO - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Window attention request not implemented yet"); -} - -void _glfwPlatformFocusWindow(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Focusing a window requires user interaction"); -} - -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) -{ - if (window->monitor == monitor) - { - if (!monitor) - _glfwPlatformSetWindowSize(window, width, height); - - return; - } - - if (window->monitor) - releaseMonitor(window); - - _glfwInputWindowMonitor(window, monitor); - - if (window->monitor) - acquireMonitor(window); - else - _glfwPlatformSetWindowSize(window, width, height); -} - -int _glfwPlatformWindowFocused(_GLFWwindow* window) -{ - return _glfw.wl.keyboardFocus == window; -} - -int _glfwPlatformWindowIconified(_GLFWwindow* window) -{ - // xdg-shell doesn’t give any way to request whether a surface is iconified - return GLFW_FALSE; -} - -int _glfwPlatformWindowVisible(_GLFWwindow* window) -{ - return window->wl.visible; -} - -int _glfwPlatformWindowMaximized(_GLFWwindow* window) -{ - return window->wl.maximized; -} - -int _glfwPlatformWindowHovered(_GLFWwindow* window) -{ - return window->wl.hovered; -} - -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) -{ - return window->wl.transparent; -} - -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) -{ - // TODO - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Window attribute setting not implemented yet"); -} - -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) -{ - if (window->wl.xdg.decoration) - { - uint32_t mode; - - if (enabled) - mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; - else - mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; - - zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode); - } - else - { - if (enabled) - createFallbackDecorations(window); - else - destroyFallbackDecorations(window); - } -} - -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) -{ - // TODO - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Window attribute setting not implemented yet"); -} - -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) -{ - return 1.f; -} - -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) -{ -} - -void _glfwPlatformSetRawMouseMotion(_GLFWwindow* window, GLFWbool enabled) -{ - // This is handled in relativePointerHandleRelativeMotion -} - -GLFWbool _glfwPlatformRawMouseMotionSupported(void) -{ - return GLFW_TRUE; -} - -void _glfwPlatformPollEvents(void) -{ - double timeout = 0.0; - handleEvents(&timeout); -} - -void _glfwPlatformWaitEvents(void) -{ - handleEvents(NULL); -} - -void _glfwPlatformWaitEventsTimeout(double timeout) -{ - handleEvents(&timeout); -} - -void _glfwPlatformPostEmptyEvent(void) -{ - wl_display_sync(_glfw.wl.display); - flushDisplay(); -} - -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) -{ - if (xpos) - *xpos = window->wl.cursorPosX; - if (ypos) - *ypos = window->wl.cursorPosY; -} - -static GLFWbool isPointerLocked(_GLFWwindow* window); - -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) -{ - if (isPointerLocked(window)) - { - zwp_locked_pointer_v1_set_cursor_position_hint( - window->wl.pointerLock.lockedPointer, - wl_fixed_from_double(x), wl_fixed_from_double(y)); - } -} - -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) -{ - _glfwPlatformSetCursor(window, window->wl.currentCursor); -} - -const char* _glfwPlatformGetScancodeName(int scancode) -{ - if (scancode < 0 || scancode > 255 || - _glfw.wl.keycodes[scancode] == GLFW_KEY_UNKNOWN) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Wayland: Invalid scancode %i", - scancode); - return NULL; - } - - const int key = _glfw.wl.keycodes[scancode]; - const xkb_keycode_t keycode = scancode + 8; - const xkb_layout_index_t layout = - xkb_state_key_get_layout(_glfw.wl.xkb.state, keycode); - if (layout == XKB_LAYOUT_INVALID) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to retrieve layout for key name"); - return NULL; - } - - const xkb_keysym_t* keysyms = NULL; - xkb_keymap_key_get_syms_by_level(_glfw.wl.xkb.keymap, - keycode, - layout, - 0, - &keysyms); - if (keysyms == NULL) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to retrieve keysym for key name"); - return NULL; - } - - const uint32_t codepoint = _glfwKeySym2Unicode(keysyms[0]); - if (codepoint == GLFW_INVALID_CODEPOINT) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to retrieve codepoint for key name"); - return NULL; - } - - const size_t count = _glfwEncodeUTF8(_glfw.wl.keynames[key], codepoint); - if (count == 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to encode codepoint for key name"); - return NULL; - } - - _glfw.wl.keynames[key][count] = '\0'; - return _glfw.wl.keynames[key]; -} - -int _glfwPlatformGetKeyScancode(int key) -{ - return _glfw.wl.scancodes[key]; -} - -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) -{ - cursor->wl.buffer = createShmBuffer(image); - if (!cursor->wl.buffer) - return GLFW_FALSE; - - cursor->wl.width = image->width; - cursor->wl.height = image->height; - cursor->wl.xhot = xhot; - cursor->wl.yhot = yhot; - return GLFW_TRUE; -} - -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) -{ - struct wl_cursor* standardCursor; - - standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, - translateCursorShape(shape)); - if (!standardCursor) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Standard cursor \"%s\" not found", - translateCursorShape(shape)); - return GLFW_FALSE; - } - - cursor->wl.cursor = standardCursor; - cursor->wl.currentImage = 0; - - if (_glfw.wl.cursorThemeHiDPI) - { - standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, - translateCursorShape(shape)); - cursor->wl.cursorHiDPI = standardCursor; - } - - return GLFW_TRUE; -} - -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) -{ - // If it's a standard cursor we don't need to do anything here - if (cursor->wl.cursor) - return; - - if (cursor->wl.buffer) - wl_buffer_destroy(cursor->wl.buffer); -} - -static void relativePointerHandleRelativeMotion(void* userData, - struct zwp_relative_pointer_v1* pointer, - uint32_t timeHi, - uint32_t timeLo, - wl_fixed_t dx, - wl_fixed_t dy, - wl_fixed_t dxUnaccel, - wl_fixed_t dyUnaccel) -{ - _GLFWwindow* window = userData; - double xpos = window->virtualCursorPosX; - double ypos = window->virtualCursorPosY; - - if (window->cursorMode != GLFW_CURSOR_DISABLED) - return; - - if (window->rawMouseMotion) - { - xpos += wl_fixed_to_double(dxUnaccel); - ypos += wl_fixed_to_double(dyUnaccel); - } - else - { - xpos += wl_fixed_to_double(dx); - ypos += wl_fixed_to_double(dy); - } - - _glfwInputCursorPos(window, xpos, ypos); -} - -static const struct zwp_relative_pointer_v1_listener relativePointerListener = -{ - relativePointerHandleRelativeMotion -}; - -static void lockedPointerHandleLocked(void* userData, - struct zwp_locked_pointer_v1* lockedPointer) -{ -} - -static void unlockPointer(_GLFWwindow* window) -{ - struct zwp_relative_pointer_v1* relativePointer = - window->wl.pointerLock.relativePointer; - struct zwp_locked_pointer_v1* lockedPointer = - window->wl.pointerLock.lockedPointer; - - zwp_relative_pointer_v1_destroy(relativePointer); - zwp_locked_pointer_v1_destroy(lockedPointer); - - window->wl.pointerLock.relativePointer = NULL; - window->wl.pointerLock.lockedPointer = NULL; -} - -static void lockPointer(_GLFWwindow* window); - -static void lockedPointerHandleUnlocked(void* userData, - struct zwp_locked_pointer_v1* lockedPointer) -{ -} - -static const struct zwp_locked_pointer_v1_listener lockedPointerListener = -{ - lockedPointerHandleLocked, - lockedPointerHandleUnlocked -}; - -static void lockPointer(_GLFWwindow* window) -{ - struct zwp_relative_pointer_v1* relativePointer; - struct zwp_locked_pointer_v1* lockedPointer; - - if (!_glfw.wl.relativePointerManager) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: no relative pointer manager"); - return; - } - - relativePointer = - zwp_relative_pointer_manager_v1_get_relative_pointer( - _glfw.wl.relativePointerManager, - _glfw.wl.pointer); - zwp_relative_pointer_v1_add_listener(relativePointer, - &relativePointerListener, - window); - - lockedPointer = - zwp_pointer_constraints_v1_lock_pointer( - _glfw.wl.pointerConstraints, - window->wl.surface, - _glfw.wl.pointer, - NULL, - ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); - zwp_locked_pointer_v1_add_listener(lockedPointer, - &lockedPointerListener, - window); - - window->wl.pointerLock.relativePointer = relativePointer; - window->wl.pointerLock.lockedPointer = lockedPointer; - - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, - NULL, 0, 0); -} - -static GLFWbool isPointerLocked(_GLFWwindow* window) -{ - return window->wl.pointerLock.lockedPointer != NULL; -} - -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) -{ - struct wl_cursor* defaultCursor; - struct wl_cursor* defaultCursorHiDPI = NULL; - - if (!_glfw.wl.pointer) - return; - - window->wl.currentCursor = cursor; - - // If we're not in the correct window just save the cursor - // the next time the pointer enters the window the cursor will change - if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow) - return; - - // Unlock possible pointer lock if no longer disabled. - if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window)) - unlockPointer(window); - - if (window->cursorMode == GLFW_CURSOR_NORMAL) - { - if (cursor) - setCursorImage(window, &cursor->wl); - else - { - defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, - "left_ptr"); - if (!defaultCursor) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Standard cursor not found"); - return; - } - if (_glfw.wl.cursorThemeHiDPI) - defaultCursorHiDPI = - wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, - "left_ptr"); - _GLFWcursorWayland cursorWayland = { - defaultCursor, - defaultCursorHiDPI, - NULL, - 0, 0, - 0, 0, - 0 - }; - setCursorImage(window, &cursorWayland); - } - } - else if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - if (!isPointerLocked(window)) - lockPointer(window); - } - else if (window->cursorMode == GLFW_CURSOR_HIDDEN) - { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, NULL, 0, 0); - } -} - -static void dataSourceHandleTarget(void* userData, - struct wl_data_source* source, - const char* mimeType) -{ - if (_glfw.wl.selectionSource != source) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Unknown clipboard data source"); - return; - } -} - -static void dataSourceHandleSend(void* userData, - struct wl_data_source* source, - const char* mimeType, - int fd) -{ - // Ignore it if this is an outdated or invalid request - if (_glfw.wl.selectionSource != source || - strcmp(mimeType, "text/plain;charset=utf-8") != 0) - { - close(fd); - return; - } - - char* string = _glfw.wl.clipboardString; - size_t length = strlen(string); - - while (length > 0) - { - const ssize_t result = write(fd, string, length); - if (result == -1) - { - if (errno == EINTR) - continue; - - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Error while writing the clipboard: %s", - strerror(errno)); - break; - } - - length -= result; - string += result; - } - - close(fd); -} - -static void dataSourceHandleCancelled(void* userData, - struct wl_data_source* source) -{ - wl_data_source_destroy(source); - - if (_glfw.wl.selectionSource != source) - return; - - _glfw.wl.selectionSource = NULL; -} - -static const struct wl_data_source_listener dataSourceListener = -{ - dataSourceHandleTarget, - dataSourceHandleSend, - dataSourceHandleCancelled, -}; - -void _glfwPlatformSetClipboardString(const char* string) -{ - if (_glfw.wl.selectionSource) - { - wl_data_source_destroy(_glfw.wl.selectionSource); - _glfw.wl.selectionSource = NULL; - } - - char* copy = _glfw_strdup(string); - if (!copy) - { - _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); - return; - } - - free(_glfw.wl.clipboardString); - _glfw.wl.clipboardString = copy; - - _glfw.wl.selectionSource = - wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); - if (!_glfw.wl.selectionSource) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create clipboard data source"); - return; - } - wl_data_source_add_listener(_glfw.wl.selectionSource, - &dataSourceListener, - NULL); - wl_data_source_offer(_glfw.wl.selectionSource, "text/plain;charset=utf-8"); - wl_data_device_set_selection(_glfw.wl.dataDevice, - _glfw.wl.selectionSource, - _glfw.wl.serial); -} - -const char* _glfwPlatformGetClipboardString(void) -{ - if (!_glfw.wl.selectionOffer) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "Wayland: No clipboard data available"); - return NULL; - } - - if (_glfw.wl.selectionSource) - return _glfw.wl.clipboardString; - - free(_glfw.wl.clipboardString); - _glfw.wl.clipboardString = - readDataOfferAsString(_glfw.wl.selectionOffer, "text/plain;charset=utf-8"); - return _glfw.wl.clipboardString; -} - -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) -{ - if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface) - return; - - extensions[0] = "VK_KHR_surface"; - extensions[1] = "VK_KHR_wayland_surface"; -} - -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) -{ - PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR - vkGetPhysicalDeviceWaylandPresentationSupportKHR = - (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); - if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); - return VK_NULL_HANDLE; - } - - return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device, - queuefamily, - _glfw.wl.display); -} - -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) -{ - VkResult err; - VkWaylandSurfaceCreateInfoKHR sci; - PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR; - - vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR) - vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR"); - if (!vkCreateWaylandSurfaceKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - memset(&sci, 0, sizeof(sci)); - sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - sci.display = _glfw.wl.display; - sci.surface = window->wl.surface; - - err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface); - if (err) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create Vulkan surface: %s", - _glfwGetVulkanResultString(err)); - } - - return err; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI struct wl_display* glfwGetWaylandDisplay(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfw.wl.display; -} - -GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return window->wl.surface; -} - diff --git a/libs/zglfw/libs/glfw/src/x11_init.c b/libs/zglfw/libs/glfw/src/x11_init.c deleted file mode 100644 index 6049904ae..000000000 --- a/libs/zglfw/libs/glfw/src/x11_init.c +++ /dev/null @@ -1,1274 +0,0 @@ -//======================================================================== -// GLFW 3.3 X11 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -// Translate the X11 KeySyms for a key to a GLFW key code -// NOTE: This is only used as a fallback, in case the XKB method fails -// It is layout-dependent and will fail partially on most non-US layouts -// -static int translateKeySyms(const KeySym* keysyms, int width) -{ - if (width > 1) - { - switch (keysyms[1]) - { - case XK_KP_0: return GLFW_KEY_KP_0; - case XK_KP_1: return GLFW_KEY_KP_1; - case XK_KP_2: return GLFW_KEY_KP_2; - case XK_KP_3: return GLFW_KEY_KP_3; - case XK_KP_4: return GLFW_KEY_KP_4; - case XK_KP_5: return GLFW_KEY_KP_5; - case XK_KP_6: return GLFW_KEY_KP_6; - case XK_KP_7: return GLFW_KEY_KP_7; - case XK_KP_8: return GLFW_KEY_KP_8; - case XK_KP_9: return GLFW_KEY_KP_9; - case XK_KP_Separator: - case XK_KP_Decimal: return GLFW_KEY_KP_DECIMAL; - case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; - case XK_KP_Enter: return GLFW_KEY_KP_ENTER; - default: break; - } - } - - switch (keysyms[0]) - { - case XK_Escape: return GLFW_KEY_ESCAPE; - case XK_Tab: return GLFW_KEY_TAB; - case XK_Shift_L: return GLFW_KEY_LEFT_SHIFT; - case XK_Shift_R: return GLFW_KEY_RIGHT_SHIFT; - case XK_Control_L: return GLFW_KEY_LEFT_CONTROL; - case XK_Control_R: return GLFW_KEY_RIGHT_CONTROL; - case XK_Meta_L: - case XK_Alt_L: return GLFW_KEY_LEFT_ALT; - case XK_Mode_switch: // Mapped to Alt_R on many keyboards - case XK_ISO_Level3_Shift: // AltGr on at least some machines - case XK_Meta_R: - case XK_Alt_R: return GLFW_KEY_RIGHT_ALT; - case XK_Super_L: return GLFW_KEY_LEFT_SUPER; - case XK_Super_R: return GLFW_KEY_RIGHT_SUPER; - case XK_Menu: return GLFW_KEY_MENU; - case XK_Num_Lock: return GLFW_KEY_NUM_LOCK; - case XK_Caps_Lock: return GLFW_KEY_CAPS_LOCK; - case XK_Print: return GLFW_KEY_PRINT_SCREEN; - case XK_Scroll_Lock: return GLFW_KEY_SCROLL_LOCK; - case XK_Pause: return GLFW_KEY_PAUSE; - case XK_Delete: return GLFW_KEY_DELETE; - case XK_BackSpace: return GLFW_KEY_BACKSPACE; - case XK_Return: return GLFW_KEY_ENTER; - case XK_Home: return GLFW_KEY_HOME; - case XK_End: return GLFW_KEY_END; - case XK_Page_Up: return GLFW_KEY_PAGE_UP; - case XK_Page_Down: return GLFW_KEY_PAGE_DOWN; - case XK_Insert: return GLFW_KEY_INSERT; - case XK_Left: return GLFW_KEY_LEFT; - case XK_Right: return GLFW_KEY_RIGHT; - case XK_Down: return GLFW_KEY_DOWN; - case XK_Up: return GLFW_KEY_UP; - case XK_F1: return GLFW_KEY_F1; - case XK_F2: return GLFW_KEY_F2; - case XK_F3: return GLFW_KEY_F3; - case XK_F4: return GLFW_KEY_F4; - case XK_F5: return GLFW_KEY_F5; - case XK_F6: return GLFW_KEY_F6; - case XK_F7: return GLFW_KEY_F7; - case XK_F8: return GLFW_KEY_F8; - case XK_F9: return GLFW_KEY_F9; - case XK_F10: return GLFW_KEY_F10; - case XK_F11: return GLFW_KEY_F11; - case XK_F12: return GLFW_KEY_F12; - case XK_F13: return GLFW_KEY_F13; - case XK_F14: return GLFW_KEY_F14; - case XK_F15: return GLFW_KEY_F15; - case XK_F16: return GLFW_KEY_F16; - case XK_F17: return GLFW_KEY_F17; - case XK_F18: return GLFW_KEY_F18; - case XK_F19: return GLFW_KEY_F19; - case XK_F20: return GLFW_KEY_F20; - case XK_F21: return GLFW_KEY_F21; - case XK_F22: return GLFW_KEY_F22; - case XK_F23: return GLFW_KEY_F23; - case XK_F24: return GLFW_KEY_F24; - case XK_F25: return GLFW_KEY_F25; - - // Numeric keypad - case XK_KP_Divide: return GLFW_KEY_KP_DIVIDE; - case XK_KP_Multiply: return GLFW_KEY_KP_MULTIPLY; - case XK_KP_Subtract: return GLFW_KEY_KP_SUBTRACT; - case XK_KP_Add: return GLFW_KEY_KP_ADD; - - // These should have been detected in secondary keysym test above! - case XK_KP_Insert: return GLFW_KEY_KP_0; - case XK_KP_End: return GLFW_KEY_KP_1; - case XK_KP_Down: return GLFW_KEY_KP_2; - case XK_KP_Page_Down: return GLFW_KEY_KP_3; - case XK_KP_Left: return GLFW_KEY_KP_4; - case XK_KP_Right: return GLFW_KEY_KP_6; - case XK_KP_Home: return GLFW_KEY_KP_7; - case XK_KP_Up: return GLFW_KEY_KP_8; - case XK_KP_Page_Up: return GLFW_KEY_KP_9; - case XK_KP_Delete: return GLFW_KEY_KP_DECIMAL; - case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; - case XK_KP_Enter: return GLFW_KEY_KP_ENTER; - - // Last resort: Check for printable keys (should not happen if the XKB - // extension is available). This will give a layout dependent mapping - // (which is wrong, and we may miss some keys, especially on non-US - // keyboards), but it's better than nothing... - case XK_a: return GLFW_KEY_A; - case XK_b: return GLFW_KEY_B; - case XK_c: return GLFW_KEY_C; - case XK_d: return GLFW_KEY_D; - case XK_e: return GLFW_KEY_E; - case XK_f: return GLFW_KEY_F; - case XK_g: return GLFW_KEY_G; - case XK_h: return GLFW_KEY_H; - case XK_i: return GLFW_KEY_I; - case XK_j: return GLFW_KEY_J; - case XK_k: return GLFW_KEY_K; - case XK_l: return GLFW_KEY_L; - case XK_m: return GLFW_KEY_M; - case XK_n: return GLFW_KEY_N; - case XK_o: return GLFW_KEY_O; - case XK_p: return GLFW_KEY_P; - case XK_q: return GLFW_KEY_Q; - case XK_r: return GLFW_KEY_R; - case XK_s: return GLFW_KEY_S; - case XK_t: return GLFW_KEY_T; - case XK_u: return GLFW_KEY_U; - case XK_v: return GLFW_KEY_V; - case XK_w: return GLFW_KEY_W; - case XK_x: return GLFW_KEY_X; - case XK_y: return GLFW_KEY_Y; - case XK_z: return GLFW_KEY_Z; - case XK_1: return GLFW_KEY_1; - case XK_2: return GLFW_KEY_2; - case XK_3: return GLFW_KEY_3; - case XK_4: return GLFW_KEY_4; - case XK_5: return GLFW_KEY_5; - case XK_6: return GLFW_KEY_6; - case XK_7: return GLFW_KEY_7; - case XK_8: return GLFW_KEY_8; - case XK_9: return GLFW_KEY_9; - case XK_0: return GLFW_KEY_0; - case XK_space: return GLFW_KEY_SPACE; - case XK_minus: return GLFW_KEY_MINUS; - case XK_equal: return GLFW_KEY_EQUAL; - case XK_bracketleft: return GLFW_KEY_LEFT_BRACKET; - case XK_bracketright: return GLFW_KEY_RIGHT_BRACKET; - case XK_backslash: return GLFW_KEY_BACKSLASH; - case XK_semicolon: return GLFW_KEY_SEMICOLON; - case XK_apostrophe: return GLFW_KEY_APOSTROPHE; - case XK_grave: return GLFW_KEY_GRAVE_ACCENT; - case XK_comma: return GLFW_KEY_COMMA; - case XK_period: return GLFW_KEY_PERIOD; - case XK_slash: return GLFW_KEY_SLASH; - case XK_less: return GLFW_KEY_WORLD_1; // At least in some layouts... - default: break; - } - - // No matching translation was found - return GLFW_KEY_UNKNOWN; -} - -// Create key code translation tables -// -static void createKeyTables(void) -{ - int scancode, scancodeMin, scancodeMax; - - memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes)); - memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes)); - - if (_glfw.x11.xkb.available) - { - // Use XKB to determine physical key locations independently of the - // current keyboard layout - - XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd); - XkbGetNames(_glfw.x11.display, XkbKeyNamesMask | XkbKeyAliasesMask, desc); - - scancodeMin = desc->min_key_code; - scancodeMax = desc->max_key_code; - - const struct - { - int key; - char* name; - } keymap[] = - { - { GLFW_KEY_GRAVE_ACCENT, "TLDE" }, - { GLFW_KEY_1, "AE01" }, - { GLFW_KEY_2, "AE02" }, - { GLFW_KEY_3, "AE03" }, - { GLFW_KEY_4, "AE04" }, - { GLFW_KEY_5, "AE05" }, - { GLFW_KEY_6, "AE06" }, - { GLFW_KEY_7, "AE07" }, - { GLFW_KEY_8, "AE08" }, - { GLFW_KEY_9, "AE09" }, - { GLFW_KEY_0, "AE10" }, - { GLFW_KEY_MINUS, "AE11" }, - { GLFW_KEY_EQUAL, "AE12" }, - { GLFW_KEY_Q, "AD01" }, - { GLFW_KEY_W, "AD02" }, - { GLFW_KEY_E, "AD03" }, - { GLFW_KEY_R, "AD04" }, - { GLFW_KEY_T, "AD05" }, - { GLFW_KEY_Y, "AD06" }, - { GLFW_KEY_U, "AD07" }, - { GLFW_KEY_I, "AD08" }, - { GLFW_KEY_O, "AD09" }, - { GLFW_KEY_P, "AD10" }, - { GLFW_KEY_LEFT_BRACKET, "AD11" }, - { GLFW_KEY_RIGHT_BRACKET, "AD12" }, - { GLFW_KEY_A, "AC01" }, - { GLFW_KEY_S, "AC02" }, - { GLFW_KEY_D, "AC03" }, - { GLFW_KEY_F, "AC04" }, - { GLFW_KEY_G, "AC05" }, - { GLFW_KEY_H, "AC06" }, - { GLFW_KEY_J, "AC07" }, - { GLFW_KEY_K, "AC08" }, - { GLFW_KEY_L, "AC09" }, - { GLFW_KEY_SEMICOLON, "AC10" }, - { GLFW_KEY_APOSTROPHE, "AC11" }, - { GLFW_KEY_Z, "AB01" }, - { GLFW_KEY_X, "AB02" }, - { GLFW_KEY_C, "AB03" }, - { GLFW_KEY_V, "AB04" }, - { GLFW_KEY_B, "AB05" }, - { GLFW_KEY_N, "AB06" }, - { GLFW_KEY_M, "AB07" }, - { GLFW_KEY_COMMA, "AB08" }, - { GLFW_KEY_PERIOD, "AB09" }, - { GLFW_KEY_SLASH, "AB10" }, - { GLFW_KEY_BACKSLASH, "BKSL" }, - { GLFW_KEY_WORLD_1, "LSGT" }, - { GLFW_KEY_SPACE, "SPCE" }, - { GLFW_KEY_ESCAPE, "ESC" }, - { GLFW_KEY_ENTER, "RTRN" }, - { GLFW_KEY_TAB, "TAB" }, - { GLFW_KEY_BACKSPACE, "BKSP" }, - { GLFW_KEY_INSERT, "INS" }, - { GLFW_KEY_DELETE, "DELE" }, - { GLFW_KEY_RIGHT, "RGHT" }, - { GLFW_KEY_LEFT, "LEFT" }, - { GLFW_KEY_DOWN, "DOWN" }, - { GLFW_KEY_UP, "UP" }, - { GLFW_KEY_PAGE_UP, "PGUP" }, - { GLFW_KEY_PAGE_DOWN, "PGDN" }, - { GLFW_KEY_HOME, "HOME" }, - { GLFW_KEY_END, "END" }, - { GLFW_KEY_CAPS_LOCK, "CAPS" }, - { GLFW_KEY_SCROLL_LOCK, "SCLK" }, - { GLFW_KEY_NUM_LOCK, "NMLK" }, - { GLFW_KEY_PRINT_SCREEN, "PRSC" }, - { GLFW_KEY_PAUSE, "PAUS" }, - { GLFW_KEY_F1, "FK01" }, - { GLFW_KEY_F2, "FK02" }, - { GLFW_KEY_F3, "FK03" }, - { GLFW_KEY_F4, "FK04" }, - { GLFW_KEY_F5, "FK05" }, - { GLFW_KEY_F6, "FK06" }, - { GLFW_KEY_F7, "FK07" }, - { GLFW_KEY_F8, "FK08" }, - { GLFW_KEY_F9, "FK09" }, - { GLFW_KEY_F10, "FK10" }, - { GLFW_KEY_F11, "FK11" }, - { GLFW_KEY_F12, "FK12" }, - { GLFW_KEY_F13, "FK13" }, - { GLFW_KEY_F14, "FK14" }, - { GLFW_KEY_F15, "FK15" }, - { GLFW_KEY_F16, "FK16" }, - { GLFW_KEY_F17, "FK17" }, - { GLFW_KEY_F18, "FK18" }, - { GLFW_KEY_F19, "FK19" }, - { GLFW_KEY_F20, "FK20" }, - { GLFW_KEY_F21, "FK21" }, - { GLFW_KEY_F22, "FK22" }, - { GLFW_KEY_F23, "FK23" }, - { GLFW_KEY_F24, "FK24" }, - { GLFW_KEY_F25, "FK25" }, - { GLFW_KEY_KP_0, "KP0" }, - { GLFW_KEY_KP_1, "KP1" }, - { GLFW_KEY_KP_2, "KP2" }, - { GLFW_KEY_KP_3, "KP3" }, - { GLFW_KEY_KP_4, "KP4" }, - { GLFW_KEY_KP_5, "KP5" }, - { GLFW_KEY_KP_6, "KP6" }, - { GLFW_KEY_KP_7, "KP7" }, - { GLFW_KEY_KP_8, "KP8" }, - { GLFW_KEY_KP_9, "KP9" }, - { GLFW_KEY_KP_DECIMAL, "KPDL" }, - { GLFW_KEY_KP_DIVIDE, "KPDV" }, - { GLFW_KEY_KP_MULTIPLY, "KPMU" }, - { GLFW_KEY_KP_SUBTRACT, "KPSU" }, - { GLFW_KEY_KP_ADD, "KPAD" }, - { GLFW_KEY_KP_ENTER, "KPEN" }, - { GLFW_KEY_KP_EQUAL, "KPEQ" }, - { GLFW_KEY_LEFT_SHIFT, "LFSH" }, - { GLFW_KEY_LEFT_CONTROL, "LCTL" }, - { GLFW_KEY_LEFT_ALT, "LALT" }, - { GLFW_KEY_LEFT_SUPER, "LWIN" }, - { GLFW_KEY_RIGHT_SHIFT, "RTSH" }, - { GLFW_KEY_RIGHT_CONTROL, "RCTL" }, - { GLFW_KEY_RIGHT_ALT, "RALT" }, - { GLFW_KEY_RIGHT_ALT, "LVL3" }, - { GLFW_KEY_RIGHT_ALT, "MDSW" }, - { GLFW_KEY_RIGHT_SUPER, "RWIN" }, - { GLFW_KEY_MENU, "MENU" } - }; - - // Find the X11 key code -> GLFW key code mapping - for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) - { - int key = GLFW_KEY_UNKNOWN; - - // Map the key name to a GLFW key code. Note: We use the US - // keyboard layout. Because function keys aren't mapped correctly - // when using traditional KeySym translations, they are mapped - // here instead. - for (int i = 0; i < sizeof(keymap) / sizeof(keymap[0]); i++) - { - if (strncmp(desc->names->keys[scancode].name, - keymap[i].name, - XkbKeyNameLength) == 0) - { - key = keymap[i].key; - break; - } - } - - // Fall back to key aliases in case the key name did not match - for (int i = 0; i < desc->names->num_key_aliases; i++) - { - if (key != GLFW_KEY_UNKNOWN) - break; - - if (strncmp(desc->names->key_aliases[i].real, - desc->names->keys[scancode].name, - XkbKeyNameLength) != 0) - { - continue; - } - - for (int j = 0; j < sizeof(keymap) / sizeof(keymap[0]); j++) - { - if (strncmp(desc->names->key_aliases[i].alias, - keymap[j].name, - XkbKeyNameLength) == 0) - { - key = keymap[j].key; - break; - } - } - } - - _glfw.x11.keycodes[scancode] = key; - } - - XkbFreeNames(desc, XkbKeyNamesMask, True); - XkbFreeKeyboard(desc, 0, True); - } - else - XDisplayKeycodes(_glfw.x11.display, &scancodeMin, &scancodeMax); - - int width; - KeySym* keysyms = XGetKeyboardMapping(_glfw.x11.display, - scancodeMin, - scancodeMax - scancodeMin + 1, - &width); - - for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) - { - // Translate the un-translated key codes using traditional X11 KeySym - // lookups - if (_glfw.x11.keycodes[scancode] < 0) - { - const size_t base = (scancode - scancodeMin) * width; - _glfw.x11.keycodes[scancode] = translateKeySyms(&keysyms[base], width); - } - - // Store the reverse translation for faster key name lookup - if (_glfw.x11.keycodes[scancode] > 0) - _glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode; - } - - XFree(keysyms); -} - -// Check whether the IM has a usable style -// -static GLFWbool hasUsableInputMethodStyle(void) -{ - GLFWbool found = GLFW_FALSE; - XIMStyles* styles = NULL; - - if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, NULL) != NULL) - return GLFW_FALSE; - - for (unsigned int i = 0; i < styles->count_styles; i++) - { - if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) - { - found = GLFW_TRUE; - break; - } - } - - XFree(styles); - return found; -} - -// Check whether the specified atom is supported -// -static Atom getAtomIfSupported(Atom* supportedAtoms, - unsigned long atomCount, - const char* atomName) -{ - const Atom atom = XInternAtom(_glfw.x11.display, atomName, False); - - for (unsigned long i = 0; i < atomCount; i++) - { - if (supportedAtoms[i] == atom) - return atom; - } - - return None; -} - -// Check whether the running window manager is EWMH-compliant -// -static void detectEWMH(void) -{ - // First we read the _NET_SUPPORTING_WM_CHECK property on the root window - - Window* windowFromRoot = NULL; - if (!_glfwGetWindowPropertyX11(_glfw.x11.root, - _glfw.x11.NET_SUPPORTING_WM_CHECK, - XA_WINDOW, - (unsigned char**) &windowFromRoot)) - { - return; - } - - _glfwGrabErrorHandlerX11(); - - // If it exists, it should be the XID of a top-level window - // Then we look for the same property on that window - - Window* windowFromChild = NULL; - if (!_glfwGetWindowPropertyX11(*windowFromRoot, - _glfw.x11.NET_SUPPORTING_WM_CHECK, - XA_WINDOW, - (unsigned char**) &windowFromChild)) - { - XFree(windowFromRoot); - return; - } - - _glfwReleaseErrorHandlerX11(); - - // If the property exists, it should contain the XID of the window - - if (*windowFromRoot != *windowFromChild) - { - XFree(windowFromRoot); - XFree(windowFromChild); - return; - } - - XFree(windowFromRoot); - XFree(windowFromChild); - - // We are now fairly sure that an EWMH-compliant WM is currently running - // We can now start querying the WM about what features it supports by - // looking in the _NET_SUPPORTED property on the root window - // It should contain a list of supported EWMH protocol and state atoms - - Atom* supportedAtoms = NULL; - const unsigned long atomCount = - _glfwGetWindowPropertyX11(_glfw.x11.root, - _glfw.x11.NET_SUPPORTED, - XA_ATOM, - (unsigned char**) &supportedAtoms); - - // See which of the atoms we support that are supported by the WM - - _glfw.x11.NET_WM_STATE = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE"); - _glfw.x11.NET_WM_STATE_ABOVE = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); - _glfw.x11.NET_WM_STATE_FULLSCREEN = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); - _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); - _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); - _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION"); - _glfw.x11.NET_WM_FULLSCREEN_MONITORS = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); - _glfw.x11.NET_WM_WINDOW_TYPE = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE"); - _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); - _glfw.x11.NET_WORKAREA = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_WORKAREA"); - _glfw.x11.NET_CURRENT_DESKTOP = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP"); - _glfw.x11.NET_ACTIVE_WINDOW = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); - _glfw.x11.NET_FRAME_EXTENTS = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS"); - _glfw.x11.NET_REQUEST_FRAME_EXTENTS = - getAtomIfSupported(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); - - if (supportedAtoms) - XFree(supportedAtoms); -} - -// Look for and initialize supported X11 extensions -// -static GLFWbool initExtensions(void) -{ -#if defined(__OpenBSD__) || defined(__NetBSD__) - _glfw.x11.vidmode.handle = _glfw_dlopen("libXxf86vm.so"); -#else - _glfw.x11.vidmode.handle = _glfw_dlopen("libXxf86vm.so.1"); -#endif - if (_glfw.x11.vidmode.handle) - { - _glfw.x11.vidmode.QueryExtension = (PFN_XF86VidModeQueryExtension) - _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension"); - _glfw.x11.vidmode.GetGammaRamp = (PFN_XF86VidModeGetGammaRamp) - _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp"); - _glfw.x11.vidmode.SetGammaRamp = (PFN_XF86VidModeSetGammaRamp) - _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp"); - _glfw.x11.vidmode.GetGammaRampSize = (PFN_XF86VidModeGetGammaRampSize) - _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize"); - - _glfw.x11.vidmode.available = - XF86VidModeQueryExtension(_glfw.x11.display, - &_glfw.x11.vidmode.eventBase, - &_glfw.x11.vidmode.errorBase); - } - -#if defined(__CYGWIN__) - _glfw.x11.xi.handle = _glfw_dlopen("libXi-6.so"); -#elif defined(__OpenBSD__) || defined(__NetBSD__) - _glfw.x11.xi.handle = _glfw_dlopen("libXi.so"); -#else - _glfw.x11.xi.handle = _glfw_dlopen("libXi.so.6"); -#endif - if (_glfw.x11.xi.handle) - { - _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) - _glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion"); - _glfw.x11.xi.SelectEvents = (PFN_XISelectEvents) - _glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents"); - - if (XQueryExtension(_glfw.x11.display, - "XInputExtension", - &_glfw.x11.xi.majorOpcode, - &_glfw.x11.xi.eventBase, - &_glfw.x11.xi.errorBase)) - { - _glfw.x11.xi.major = 2; - _glfw.x11.xi.minor = 0; - - if (XIQueryVersion(_glfw.x11.display, - &_glfw.x11.xi.major, - &_glfw.x11.xi.minor) == Success) - { - _glfw.x11.xi.available = GLFW_TRUE; - } - } - } - -#if defined(__CYGWIN__) - _glfw.x11.randr.handle = _glfw_dlopen("libXrandr-2.so"); -#elif defined(__OpenBSD__) || defined(__NetBSD__) - _glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so"); -#else - _glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so.2"); -#endif - if (_glfw.x11.randr.handle) - { - _glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRAllocGamma"); - _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); - _glfw.x11.randr.FreeCrtcInfo = (PFN_XRRFreeCrtcInfo) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo"); - _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); - _glfw.x11.randr.FreeOutputInfo = (PFN_XRRFreeOutputInfo) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo"); - _glfw.x11.randr.FreeScreenResources = (PFN_XRRFreeScreenResources) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources"); - _glfw.x11.randr.GetCrtcGamma = (PFN_XRRGetCrtcGamma) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma"); - _glfw.x11.randr.GetCrtcGammaSize = (PFN_XRRGetCrtcGammaSize) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize"); - _glfw.x11.randr.GetCrtcInfo = (PFN_XRRGetCrtcInfo) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo"); - _glfw.x11.randr.GetOutputInfo = (PFN_XRRGetOutputInfo) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo"); - _glfw.x11.randr.GetOutputPrimary = (PFN_XRRGetOutputPrimary) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary"); - _glfw.x11.randr.GetScreenResourcesCurrent = (PFN_XRRGetScreenResourcesCurrent) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent"); - _glfw.x11.randr.QueryExtension = (PFN_XRRQueryExtension) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryExtension"); - _glfw.x11.randr.QueryVersion = (PFN_XRRQueryVersion) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryVersion"); - _glfw.x11.randr.SelectInput = (PFN_XRRSelectInput) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRSelectInput"); - _glfw.x11.randr.SetCrtcConfig = (PFN_XRRSetCrtcConfig) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig"); - _glfw.x11.randr.SetCrtcGamma = (PFN_XRRSetCrtcGamma) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma"); - _glfw.x11.randr.UpdateConfiguration = (PFN_XRRUpdateConfiguration) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration"); - - if (XRRQueryExtension(_glfw.x11.display, - &_glfw.x11.randr.eventBase, - &_glfw.x11.randr.errorBase)) - { - if (XRRQueryVersion(_glfw.x11.display, - &_glfw.x11.randr.major, - &_glfw.x11.randr.minor)) - { - // The GLFW RandR path requires at least version 1.3 - if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3) - _glfw.x11.randr.available = GLFW_TRUE; - } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to query RandR version"); - } - } - } - - if (_glfw.x11.randr.available) - { - XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, - _glfw.x11.root); - - if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0])) - { - // This is likely an older Nvidia driver with broken gamma support - // Flag it as useless and fall back to xf86vm gamma, if available - _glfw.x11.randr.gammaBroken = GLFW_TRUE; - } - - if (!sr->ncrtc) - { - // A system without CRTCs is likely a system with broken RandR - // Disable the RandR monitor path and fall back to core functions - _glfw.x11.randr.monitorBroken = GLFW_TRUE; - } - - XRRFreeScreenResources(sr); - } - - if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) - { - XRRSelectInput(_glfw.x11.display, _glfw.x11.root, - RROutputChangeNotifyMask); - } - -#if defined(__CYGWIN__) - _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor-1.so"); -#elif defined(__OpenBSD__) || defined(__NetBSD__) - _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so"); -#else - _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1"); -#endif - if (_glfw.x11.xcursor.handle) - { - _glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate) - _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate"); - _glfw.x11.xcursor.ImageDestroy = (PFN_XcursorImageDestroy) - _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); - _glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) - _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor"); - } - -#if defined(__CYGWIN__) - _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama-1.so"); -#elif defined(__OpenBSD__) || defined(__NetBSD__) - _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so"); -#else - _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so.1"); -#endif - if (_glfw.x11.xinerama.handle) - { - _glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive) - _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive"); - _glfw.x11.xinerama.QueryExtension = (PFN_XineramaQueryExtension) - _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension"); - _glfw.x11.xinerama.QueryScreens = (PFN_XineramaQueryScreens) - _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens"); - - if (XineramaQueryExtension(_glfw.x11.display, - &_glfw.x11.xinerama.major, - &_glfw.x11.xinerama.minor)) - { - if (XineramaIsActive(_glfw.x11.display)) - _glfw.x11.xinerama.available = GLFW_TRUE; - } - } - - _glfw.x11.xkb.major = 1; - _glfw.x11.xkb.minor = 0; - _glfw.x11.xkb.available = - XkbQueryExtension(_glfw.x11.display, - &_glfw.x11.xkb.majorOpcode, - &_glfw.x11.xkb.eventBase, - &_glfw.x11.xkb.errorBase, - &_glfw.x11.xkb.major, - &_glfw.x11.xkb.minor); - - if (_glfw.x11.xkb.available) - { - Bool supported; - - if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported)) - { - if (supported) - _glfw.x11.xkb.detectable = GLFW_TRUE; - } - - XkbStateRec state; - if (XkbGetState(_glfw.x11.display, XkbUseCoreKbd, &state) == Success) - _glfw.x11.xkb.group = (unsigned int)state.group; - - XkbSelectEventDetails(_glfw.x11.display, XkbUseCoreKbd, XkbStateNotify, - XkbGroupStateMask, XkbGroupStateMask); - } - -#if defined(__CYGWIN__) - _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb-1.so"); -#elif defined(__OpenBSD__) || defined(__NetBSD__) - _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so"); -#else - _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so.1"); -#endif - if (_glfw.x11.x11xcb.handle) - { - _glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection) - _glfw_dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); - } - -#if defined(__CYGWIN__) - _glfw.x11.xrender.handle = _glfw_dlopen("libXrender-1.so"); -#elif defined(__OpenBSD__) || defined(__NetBSD__) - _glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so"); -#else - _glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1"); -#endif - if (_glfw.x11.xrender.handle) - { - _glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension) - _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension"); - _glfw.x11.xrender.QueryVersion = (PFN_XRenderQueryVersion) - _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion"); - _glfw.x11.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat) - _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat"); - - if (XRenderQueryExtension(_glfw.x11.display, - &_glfw.x11.xrender.errorBase, - &_glfw.x11.xrender.eventBase)) - { - if (XRenderQueryVersion(_glfw.x11.display, - &_glfw.x11.xrender.major, - &_glfw.x11.xrender.minor)) - { - _glfw.x11.xrender.available = GLFW_TRUE; - } - } - } - - // Update the key code LUT - // FIXME: We should listen to XkbMapNotify events to track changes to - // the keyboard mapping. - createKeyTables(); - - // String format atoms - _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False); - _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False); - _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); - - // Custom selection property atom - _glfw.x11.GLFW_SELECTION = - XInternAtom(_glfw.x11.display, "GLFW_SELECTION", False); - - // ICCCM standard clipboard atoms - _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); - _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); - _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False); - _glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False); - _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); - - // Clipboard manager atoms - _glfw.x11.CLIPBOARD_MANAGER = - XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False); - _glfw.x11.SAVE_TARGETS = - XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False); - - // Xdnd (drag and drop) atoms - _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", False); - _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", False); - _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False); - _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False); - _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False); - _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False); - _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False); - _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False); - _glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False); - _glfw.x11.text_uri_list = XInternAtom(_glfw.x11.display, "text/uri-list", False); - - // ICCCM, EWMH and Motif window property atoms - // These can be set safely even without WM support - // The EWMH atoms that require WM support are handled in detectEWMH - _glfw.x11.WM_PROTOCOLS = - XInternAtom(_glfw.x11.display, "WM_PROTOCOLS", False); - _glfw.x11.WM_STATE = - XInternAtom(_glfw.x11.display, "WM_STATE", False); - _glfw.x11.WM_DELETE_WINDOW = - XInternAtom(_glfw.x11.display, "WM_DELETE_WINDOW", False); - _glfw.x11.NET_SUPPORTED = - XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", False); - _glfw.x11.NET_SUPPORTING_WM_CHECK = - XInternAtom(_glfw.x11.display, "_NET_SUPPORTING_WM_CHECK", False); - _glfw.x11.NET_WM_ICON = - XInternAtom(_glfw.x11.display, "_NET_WM_ICON", False); - _glfw.x11.NET_WM_PING = - XInternAtom(_glfw.x11.display, "_NET_WM_PING", False); - _glfw.x11.NET_WM_PID = - XInternAtom(_glfw.x11.display, "_NET_WM_PID", False); - _glfw.x11.NET_WM_NAME = - XInternAtom(_glfw.x11.display, "_NET_WM_NAME", False); - _glfw.x11.NET_WM_ICON_NAME = - XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False); - _glfw.x11.NET_WM_BYPASS_COMPOSITOR = - XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False); - _glfw.x11.NET_WM_WINDOW_OPACITY = - XInternAtom(_glfw.x11.display, "_NET_WM_WINDOW_OPACITY", False); - _glfw.x11.MOTIF_WM_HINTS = - XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); - - // The compositing manager selection name contains the screen number - { - char name[32]; - snprintf(name, sizeof(name), "_NET_WM_CM_S%u", _glfw.x11.screen); - _glfw.x11.NET_WM_CM_Sx = XInternAtom(_glfw.x11.display, name, False); - } - - // Detect whether an EWMH-conformant window manager is running - detectEWMH(); - - return GLFW_TRUE; -} - -// Retrieve system content scale via folklore heuristics -// -static void getSystemContentScale(float* xscale, float* yscale) -{ - // Start by assuming the default X11 DPI - // NOTE: Some desktop environments (KDE) may remove the Xft.dpi field when it - // would be set to 96, so assume that is the case if we cannot find it - float xdpi = 96.f, ydpi = 96.f; - - // NOTE: Basing the scale on Xft.dpi where available should provide the most - // consistent user experience (matches Qt, Gtk, etc), although not - // always the most accurate one - char* rms = XResourceManagerString(_glfw.x11.display); - if (rms) - { - XrmDatabase db = XrmGetStringDatabase(rms); - if (db) - { - XrmValue value; - char* type = NULL; - - if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) - { - if (type && strcmp(type, "String") == 0) - xdpi = ydpi = atof(value.addr); - } - - XrmDestroyDatabase(db); - } - } - - *xscale = xdpi / 96.f; - *yscale = ydpi / 96.f; -} - -// Create a blank cursor for hidden and disabled cursor modes -// -static Cursor createHiddenCursor(void) -{ - unsigned char pixels[16 * 16 * 4] = { 0 }; - GLFWimage image = { 16, 16, pixels }; - return _glfwCreateCursorX11(&image, 0, 0); -} - -// Create a helper window for IPC -// -static Window createHelperWindow(void) -{ - XSetWindowAttributes wa; - wa.event_mask = PropertyChangeMask; - - return XCreateWindow(_glfw.x11.display, _glfw.x11.root, - 0, 0, 1, 1, 0, 0, - InputOnly, - DefaultVisual(_glfw.x11.display, _glfw.x11.screen), - CWEventMask, &wa); -} - -// Create the pipe for empty events without assumuing the OS has pipe2(2) -// -static GLFWbool createEmptyEventPipe(void) -{ - if (pipe(_glfw.x11.emptyEventPipe) != 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to create empty event pipe: %s", - strerror(errno)); - return GLFW_FALSE; - } - - for (int i = 0; i < 2; i++) - { - const int sf = fcntl(_glfw.x11.emptyEventPipe[i], F_GETFL, 0); - const int df = fcntl(_glfw.x11.emptyEventPipe[i], F_GETFD, 0); - - if (sf == -1 || df == -1 || - fcntl(_glfw.x11.emptyEventPipe[i], F_SETFL, sf | O_NONBLOCK) == -1 || - fcntl(_glfw.x11.emptyEventPipe[i], F_SETFD, df | FD_CLOEXEC) == -1) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to set flags for empty event pipe: %s", - strerror(errno)); - return GLFW_FALSE; - } - } - - return GLFW_TRUE; -} - -// X error handler -// -static int errorHandler(Display *display, XErrorEvent* event) -{ - if (_glfw.x11.display != display) - return 0; - - _glfw.x11.errorCode = event->error_code; - return 0; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Sets the X error handler callback -// -void _glfwGrabErrorHandlerX11(void) -{ - assert(_glfw.x11.errorHandler == NULL); - _glfw.x11.errorCode = Success; - _glfw.x11.errorHandler = XSetErrorHandler(errorHandler); -} - -// Clears the X error handler callback -// -void _glfwReleaseErrorHandlerX11(void) -{ - // Synchronize to make sure all commands are processed - XSync(_glfw.x11.display, False); - XSetErrorHandler(_glfw.x11.errorHandler); - _glfw.x11.errorHandler = NULL; -} - -// Reports the specified error, appending information about the last X error -// -void _glfwInputErrorX11(int error, const char* message) -{ - char buffer[_GLFW_MESSAGE_SIZE]; - XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode, - buffer, sizeof(buffer)); - - _glfwInputError(error, "%s: %s", message, buffer); -} - -// Creates a native cursor object from the specified image and hotspot -// -Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) -{ - int i; - Cursor cursor; - - if (!_glfw.x11.xcursor.handle) - return None; - - XcursorImage* native = XcursorImageCreate(image->width, image->height); - if (native == NULL) - return None; - - native->xhot = xhot; - native->yhot = yhot; - - unsigned char* source = (unsigned char*) image->pixels; - XcursorPixel* target = native->pixels; - - for (i = 0; i < image->width * image->height; i++, target++, source += 4) - { - unsigned int alpha = source[3]; - - *target = (alpha << 24) | - ((unsigned char) ((source[0] * alpha) / 255) << 16) | - ((unsigned char) ((source[1] * alpha) / 255) << 8) | - ((unsigned char) ((source[2] * alpha) / 255) << 0); - } - - cursor = XcursorImageLoadCursor(_glfw.x11.display, native); - XcursorImageDestroy(native); - - return cursor; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformInit(void) -{ - // HACK: If the application has left the locale as "C" then both wide - // character text input and explicit UTF-8 input via XIM will break - // This sets the CTYPE part of the current locale from the environment - // in the hope that it is set to something more sane than "C" - if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0) - setlocale(LC_CTYPE, ""); - - XInitThreads(); - XrmInitialize(); - - _glfw.x11.display = XOpenDisplay(NULL); - if (!_glfw.x11.display) - { - const char* display = getenv("DISPLAY"); - if (display) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to open display %s", display); - } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: The DISPLAY environment variable is missing"); - } - - return GLFW_FALSE; - } - - _glfw.x11.screen = DefaultScreen(_glfw.x11.display); - _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); - _glfw.x11.context = XUniqueContext(); - - getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY); - - if (!createEmptyEventPipe()) - return GLFW_FALSE; - - if (!initExtensions()) - return GLFW_FALSE; - - _glfw.x11.helperWindowHandle = createHelperWindow(); - _glfw.x11.hiddenCursorHandle = createHiddenCursor(); - - if (XSupportsLocale()) - { - XSetLocaleModifiers(""); - - _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL); - if (_glfw.x11.im) - { - if (!hasUsableInputMethodStyle()) - { - XCloseIM(_glfw.x11.im); - _glfw.x11.im = NULL; - } - } - } - -#if defined(__linux__) - if (!_glfwInitJoysticksLinux()) - return GLFW_FALSE; -#endif - - _glfwInitTimerPOSIX(); - - _glfwPollMonitorsX11(); - return GLFW_TRUE; -} - -void _glfwPlatformTerminate(void) -{ - if (_glfw.x11.helperWindowHandle) - { - if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == - _glfw.x11.helperWindowHandle) - { - _glfwPushSelectionToManagerX11(); - } - - XDestroyWindow(_glfw.x11.display, _glfw.x11.helperWindowHandle); - _glfw.x11.helperWindowHandle = None; - } - - if (_glfw.x11.hiddenCursorHandle) - { - XFreeCursor(_glfw.x11.display, _glfw.x11.hiddenCursorHandle); - _glfw.x11.hiddenCursorHandle = (Cursor) 0; - } - - free(_glfw.x11.primarySelectionString); - free(_glfw.x11.clipboardString); - - if (_glfw.x11.im) - { - XCloseIM(_glfw.x11.im); - _glfw.x11.im = NULL; - } - - if (_glfw.x11.display) - { - XCloseDisplay(_glfw.x11.display); - _glfw.x11.display = NULL; - } - - if (_glfw.x11.x11xcb.handle) - { - _glfw_dlclose(_glfw.x11.x11xcb.handle); - _glfw.x11.x11xcb.handle = NULL; - } - - if (_glfw.x11.xcursor.handle) - { - _glfw_dlclose(_glfw.x11.xcursor.handle); - _glfw.x11.xcursor.handle = NULL; - } - - if (_glfw.x11.randr.handle) - { - _glfw_dlclose(_glfw.x11.randr.handle); - _glfw.x11.randr.handle = NULL; - } - - if (_glfw.x11.xinerama.handle) - { - _glfw_dlclose(_glfw.x11.xinerama.handle); - _glfw.x11.xinerama.handle = NULL; - } - - if (_glfw.x11.xrender.handle) - { - _glfw_dlclose(_glfw.x11.xrender.handle); - _glfw.x11.xrender.handle = NULL; - } - - if (_glfw.x11.vidmode.handle) - { - _glfw_dlclose(_glfw.x11.vidmode.handle); - _glfw.x11.vidmode.handle = NULL; - } - - if (_glfw.x11.xi.handle) - { - _glfw_dlclose(_glfw.x11.xi.handle); - _glfw.x11.xi.handle = NULL; - } - - _glfwTerminateOSMesa(); - // NOTE: These need to be unloaded after XCloseDisplay, as they register - // cleanup callbacks that get called by that function - _glfwTerminateEGL(); - _glfwTerminateGLX(); - -#if defined(__linux__) - _glfwTerminateJoysticksLinux(); -#endif - - if (_glfw.x11.emptyEventPipe[0] || _glfw.x11.emptyEventPipe[1]) - { - close(_glfw.x11.emptyEventPipe[0]); - close(_glfw.x11.emptyEventPipe[1]); - } -} - -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " X11 GLX EGL OSMesa" -#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) - " clock_gettime" -#else - " gettimeofday" -#endif -#if defined(__linux__) - " evdev" -#endif -#if defined(_GLFW_BUILD_DLL) - " shared" -#endif - ; -} - diff --git a/libs/zglfw/libs/glfw/src/x11_monitor.c b/libs/zglfw/libs/glfw/src/x11_monitor.c deleted file mode 100644 index fb3a67bac..000000000 --- a/libs/zglfw/libs/glfw/src/x11_monitor.c +++ /dev/null @@ -1,614 +0,0 @@ -//======================================================================== -// GLFW 3.3 X11 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include - - -// Check whether the display mode should be included in enumeration -// -static GLFWbool modeIsGood(const XRRModeInfo* mi) -{ - return (mi->modeFlags & RR_Interlace) == 0; -} - -// Calculates the refresh rate, in Hz, from the specified RandR mode info -// -static int calculateRefreshRate(const XRRModeInfo* mi) -{ - if (mi->hTotal && mi->vTotal) - return (int) round((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal)); - else - return 0; -} - -// Returns the mode info for a RandR mode XID -// -static const XRRModeInfo* getModeInfo(const XRRScreenResources* sr, RRMode id) -{ - for (int i = 0; i < sr->nmode; i++) - { - if (sr->modes[i].id == id) - return sr->modes + i; - } - - return NULL; -} - -// Convert RandR mode info to GLFW video mode -// -static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi, - const XRRCrtcInfo* ci) -{ - GLFWvidmode mode; - - if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) - { - mode.width = mi->height; - mode.height = mi->width; - } - else - { - mode.width = mi->width; - mode.height = mi->height; - } - - mode.refreshRate = calculateRefreshRate(mi); - - _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), - &mode.redBits, &mode.greenBits, &mode.blueBits); - - return mode; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Poll for changes in the set of connected monitors -// -void _glfwPollMonitorsX11(void) -{ - if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) - { - int disconnectedCount, screenCount = 0; - _GLFWmonitor** disconnected = NULL; - XineramaScreenInfo* screens = NULL; - XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, - _glfw.x11.root); - RROutput primary = XRRGetOutputPrimary(_glfw.x11.display, - _glfw.x11.root); - - if (_glfw.x11.xinerama.available) - screens = XineramaQueryScreens(_glfw.x11.display, &screenCount); - - disconnectedCount = _glfw.monitorCount; - if (disconnectedCount) - { - disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); - memcpy(disconnected, - _glfw.monitors, - _glfw.monitorCount * sizeof(_GLFWmonitor*)); - } - - for (int i = 0; i < sr->noutput; i++) - { - int j, type, widthMM, heightMM; - - XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, sr->outputs[i]); - if (oi->connection != RR_Connected || oi->crtc == None) - { - XRRFreeOutputInfo(oi); - continue; - } - - for (j = 0; j < disconnectedCount; j++) - { - if (disconnected[j] && - disconnected[j]->x11.output == sr->outputs[i]) - { - disconnected[j] = NULL; - break; - } - } - - if (j < disconnectedCount) - { - XRRFreeOutputInfo(oi); - continue; - } - - XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc); - if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) - { - widthMM = oi->mm_height; - heightMM = oi->mm_width; - } - else - { - widthMM = oi->mm_width; - heightMM = oi->mm_height; - } - - if (widthMM <= 0 || heightMM <= 0) - { - // HACK: If RandR does not provide a physical size, assume the - // X11 default 96 DPI and calculate from the CRTC viewport - // NOTE: These members are affected by rotation, unlike the mode - // info and output info members - widthMM = (int) (ci->width * 25.4f / 96.f); - heightMM = (int) (ci->height * 25.4f / 96.f); - } - - _GLFWmonitor* monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM); - monitor->x11.output = sr->outputs[i]; - monitor->x11.crtc = oi->crtc; - - for (j = 0; j < screenCount; j++) - { - if (screens[j].x_org == ci->x && - screens[j].y_org == ci->y && - screens[j].width == ci->width && - screens[j].height == ci->height) - { - monitor->x11.index = j; - break; - } - } - - if (monitor->x11.output == primary) - type = _GLFW_INSERT_FIRST; - else - type = _GLFW_INSERT_LAST; - - _glfwInputMonitor(monitor, GLFW_CONNECTED, type); - - XRRFreeOutputInfo(oi); - XRRFreeCrtcInfo(ci); - } - - XRRFreeScreenResources(sr); - - if (screens) - XFree(screens); - - for (int i = 0; i < disconnectedCount; i++) - { - if (disconnected[i]) - _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); - } - - free(disconnected); - } - else - { - const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); - const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); - - _glfwInputMonitor(_glfwAllocMonitor("Display", widthMM, heightMM), - GLFW_CONNECTED, - _GLFW_INSERT_FIRST); - } -} - -// Set the current video mode for the specified monitor -// -void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) -{ - if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) - { - GLFWvidmode current; - RRMode native = None; - - const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired); - _glfwPlatformGetVideoMode(monitor, ¤t); - if (_glfwCompareVideoModes(¤t, best) == 0) - return; - - XRRScreenResources* sr = - XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); - XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); - XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); - - for (int i = 0; i < oi->nmode; i++) - { - const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); - if (!modeIsGood(mi)) - continue; - - const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); - if (_glfwCompareVideoModes(best, &mode) == 0) - { - native = mi->id; - break; - } - } - - if (native) - { - if (monitor->x11.oldMode == None) - monitor->x11.oldMode = ci->mode; - - XRRSetCrtcConfig(_glfw.x11.display, - sr, monitor->x11.crtc, - CurrentTime, - ci->x, ci->y, - native, - ci->rotation, - ci->outputs, - ci->noutput); - } - - XRRFreeOutputInfo(oi); - XRRFreeCrtcInfo(ci); - XRRFreeScreenResources(sr); - } -} - -// Restore the saved (original) video mode for the specified monitor -// -void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) -{ - if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) - { - if (monitor->x11.oldMode == None) - return; - - XRRScreenResources* sr = - XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); - XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); - - XRRSetCrtcConfig(_glfw.x11.display, - sr, monitor->x11.crtc, - CurrentTime, - ci->x, ci->y, - monitor->x11.oldMode, - ci->rotation, - ci->outputs, - ci->noutput); - - XRRFreeCrtcInfo(ci); - XRRFreeScreenResources(sr); - - monitor->x11.oldMode = None; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) -{ -} - -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) -{ - if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) - { - XRRScreenResources* sr = - XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); - XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); - - if (ci) - { - if (xpos) - *xpos = ci->x; - if (ypos) - *ypos = ci->y; - - XRRFreeCrtcInfo(ci); - } - - XRRFreeScreenResources(sr); - } -} - -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) -{ - if (xscale) - *xscale = _glfw.x11.contentScaleX; - if (yscale) - *yscale = _glfw.x11.contentScaleY; -} - -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) -{ - int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0; - - if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) - { - XRRScreenResources* sr = - XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); - XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); - - areaX = ci->x; - areaY = ci->y; - - const XRRModeInfo* mi = getModeInfo(sr, ci->mode); - - if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) - { - areaWidth = mi->height; - areaHeight = mi->width; - } - else - { - areaWidth = mi->width; - areaHeight = mi->height; - } - - XRRFreeCrtcInfo(ci); - XRRFreeScreenResources(sr); - } - else - { - areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); - areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); - } - - if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP) - { - Atom* extents = NULL; - Atom* desktop = NULL; - const unsigned long extentCount = - _glfwGetWindowPropertyX11(_glfw.x11.root, - _glfw.x11.NET_WORKAREA, - XA_CARDINAL, - (unsigned char**) &extents); - - if (_glfwGetWindowPropertyX11(_glfw.x11.root, - _glfw.x11.NET_CURRENT_DESKTOP, - XA_CARDINAL, - (unsigned char**) &desktop) > 0) - { - if (extentCount >= 4 && *desktop < extentCount / 4) - { - const int globalX = extents[*desktop * 4 + 0]; - const int globalY = extents[*desktop * 4 + 1]; - const int globalWidth = extents[*desktop * 4 + 2]; - const int globalHeight = extents[*desktop * 4 + 3]; - - if (areaX < globalX) - { - areaWidth -= globalX - areaX; - areaX = globalX; - } - - if (areaY < globalY) - { - areaHeight -= globalY - areaY; - areaY = globalY; - } - - if (areaX + areaWidth > globalX + globalWidth) - areaWidth = globalX - areaX + globalWidth; - if (areaY + areaHeight > globalY + globalHeight) - areaHeight = globalY - areaY + globalHeight; - } - } - - if (extents) - XFree(extents); - if (desktop) - XFree(desktop); - } - - if (xpos) - *xpos = areaX; - if (ypos) - *ypos = areaY; - if (width) - *width = areaWidth; - if (height) - *height = areaHeight; -} - -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) -{ - GLFWvidmode* result; - - *count = 0; - - if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) - { - XRRScreenResources* sr = - XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); - XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); - XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); - - result = calloc(oi->nmode, sizeof(GLFWvidmode)); - - for (int i = 0; i < oi->nmode; i++) - { - const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); - if (!modeIsGood(mi)) - continue; - - const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); - int j; - - for (j = 0; j < *count; j++) - { - if (_glfwCompareVideoModes(result + j, &mode) == 0) - break; - } - - // Skip duplicate modes - if (j < *count) - continue; - - (*count)++; - result[*count - 1] = mode; - } - - XRRFreeOutputInfo(oi); - XRRFreeCrtcInfo(ci); - XRRFreeScreenResources(sr); - } - else - { - *count = 1; - result = calloc(1, sizeof(GLFWvidmode)); - _glfwPlatformGetVideoMode(monitor, result); - } - - return result; -} - -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) -{ - if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) - { - XRRScreenResources* sr = - XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); - XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); - - if (ci) - { - const XRRModeInfo* mi = getModeInfo(sr, ci->mode); - if (mi) // mi can be NULL if the monitor has been disconnected - *mode = vidmodeFromModeInfo(mi, ci); - - XRRFreeCrtcInfo(ci); - } - - XRRFreeScreenResources(sr); - } - else - { - mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); - mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); - mode->refreshRate = 0; - - _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), - &mode->redBits, &mode->greenBits, &mode->blueBits); - } -} - -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) -{ - if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) - { - const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display, - monitor->x11.crtc); - XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display, - monitor->x11.crtc); - - _glfwAllocGammaArrays(ramp, size); - - memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); - memcpy(ramp->green, gamma->green, size * sizeof(unsigned short)); - memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); - - XRRFreeGamma(gamma); - return GLFW_TRUE; - } - else if (_glfw.x11.vidmode.available) - { - int size; - XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size); - - _glfwAllocGammaArrays(ramp, size); - - XF86VidModeGetGammaRamp(_glfw.x11.display, - _glfw.x11.screen, - ramp->size, ramp->red, ramp->green, ramp->blue); - return GLFW_TRUE; - } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Gamma ramp access not supported by server"); - return GLFW_FALSE; - } -} - -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) -{ - if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) - { - if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != ramp->size) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Gamma ramp size must match current ramp size"); - return; - } - - XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size); - - memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); - memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short)); - memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); - - XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma); - XRRFreeGamma(gamma); - } - else if (_glfw.x11.vidmode.available) - { - XF86VidModeSetGammaRamp(_glfw.x11.display, - _glfw.x11.screen, - ramp->size, - (unsigned short*) ramp->red, - (unsigned short*) ramp->green, - (unsigned short*) ramp->blue); - } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Gamma ramp access not supported by server"); - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(None); - return monitor->x11.crtc; -} - -GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(None); - return monitor->x11.output; -} - diff --git a/libs/zglfw/libs/glfw/src/x11_platform.h b/libs/zglfw/libs/glfw/src/x11_platform.h deleted file mode 100644 index 03ff9d242..000000000 --- a/libs/zglfw/libs/glfw/src/x11_platform.h +++ /dev/null @@ -1,450 +0,0 @@ -//======================================================================== -// GLFW 3.3 X11 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== - -#include -#include -#include -#include - -#include -#include -#include -#include - -// The XRandR extension provides mode setting and gamma control -#include - -// The Xkb extension provides improved keyboard support -#include - -// The Xinerama extension provides legacy monitor indices -#include - -// The XInput extension provides raw mouse motion input -#include - -typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int); -typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*); -typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*); -typedef void (* PFN_XRRFreeOutputInfo)(XRROutputInfo*); -typedef void (* PFN_XRRFreeScreenResources)(XRRScreenResources*); -typedef XRRCrtcGamma* (* PFN_XRRGetCrtcGamma)(Display*,RRCrtc); -typedef int (* PFN_XRRGetCrtcGammaSize)(Display*,RRCrtc); -typedef XRRCrtcInfo* (* PFN_XRRGetCrtcInfo) (Display*,XRRScreenResources*,RRCrtc); -typedef XRROutputInfo* (* PFN_XRRGetOutputInfo)(Display*,XRRScreenResources*,RROutput); -typedef RROutput (* PFN_XRRGetOutputPrimary)(Display*,Window); -typedef XRRScreenResources* (* PFN_XRRGetScreenResourcesCurrent)(Display*,Window); -typedef Bool (* PFN_XRRQueryExtension)(Display*,int*,int*); -typedef Status (* PFN_XRRQueryVersion)(Display*,int*,int*); -typedef void (* PFN_XRRSelectInput)(Display*,Window,int); -typedef Status (* PFN_XRRSetCrtcConfig)(Display*,XRRScreenResources*,RRCrtc,Time,int,int,RRMode,Rotation,RROutput*,int); -typedef void (* PFN_XRRSetCrtcGamma)(Display*,RRCrtc,XRRCrtcGamma*); -typedef int (* PFN_XRRUpdateConfiguration)(XEvent*); -#define XRRAllocGamma _glfw.x11.randr.AllocGamma -#define XRRFreeCrtcInfo _glfw.x11.randr.FreeCrtcInfo -#define XRRFreeGamma _glfw.x11.randr.FreeGamma -#define XRRFreeOutputInfo _glfw.x11.randr.FreeOutputInfo -#define XRRFreeScreenResources _glfw.x11.randr.FreeScreenResources -#define XRRGetCrtcGamma _glfw.x11.randr.GetCrtcGamma -#define XRRGetCrtcGammaSize _glfw.x11.randr.GetCrtcGammaSize -#define XRRGetCrtcInfo _glfw.x11.randr.GetCrtcInfo -#define XRRGetOutputInfo _glfw.x11.randr.GetOutputInfo -#define XRRGetOutputPrimary _glfw.x11.randr.GetOutputPrimary -#define XRRGetScreenResourcesCurrent _glfw.x11.randr.GetScreenResourcesCurrent -#define XRRQueryExtension _glfw.x11.randr.QueryExtension -#define XRRQueryVersion _glfw.x11.randr.QueryVersion -#define XRRSelectInput _glfw.x11.randr.SelectInput -#define XRRSetCrtcConfig _glfw.x11.randr.SetCrtcConfig -#define XRRSetCrtcGamma _glfw.x11.randr.SetCrtcGamma -#define XRRUpdateConfiguration _glfw.x11.randr.UpdateConfiguration - -typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int); -typedef void (* PFN_XcursorImageDestroy)(XcursorImage*); -typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*); -#define XcursorImageCreate _glfw.x11.xcursor.ImageCreate -#define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy -#define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor - -typedef Bool (* PFN_XineramaIsActive)(Display*); -typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*); -typedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*); -#define XineramaIsActive _glfw.x11.xinerama.IsActive -#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension -#define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens - -typedef XID xcb_window_t; -typedef XID xcb_visualid_t; -typedef struct xcb_connection_t xcb_connection_t; -typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*); -#define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection - -typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*); -typedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); -typedef Bool (* PFN_XF86VidModeSetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); -typedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*); -#define XF86VidModeQueryExtension _glfw.x11.vidmode.QueryExtension -#define XF86VidModeGetGammaRamp _glfw.x11.vidmode.GetGammaRamp -#define XF86VidModeSetGammaRamp _glfw.x11.vidmode.SetGammaRamp -#define XF86VidModeGetGammaRampSize _glfw.x11.vidmode.GetGammaRampSize - -typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*); -typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); -#define XIQueryVersion _glfw.x11.xi.QueryVersion -#define XISelectEvents _glfw.x11.xi.SelectEvents - -typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*); -typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*); -typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*); -#define XRenderQueryExtension _glfw.x11.xrender.QueryExtension -#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion -#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat - -typedef VkFlags VkXlibSurfaceCreateFlagsKHR; -typedef VkFlags VkXcbSurfaceCreateFlagsKHR; - -typedef struct VkXlibSurfaceCreateInfoKHR -{ - VkStructureType sType; - const void* pNext; - VkXlibSurfaceCreateFlagsKHR flags; - Display* dpy; - Window window; -} VkXlibSurfaceCreateInfoKHR; - -typedef struct VkXcbSurfaceCreateInfoKHR -{ - VkStructureType sType; - const void* pNext; - VkXcbSurfaceCreateFlagsKHR flags; - xcb_connection_t* connection; - xcb_window_t window; -} VkXcbSurfaceCreateInfoKHR; - -typedef VkResult (APIENTRY *PFN_vkCreateXlibSurfaceKHR)(VkInstance,const VkXlibSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); -typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice,uint32_t,Display*,VisualID); -typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); -typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t); - -#include "posix_thread.h" -#include "posix_time.h" -#include "xkb_unicode.h" -#include "glx_context.h" -#include "egl_context.h" -#include "osmesa_context.h" -#if defined(__linux__) -#include "linux_joystick.h" -#else -#include "null_joystick.h" -#endif - -#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) -#define _glfw_dlclose(handle) dlclose(handle) -#define _glfw_dlsym(handle, name) dlsym(handle, name) - -#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->x11.handle) -#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display) - -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11 -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11 -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11 - - -// X11-specific per-window data -// -typedef struct _GLFWwindowX11 -{ - Colormap colormap; - Window handle; - Window parent; - XIC ic; - - GLFWbool overrideRedirect; - GLFWbool iconified; - GLFWbool maximized; - - // Whether the visual supports framebuffer transparency - GLFWbool transparent; - - // Cached position and size used to filter out duplicate events - int width, height; - int xpos, ypos; - - // The last received cursor position, regardless of source - int lastCursorPosX, lastCursorPosY; - // The last position the cursor was warped to by GLFW - int warpCursorPosX, warpCursorPosY; - - // The time of the last KeyPress event per keycode, for discarding - // duplicate key events generated for some keys by ibus - Time keyPressTimes[256]; -} _GLFWwindowX11; - -// X11-specific global data -// -typedef struct _GLFWlibraryX11 -{ - Display* display; - int screen; - Window root; - - // System content scale - float contentScaleX, contentScaleY; - // Helper window for IPC - Window helperWindowHandle; - // Invisible cursor for hidden cursor mode - Cursor hiddenCursorHandle; - // Context for mapping window XIDs to _GLFWwindow pointers - XContext context; - // XIM input method - XIM im; - // The previous X error handler, to be restored later - XErrorHandler errorHandler; - // Most recent error code received by X error handler - int errorCode; - // Primary selection string (while the primary selection is owned) - char* primarySelectionString; - // Clipboard string (while the selection is owned) - char* clipboardString; - // Key name string - char keynames[GLFW_KEY_LAST + 1][5]; - // X11 keycode to GLFW key LUT - short int keycodes[256]; - // GLFW key to X11 keycode LUT - short int scancodes[GLFW_KEY_LAST + 1]; - // Where to place the cursor when re-enabled - double restoreCursorPosX, restoreCursorPosY; - // The window whose disabled cursor mode is active - _GLFWwindow* disabledCursorWindow; - int emptyEventPipe[2]; - - // Window manager atoms - Atom NET_SUPPORTED; - Atom NET_SUPPORTING_WM_CHECK; - Atom WM_PROTOCOLS; - Atom WM_STATE; - Atom WM_DELETE_WINDOW; - Atom NET_WM_NAME; - Atom NET_WM_ICON_NAME; - Atom NET_WM_ICON; - Atom NET_WM_PID; - Atom NET_WM_PING; - Atom NET_WM_WINDOW_TYPE; - Atom NET_WM_WINDOW_TYPE_NORMAL; - Atom NET_WM_STATE; - Atom NET_WM_STATE_ABOVE; - Atom NET_WM_STATE_FULLSCREEN; - Atom NET_WM_STATE_MAXIMIZED_VERT; - Atom NET_WM_STATE_MAXIMIZED_HORZ; - Atom NET_WM_STATE_DEMANDS_ATTENTION; - Atom NET_WM_BYPASS_COMPOSITOR; - Atom NET_WM_FULLSCREEN_MONITORS; - Atom NET_WM_WINDOW_OPACITY; - Atom NET_WM_CM_Sx; - Atom NET_WORKAREA; - Atom NET_CURRENT_DESKTOP; - Atom NET_ACTIVE_WINDOW; - Atom NET_FRAME_EXTENTS; - Atom NET_REQUEST_FRAME_EXTENTS; - Atom MOTIF_WM_HINTS; - - // Xdnd (drag and drop) atoms - Atom XdndAware; - Atom XdndEnter; - Atom XdndPosition; - Atom XdndStatus; - Atom XdndActionCopy; - Atom XdndDrop; - Atom XdndFinished; - Atom XdndSelection; - Atom XdndTypeList; - Atom text_uri_list; - - // Selection (clipboard) atoms - Atom TARGETS; - Atom MULTIPLE; - Atom INCR; - Atom CLIPBOARD; - Atom PRIMARY; - Atom CLIPBOARD_MANAGER; - Atom SAVE_TARGETS; - Atom NULL_; - Atom UTF8_STRING; - Atom COMPOUND_STRING; - Atom ATOM_PAIR; - Atom GLFW_SELECTION; - - struct { - GLFWbool available; - void* handle; - int eventBase; - int errorBase; - int major; - int minor; - GLFWbool gammaBroken; - GLFWbool monitorBroken; - PFN_XRRAllocGamma AllocGamma; - PFN_XRRFreeCrtcInfo FreeCrtcInfo; - PFN_XRRFreeGamma FreeGamma; - PFN_XRRFreeOutputInfo FreeOutputInfo; - PFN_XRRFreeScreenResources FreeScreenResources; - PFN_XRRGetCrtcGamma GetCrtcGamma; - PFN_XRRGetCrtcGammaSize GetCrtcGammaSize; - PFN_XRRGetCrtcInfo GetCrtcInfo; - PFN_XRRGetOutputInfo GetOutputInfo; - PFN_XRRGetOutputPrimary GetOutputPrimary; - PFN_XRRGetScreenResourcesCurrent GetScreenResourcesCurrent; - PFN_XRRQueryExtension QueryExtension; - PFN_XRRQueryVersion QueryVersion; - PFN_XRRSelectInput SelectInput; - PFN_XRRSetCrtcConfig SetCrtcConfig; - PFN_XRRSetCrtcGamma SetCrtcGamma; - PFN_XRRUpdateConfiguration UpdateConfiguration; - } randr; - - struct { - GLFWbool available; - GLFWbool detectable; - int majorOpcode; - int eventBase; - int errorBase; - int major; - int minor; - unsigned int group; - } xkb; - - struct { - int count; - int timeout; - int interval; - int blanking; - int exposure; - } saver; - - struct { - int version; - Window source; - Atom format; - } xdnd; - - struct { - void* handle; - PFN_XcursorImageCreate ImageCreate; - PFN_XcursorImageDestroy ImageDestroy; - PFN_XcursorImageLoadCursor ImageLoadCursor; - } xcursor; - - struct { - GLFWbool available; - void* handle; - int major; - int minor; - PFN_XineramaIsActive IsActive; - PFN_XineramaQueryExtension QueryExtension; - PFN_XineramaQueryScreens QueryScreens; - } xinerama; - - struct { - void* handle; - PFN_XGetXCBConnection GetXCBConnection; - } x11xcb; - - struct { - GLFWbool available; - void* handle; - int eventBase; - int errorBase; - PFN_XF86VidModeQueryExtension QueryExtension; - PFN_XF86VidModeGetGammaRamp GetGammaRamp; - PFN_XF86VidModeSetGammaRamp SetGammaRamp; - PFN_XF86VidModeGetGammaRampSize GetGammaRampSize; - } vidmode; - - struct { - GLFWbool available; - void* handle; - int majorOpcode; - int eventBase; - int errorBase; - int major; - int minor; - PFN_XIQueryVersion QueryVersion; - PFN_XISelectEvents SelectEvents; - } xi; - - struct { - GLFWbool available; - void* handle; - int major; - int minor; - int eventBase; - int errorBase; - PFN_XRenderQueryExtension QueryExtension; - PFN_XRenderQueryVersion QueryVersion; - PFN_XRenderFindVisualFormat FindVisualFormat; - } xrender; -} _GLFWlibraryX11; - -// X11-specific per-monitor data -// -typedef struct _GLFWmonitorX11 -{ - RROutput output; - RRCrtc crtc; - RRMode oldMode; - - // Index of corresponding Xinerama screen, - // for EWMH full screen window placement - int index; -} _GLFWmonitorX11; - -// X11-specific per-cursor data -// -typedef struct _GLFWcursorX11 -{ - Cursor handle; -} _GLFWcursorX11; - - -void _glfwPollMonitorsX11(void); -void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired); -void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor); - -Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot); - -unsigned long _glfwGetWindowPropertyX11(Window window, - Atom property, - Atom type, - unsigned char** value); -GLFWbool _glfwIsVisualTransparentX11(Visual* visual); - -void _glfwGrabErrorHandlerX11(void); -void _glfwReleaseErrorHandlerX11(void); -void _glfwInputErrorX11(int error, const char* message); - -void _glfwPushSelectionToManagerX11(void); - diff --git a/libs/zglfw/libs/glfw/src/x11_window.c b/libs/zglfw/libs/glfw/src/x11_window.c deleted file mode 100644 index ddda48d77..000000000 --- a/libs/zglfw/libs/glfw/src/x11_window.c +++ /dev/null @@ -1,3174 +0,0 @@ -//======================================================================== -// GLFW 3.3 X11 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2019 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#define _GNU_SOURCE - -#include "internal.h" - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -// Action for EWMH client messages -#define _NET_WM_STATE_REMOVE 0 -#define _NET_WM_STATE_ADD 1 -#define _NET_WM_STATE_TOGGLE 2 - -// Additional mouse button names for XButtonEvent -#define Button6 6 -#define Button7 7 - -// Motif WM hints flags -#define MWM_HINTS_DECORATIONS 2 -#define MWM_DECOR_ALL 1 - -#define _GLFW_XDND_VERSION 5 - -// Wait for data to arrive on any of the specified file descriptors -// -static GLFWbool waitForData(struct pollfd* fds, nfds_t count, double* timeout) -{ - for (;;) - { - if (timeout) - { - const uint64_t base = _glfwPlatformGetTimerValue(); - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) - const time_t seconds = (time_t) *timeout; - const long nanoseconds = (long) ((*timeout - seconds) * 1e9); - const struct timespec ts = { seconds, nanoseconds }; - const int result = ppoll(fds, count, &ts, NULL); -#elif defined(__NetBSD__) - const time_t seconds = (time_t) *timeout; - const long nanoseconds = (long) ((*timeout - seconds) * 1e9); - const struct timespec ts = { seconds, nanoseconds }; - const int result = pollts(fds, count, &ts, NULL); -#else - const int milliseconds = (int) (*timeout * 1e3); - const int result = poll(fds, count, milliseconds); -#endif - const int error = errno; // clock_gettime may overwrite our error - - *timeout -= (_glfwPlatformGetTimerValue() - base) / - (double) _glfwPlatformGetTimerFrequency(); - - if (result > 0) - return GLFW_TRUE; - else if (result == -1 && error != EINTR && error != EAGAIN) - return GLFW_FALSE; - else if (*timeout <= 0.0) - return GLFW_FALSE; - } - else - { - const int result = poll(fds, count, -1); - if (result > 0) - return GLFW_TRUE; - else if (result == -1 && errno != EINTR && errno != EAGAIN) - return GLFW_FALSE; - } - } -} - -// Wait for event data to arrive on the X11 display socket -// This avoids blocking other threads via the per-display Xlib lock that also -// covers GLX functions -// -static GLFWbool waitForX11Event(double* timeout) -{ - struct pollfd fd = { ConnectionNumber(_glfw.x11.display), POLLIN }; - - while (!XPending(_glfw.x11.display)) - { - if (!waitForData(&fd, 1, timeout)) - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -// Wait for event data to arrive on any event file descriptor -// This avoids blocking other threads via the per-display Xlib lock that also -// covers GLX functions -// -static GLFWbool waitForAnyEvent(double* timeout) -{ - nfds_t count = 2; - struct pollfd fds[3] = - { - { ConnectionNumber(_glfw.x11.display), POLLIN }, - { _glfw.x11.emptyEventPipe[0], POLLIN } - }; - -#if defined(__linux__) - if (_glfw.linjs.inotify > 0) - fds[count++] = (struct pollfd) { _glfw.linjs.inotify, POLLIN }; -#endif - - while (!XPending(_glfw.x11.display)) - { - if (!waitForData(fds, count, timeout)) - return GLFW_FALSE; - - for (int i = 1; i < count; i++) - { - if (fds[i].revents & POLLIN) - return GLFW_TRUE; - } - } - - return GLFW_TRUE; -} - -// Writes a byte to the empty event pipe -// -static void writeEmptyEvent(void) -{ - for (;;) - { - const char byte = 0; - const ssize_t result = write(_glfw.x11.emptyEventPipe[1], &byte, 1); - if (result == 1 || (result == -1 && errno != EINTR)) - break; - } -} - -// Drains available data from the empty event pipe -// -static void drainEmptyEvents(void) -{ - for (;;) - { - char dummy[64]; - const ssize_t result = read(_glfw.x11.emptyEventPipe[0], dummy, sizeof(dummy)); - if (result == -1 && errno != EINTR) - break; - } -} - -// Waits until a VisibilityNotify event arrives for the specified window or the -// timeout period elapses (ICCCM section 4.2.2) -// -static GLFWbool waitForVisibilityNotify(_GLFWwindow* window) -{ - XEvent dummy; - double timeout = 0.1; - - while (!XCheckTypedWindowEvent(_glfw.x11.display, - window->x11.handle, - VisibilityNotify, - &dummy)) - { - if (!waitForX11Event(&timeout)) - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -// Returns whether the window is iconified -// -static int getWindowState(_GLFWwindow* window) -{ - int result = WithdrawnState; - struct { - CARD32 state; - Window icon; - } *state = NULL; - - if (_glfwGetWindowPropertyX11(window->x11.handle, - _glfw.x11.WM_STATE, - _glfw.x11.WM_STATE, - (unsigned char**) &state) >= 2) - { - result = state->state; - } - - if (state) - XFree(state); - - return result; -} - -// Returns whether the event is a selection event -// -static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer) -{ - if (event->xany.window != _glfw.x11.helperWindowHandle) - return False; - - return event->type == SelectionRequest || - event->type == SelectionNotify || - event->type == SelectionClear; -} - -// Returns whether it is a _NET_FRAME_EXTENTS event for the specified window -// -static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer) -{ - _GLFWwindow* window = (_GLFWwindow*) pointer; - return event->type == PropertyNotify && - event->xproperty.state == PropertyNewValue && - event->xproperty.window == window->x11.handle && - event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS; -} - -// Returns whether it is a property event for the specified selection transfer -// -static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer) -{ - XEvent* notification = (XEvent*) pointer; - return event->type == PropertyNotify && - event->xproperty.state == PropertyNewValue && - event->xproperty.window == notification->xselection.requestor && - event->xproperty.atom == notification->xselection.property; -} - -// Translates an X event modifier state mask -// -static int translateState(int state) -{ - int mods = 0; - - if (state & ShiftMask) - mods |= GLFW_MOD_SHIFT; - if (state & ControlMask) - mods |= GLFW_MOD_CONTROL; - if (state & Mod1Mask) - mods |= GLFW_MOD_ALT; - if (state & Mod4Mask) - mods |= GLFW_MOD_SUPER; - if (state & LockMask) - mods |= GLFW_MOD_CAPS_LOCK; - if (state & Mod2Mask) - mods |= GLFW_MOD_NUM_LOCK; - - return mods; -} - -// Translates an X11 key code to a GLFW key token -// -static int translateKey(int scancode) -{ - // Use the pre-filled LUT (see createKeyTables() in x11_init.c) - if (scancode < 0 || scancode > 255) - return GLFW_KEY_UNKNOWN; - - return _glfw.x11.keycodes[scancode]; -} - -// Sends an EWMH or ICCCM event to the window manager -// -static void sendEventToWM(_GLFWwindow* window, Atom type, - long a, long b, long c, long d, long e) -{ - XEvent event = { ClientMessage }; - event.xclient.window = window->x11.handle; - event.xclient.format = 32; // Data is 32-bit longs - event.xclient.message_type = type; - event.xclient.data.l[0] = a; - event.xclient.data.l[1] = b; - event.xclient.data.l[2] = c; - event.xclient.data.l[3] = d; - event.xclient.data.l[4] = e; - - XSendEvent(_glfw.x11.display, _glfw.x11.root, - False, - SubstructureNotifyMask | SubstructureRedirectMask, - &event); -} - -// Updates the normal hints according to the window settings -// -static void updateNormalHints(_GLFWwindow* window, int width, int height) -{ - XSizeHints* hints = XAllocSizeHints(); - - if (!window->monitor) - { - if (window->resizable) - { - if (window->minwidth != GLFW_DONT_CARE && - window->minheight != GLFW_DONT_CARE) - { - hints->flags |= PMinSize; - hints->min_width = window->minwidth; - hints->min_height = window->minheight; - } - - if (window->maxwidth != GLFW_DONT_CARE && - window->maxheight != GLFW_DONT_CARE) - { - hints->flags |= PMaxSize; - hints->max_width = window->maxwidth; - hints->max_height = window->maxheight; - } - - if (window->numer != GLFW_DONT_CARE && - window->denom != GLFW_DONT_CARE) - { - hints->flags |= PAspect; - hints->min_aspect.x = hints->max_aspect.x = window->numer; - hints->min_aspect.y = hints->max_aspect.y = window->denom; - } - } - else - { - hints->flags |= (PMinSize | PMaxSize); - hints->min_width = hints->max_width = width; - hints->min_height = hints->max_height = height; - } - } - - hints->flags |= PWinGravity; - hints->win_gravity = StaticGravity; - - XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); - XFree(hints); -} - -// Updates the full screen status of the window -// -static void updateWindowMode(_GLFWwindow* window) -{ - if (window->monitor) - { - if (_glfw.x11.xinerama.available && - _glfw.x11.NET_WM_FULLSCREEN_MONITORS) - { - sendEventToWM(window, - _glfw.x11.NET_WM_FULLSCREEN_MONITORS, - window->monitor->x11.index, - window->monitor->x11.index, - window->monitor->x11.index, - window->monitor->x11.index, - 0); - } - - if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) - { - sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - _NET_WM_STATE_ADD, - _glfw.x11.NET_WM_STATE_FULLSCREEN, - 0, 1, 0); - } - else - { - // This is the butcher's way of removing window decorations - // Setting the override-redirect attribute on a window makes the - // window manager ignore the window completely (ICCCM, section 4) - // The good thing is that this makes undecorated full screen windows - // easy to do; the bad thing is that we have to do everything - // manually and some things (like iconify/restore) won't work at - // all, as those are tasks usually performed by the window manager - - XSetWindowAttributes attributes; - attributes.override_redirect = True; - XChangeWindowAttributes(_glfw.x11.display, - window->x11.handle, - CWOverrideRedirect, - &attributes); - - window->x11.overrideRedirect = GLFW_TRUE; - } - - // Enable compositor bypass - if (!window->x11.transparent) - { - const unsigned long value = 1; - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, - PropModeReplace, (unsigned char*) &value, 1); - } - } - else - { - if (_glfw.x11.xinerama.available && - _glfw.x11.NET_WM_FULLSCREEN_MONITORS) - { - XDeleteProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_FULLSCREEN_MONITORS); - } - - if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) - { - sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - _NET_WM_STATE_REMOVE, - _glfw.x11.NET_WM_STATE_FULLSCREEN, - 0, 1, 0); - } - else - { - XSetWindowAttributes attributes; - attributes.override_redirect = False; - XChangeWindowAttributes(_glfw.x11.display, - window->x11.handle, - CWOverrideRedirect, - &attributes); - - window->x11.overrideRedirect = GLFW_FALSE; - } - - // Disable compositor bypass - if (!window->x11.transparent) - { - XDeleteProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_BYPASS_COMPOSITOR); - } - } -} - -// Decode a Unicode code point from a UTF-8 stream -// Based on cutef8 by Jeff Bezanson (Public Domain) -// -#if defined(X_HAVE_UTF8_STRING) -static uint32_t decodeUTF8(const char** s) -{ - uint32_t codepoint = 0, count = 0; - static const uint32_t offsets[] = - { - 0x00000000u, 0x00003080u, 0x000e2080u, - 0x03c82080u, 0xfa082080u, 0x82082080u - }; - - do - { - codepoint = (codepoint << 6) + (unsigned char) **s; - (*s)++; - count++; - } while ((**s & 0xc0) == 0x80); - - assert(count <= 6); - return codepoint - offsets[count - 1]; -} -#endif /*X_HAVE_UTF8_STRING*/ - -// Convert the specified Latin-1 string to UTF-8 -// -static char* convertLatin1toUTF8(const char* source) -{ - size_t size = 1; - const char* sp; - - for (sp = source; *sp; sp++) - size += (*sp & 0x80) ? 2 : 1; - - char* target = calloc(size, 1); - char* tp = target; - - for (sp = source; *sp; sp++) - tp += _glfwEncodeUTF8(tp, *sp); - - return target; -} - -// Updates the cursor image according to its cursor mode -// -static void updateCursorImage(_GLFWwindow* window) -{ - if (window->cursorMode == GLFW_CURSOR_NORMAL) - { - if (window->cursor) - { - XDefineCursor(_glfw.x11.display, window->x11.handle, - window->cursor->x11.handle); - } - else - XUndefineCursor(_glfw.x11.display, window->x11.handle); - } - else - { - XDefineCursor(_glfw.x11.display, window->x11.handle, - _glfw.x11.hiddenCursorHandle); - } -} - -// Enable XI2 raw mouse motion events -// -static void enableRawMouseMotion(_GLFWwindow* window) -{ - XIEventMask em; - unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; - - em.deviceid = XIAllMasterDevices; - em.mask_len = sizeof(mask); - em.mask = mask; - XISetMask(mask, XI_RawMotion); - - XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); -} - -// Disable XI2 raw mouse motion events -// -static void disableRawMouseMotion(_GLFWwindow* window) -{ - XIEventMask em; - unsigned char mask[] = { 0 }; - - em.deviceid = XIAllMasterDevices; - em.mask_len = sizeof(mask); - em.mask = mask; - - XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); -} - -// Apply disabled cursor mode to a focused window -// -static void disableCursor(_GLFWwindow* window) -{ - if (window->rawMouseMotion) - enableRawMouseMotion(window); - - _glfw.x11.disabledCursorWindow = window; - _glfwPlatformGetCursorPos(window, - &_glfw.x11.restoreCursorPosX, - &_glfw.x11.restoreCursorPosY); - updateCursorImage(window); - _glfwCenterCursorInContentArea(window); - XGrabPointer(_glfw.x11.display, window->x11.handle, True, - ButtonPressMask | ButtonReleaseMask | PointerMotionMask, - GrabModeAsync, GrabModeAsync, - window->x11.handle, - _glfw.x11.hiddenCursorHandle, - CurrentTime); -} - -// Exit disabled cursor mode for the specified window -// -static void enableCursor(_GLFWwindow* window) -{ - if (window->rawMouseMotion) - disableRawMouseMotion(window); - - _glfw.x11.disabledCursorWindow = NULL; - XUngrabPointer(_glfw.x11.display, CurrentTime); - _glfwPlatformSetCursorPos(window, - _glfw.x11.restoreCursorPosX, - _glfw.x11.restoreCursorPosY); - updateCursorImage(window); -} - -// Create the X11 window (and its colormap) -// -static GLFWbool createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - Visual* visual, int depth) -{ - int width = wndconfig->width; - int height = wndconfig->height; - - if (wndconfig->scaleToMonitor) - { - width *= _glfw.x11.contentScaleX; - height *= _glfw.x11.contentScaleY; - } - - // Create a colormap based on the visual used by the current context - window->x11.colormap = XCreateColormap(_glfw.x11.display, - _glfw.x11.root, - visual, - AllocNone); - - window->x11.transparent = _glfwIsVisualTransparentX11(visual); - - XSetWindowAttributes wa = { 0 }; - wa.colormap = window->x11.colormap; - wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | - PointerMotionMask | ButtonPressMask | ButtonReleaseMask | - ExposureMask | FocusChangeMask | VisibilityChangeMask | - EnterWindowMask | LeaveWindowMask | PropertyChangeMask; - - _glfwGrabErrorHandlerX11(); - - window->x11.parent = _glfw.x11.root; - window->x11.handle = XCreateWindow(_glfw.x11.display, - _glfw.x11.root, - 0, 0, // Position - width, height, - 0, // Border width - depth, // Color depth - InputOutput, - visual, - CWBorderPixel | CWColormap | CWEventMask, - &wa); - - _glfwReleaseErrorHandlerX11(); - - if (!window->x11.handle) - { - _glfwInputErrorX11(GLFW_PLATFORM_ERROR, - "X11: Failed to create window"); - return GLFW_FALSE; - } - - XSaveContext(_glfw.x11.display, - window->x11.handle, - _glfw.x11.context, - (XPointer) window); - - if (!wndconfig->decorated) - _glfwPlatformSetWindowDecorated(window, GLFW_FALSE); - - if (_glfw.x11.NET_WM_STATE && !window->monitor) - { - Atom states[3]; - int count = 0; - - if (wndconfig->floating) - { - if (_glfw.x11.NET_WM_STATE_ABOVE) - states[count++] = _glfw.x11.NET_WM_STATE_ABOVE; - } - - if (wndconfig->maximized) - { - if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && - _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) - { - states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT; - states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ; - window->x11.maximized = GLFW_TRUE; - } - } - - if (count) - { - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_STATE, XA_ATOM, 32, - PropModeReplace, (unsigned char*) states, count); - } - } - - // Declare the WM protocols supported by GLFW - { - Atom protocols[] = - { - _glfw.x11.WM_DELETE_WINDOW, - _glfw.x11.NET_WM_PING - }; - - XSetWMProtocols(_glfw.x11.display, window->x11.handle, - protocols, sizeof(protocols) / sizeof(Atom)); - } - - // Declare our PID - { - const long pid = getpid(); - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_PID, XA_CARDINAL, 32, - PropModeReplace, - (unsigned char*) &pid, 1); - } - - if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL) - { - Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL; - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, - PropModeReplace, (unsigned char*) &type, 1); - } - - // Set ICCCM WM_HINTS property - { - XWMHints* hints = XAllocWMHints(); - if (!hints) - { - _glfwInputError(GLFW_OUT_OF_MEMORY, - "X11: Failed to allocate WM hints"); - return GLFW_FALSE; - } - - hints->flags = StateHint; - hints->initial_state = NormalState; - - XSetWMHints(_glfw.x11.display, window->x11.handle, hints); - XFree(hints); - } - - updateNormalHints(window, width, height); - - // Set ICCCM WM_CLASS property - { - XClassHint* hint = XAllocClassHint(); - - if (strlen(wndconfig->x11.instanceName) && - strlen(wndconfig->x11.className)) - { - hint->res_name = (char*) wndconfig->x11.instanceName; - hint->res_class = (char*) wndconfig->x11.className; - } - else - { - const char* resourceName = getenv("RESOURCE_NAME"); - if (resourceName && strlen(resourceName)) - hint->res_name = (char*) resourceName; - else if (strlen(wndconfig->title)) - hint->res_name = (char*) wndconfig->title; - else - hint->res_name = (char*) "glfw-application"; - - if (strlen(wndconfig->title)) - hint->res_class = (char*) wndconfig->title; - else - hint->res_class = (char*) "GLFW-Application"; - } - - XSetClassHint(_glfw.x11.display, window->x11.handle, hint); - XFree(hint); - } - - // Announce support for Xdnd (drag and drop) - { - const Atom version = _GLFW_XDND_VERSION; - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.XdndAware, XA_ATOM, 32, - PropModeReplace, (unsigned char*) &version, 1); - } - - _glfwPlatformSetWindowTitle(window, wndconfig->title); - - if (_glfw.x11.im) - { - window->x11.ic = XCreateIC(_glfw.x11.im, - XNInputStyle, - XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, - window->x11.handle, - XNFocusWindow, - window->x11.handle, - NULL); - } - - if (window->x11.ic) - { - unsigned long filter = 0; - if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL) - XSelectInput(_glfw.x11.display, window->x11.handle, wa.event_mask | filter); - } - - _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos); - _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height); - - return GLFW_TRUE; -} - -// Set the specified property to the selection converted to the requested target -// -static Atom writeTargetToProperty(const XSelectionRequestEvent* request) -{ - int i; - char* selectionString = NULL; - const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING }; - const int formatCount = sizeof(formats) / sizeof(formats[0]); - - if (request->selection == _glfw.x11.PRIMARY) - selectionString = _glfw.x11.primarySelectionString; - else - selectionString = _glfw.x11.clipboardString; - - if (request->property == None) - { - // The requester is a legacy client (ICCCM section 2.2) - // We don't support legacy clients, so fail here - return None; - } - - if (request->target == _glfw.x11.TARGETS) - { - // The list of supported targets was requested - - const Atom targets[] = { _glfw.x11.TARGETS, - _glfw.x11.MULTIPLE, - _glfw.x11.UTF8_STRING, - XA_STRING }; - - XChangeProperty(_glfw.x11.display, - request->requestor, - request->property, - XA_ATOM, - 32, - PropModeReplace, - (unsigned char*) targets, - sizeof(targets) / sizeof(targets[0])); - - return request->property; - } - - if (request->target == _glfw.x11.MULTIPLE) - { - // Multiple conversions were requested - - Atom* targets; - unsigned long i, count; - - count = _glfwGetWindowPropertyX11(request->requestor, - request->property, - _glfw.x11.ATOM_PAIR, - (unsigned char**) &targets); - - for (i = 0; i < count; i += 2) - { - int j; - - for (j = 0; j < formatCount; j++) - { - if (targets[i] == formats[j]) - break; - } - - if (j < formatCount) - { - XChangeProperty(_glfw.x11.display, - request->requestor, - targets[i + 1], - targets[i], - 8, - PropModeReplace, - (unsigned char *) selectionString, - strlen(selectionString)); - } - else - targets[i + 1] = None; - } - - XChangeProperty(_glfw.x11.display, - request->requestor, - request->property, - _glfw.x11.ATOM_PAIR, - 32, - PropModeReplace, - (unsigned char*) targets, - count); - - XFree(targets); - - return request->property; - } - - if (request->target == _glfw.x11.SAVE_TARGETS) - { - // The request is a check whether we support SAVE_TARGETS - // It should be handled as a no-op side effect target - - XChangeProperty(_glfw.x11.display, - request->requestor, - request->property, - _glfw.x11.NULL_, - 32, - PropModeReplace, - NULL, - 0); - - return request->property; - } - - // Conversion to a data target was requested - - for (i = 0; i < formatCount; i++) - { - if (request->target == formats[i]) - { - // The requested target is one we support - - XChangeProperty(_glfw.x11.display, - request->requestor, - request->property, - request->target, - 8, - PropModeReplace, - (unsigned char *) selectionString, - strlen(selectionString)); - - return request->property; - } - } - - // The requested target is not supported - - return None; -} - -static void handleSelectionRequest(XEvent* event) -{ - const XSelectionRequestEvent* request = &event->xselectionrequest; - - XEvent reply = { SelectionNotify }; - reply.xselection.property = writeTargetToProperty(request); - reply.xselection.display = request->display; - reply.xselection.requestor = request->requestor; - reply.xselection.selection = request->selection; - reply.xselection.target = request->target; - reply.xselection.time = request->time; - - XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply); -} - -static const char* getSelectionString(Atom selection) -{ - char** selectionString = NULL; - const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING }; - const size_t targetCount = sizeof(targets) / sizeof(targets[0]); - - if (selection == _glfw.x11.PRIMARY) - selectionString = &_glfw.x11.primarySelectionString; - else - selectionString = &_glfw.x11.clipboardString; - - if (XGetSelectionOwner(_glfw.x11.display, selection) == - _glfw.x11.helperWindowHandle) - { - // Instead of doing a large number of X round-trips just to put this - // string into a window property and then read it back, just return it - return *selectionString; - } - - free(*selectionString); - *selectionString = NULL; - - for (size_t i = 0; i < targetCount; i++) - { - char* data; - Atom actualType; - int actualFormat; - unsigned long itemCount, bytesAfter; - XEvent notification, dummy; - - XConvertSelection(_glfw.x11.display, - selection, - targets[i], - _glfw.x11.GLFW_SELECTION, - _glfw.x11.helperWindowHandle, - CurrentTime); - - while (!XCheckTypedWindowEvent(_glfw.x11.display, - _glfw.x11.helperWindowHandle, - SelectionNotify, - ¬ification)) - { - waitForX11Event(NULL); - } - - if (notification.xselection.property == None) - continue; - - XCheckIfEvent(_glfw.x11.display, - &dummy, - isSelPropNewValueNotify, - (XPointer) ¬ification); - - XGetWindowProperty(_glfw.x11.display, - notification.xselection.requestor, - notification.xselection.property, - 0, - LONG_MAX, - True, - AnyPropertyType, - &actualType, - &actualFormat, - &itemCount, - &bytesAfter, - (unsigned char**) &data); - - if (actualType == _glfw.x11.INCR) - { - size_t size = 1; - char* string = NULL; - - for (;;) - { - while (!XCheckIfEvent(_glfw.x11.display, - &dummy, - isSelPropNewValueNotify, - (XPointer) ¬ification)) - { - waitForX11Event(NULL); - } - - XFree(data); - XGetWindowProperty(_glfw.x11.display, - notification.xselection.requestor, - notification.xselection.property, - 0, - LONG_MAX, - True, - AnyPropertyType, - &actualType, - &actualFormat, - &itemCount, - &bytesAfter, - (unsigned char**) &data); - - if (itemCount) - { - size += itemCount; - string = realloc(string, size); - string[size - itemCount - 1] = '\0'; - strcat(string, data); - } - - if (!itemCount) - { - if (string) - { - if (targets[i] == XA_STRING) - { - *selectionString = convertLatin1toUTF8(string); - free(string); - } - else - *selectionString = string; - } - - break; - } - } - } - else if (actualType == targets[i]) - { - if (targets[i] == XA_STRING) - *selectionString = convertLatin1toUTF8(data); - else - *selectionString = _glfw_strdup(data); - } - - XFree(data); - - if (*selectionString) - break; - } - - if (!*selectionString) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "X11: Failed to convert selection to string"); - } - - return *selectionString; -} - -// Make the specified window and its video mode active on its monitor -// -static void acquireMonitor(_GLFWwindow* window) -{ - if (_glfw.x11.saver.count == 0) - { - // Remember old screen saver settings - XGetScreenSaver(_glfw.x11.display, - &_glfw.x11.saver.timeout, - &_glfw.x11.saver.interval, - &_glfw.x11.saver.blanking, - &_glfw.x11.saver.exposure); - - // Disable screen saver - XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking, - DefaultExposures); - } - - if (!window->monitor->window) - _glfw.x11.saver.count++; - - _glfwSetVideoModeX11(window->monitor, &window->videoMode); - - if (window->x11.overrideRedirect) - { - int xpos, ypos; - GLFWvidmode mode; - - // Manually position the window over its monitor - _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); - _glfwPlatformGetVideoMode(window->monitor, &mode); - - XMoveResizeWindow(_glfw.x11.display, window->x11.handle, - xpos, ypos, mode.width, mode.height); - } - - _glfwInputMonitorWindow(window->monitor, window); -} - -// Remove the window and restore the original video mode -// -static void releaseMonitor(_GLFWwindow* window) -{ - if (window->monitor->window != window) - return; - - _glfwInputMonitorWindow(window->monitor, NULL); - _glfwRestoreVideoModeX11(window->monitor); - - _glfw.x11.saver.count--; - - if (_glfw.x11.saver.count == 0) - { - // Restore old screen saver settings - XSetScreenSaver(_glfw.x11.display, - _glfw.x11.saver.timeout, - _glfw.x11.saver.interval, - _glfw.x11.saver.blanking, - _glfw.x11.saver.exposure); - } -} - -// Process the specified X event -// -static void processEvent(XEvent *event) -{ - int keycode = 0; - Bool filtered = False; - - // HACK: Save scancode as some IMs clear the field in XFilterEvent - if (event->type == KeyPress || event->type == KeyRelease) - keycode = event->xkey.keycode; - - if (_glfw.x11.im) - filtered = XFilterEvent(event, None); - - if (_glfw.x11.randr.available) - { - if (event->type == _glfw.x11.randr.eventBase + RRNotify) - { - XRRUpdateConfiguration(event); - _glfwPollMonitorsX11(); - return; - } - } - - if (_glfw.x11.xkb.available) - { - if (event->type == _glfw.x11.xkb.eventBase + XkbEventCode) - { - if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify && - (((XkbEvent*) event)->state.changed & XkbGroupStateMask)) - { - _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group; - } - - return; - } - } - - if (event->type == GenericEvent) - { - if (_glfw.x11.xi.available) - { - _GLFWwindow* window = _glfw.x11.disabledCursorWindow; - - if (window && - window->rawMouseMotion && - event->xcookie.extension == _glfw.x11.xi.majorOpcode && - XGetEventData(_glfw.x11.display, &event->xcookie) && - event->xcookie.evtype == XI_RawMotion) - { - XIRawEvent* re = event->xcookie.data; - if (re->valuators.mask_len) - { - const double* values = re->raw_values; - double xpos = window->virtualCursorPosX; - double ypos = window->virtualCursorPosY; - - if (XIMaskIsSet(re->valuators.mask, 0)) - { - xpos += *values; - values++; - } - - if (XIMaskIsSet(re->valuators.mask, 1)) - ypos += *values; - - _glfwInputCursorPos(window, xpos, ypos); - } - } - - XFreeEventData(_glfw.x11.display, &event->xcookie); - } - - return; - } - - if (event->type == SelectionRequest) - { - handleSelectionRequest(event); - return; - } - - _GLFWwindow* window = NULL; - if (XFindContext(_glfw.x11.display, - event->xany.window, - _glfw.x11.context, - (XPointer*) &window) != 0) - { - // This is an event for a window that has already been destroyed - return; - } - - switch (event->type) - { - case ReparentNotify: - { - window->x11.parent = event->xreparent.parent; - return; - } - - case KeyPress: - { - const int key = translateKey(keycode); - const int mods = translateState(event->xkey.state); - const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - - if (window->x11.ic) - { - // HACK: Do not report the key press events duplicated by XIM - // Duplicate key releases are filtered out implicitly by - // the GLFW key repeat logic in _glfwInputKey - // A timestamp per key is used to handle simultaneous keys - // NOTE: Always allow the first event for each key through - // (the server never sends a timestamp of zero) - // NOTE: Timestamp difference is compared to handle wrap-around - Time diff = event->xkey.time - window->x11.keyPressTimes[keycode]; - if (diff == event->xkey.time || (diff > 0 && diff < ((Time)1 << 31))) - { - if (keycode) - _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); - - window->x11.keyPressTimes[keycode] = event->xkey.time; - } - - if (!filtered) - { - int count; - Status status; -#if defined(X_HAVE_UTF8_STRING) - char buffer[100]; - char* chars = buffer; - - count = Xutf8LookupString(window->x11.ic, - &event->xkey, - buffer, sizeof(buffer) - 1, - NULL, &status); - - if (status == XBufferOverflow) - { - chars = calloc(count + 1, 1); - count = Xutf8LookupString(window->x11.ic, - &event->xkey, - chars, count, - NULL, &status); - } - - if (status == XLookupChars || status == XLookupBoth) - { - const char* c = chars; - chars[count] = '\0'; - while (c - chars < count) - _glfwInputChar(window, decodeUTF8(&c), mods, plain); - } -#else /*X_HAVE_UTF8_STRING*/ - wchar_t buffer[16]; - wchar_t* chars = buffer; - - count = XwcLookupString(window->x11.ic, - &event->xkey, - buffer, - sizeof(buffer) / sizeof(wchar_t), - NULL, - &status); - - if (status == XBufferOverflow) - { - chars = calloc(count, sizeof(wchar_t)); - count = XwcLookupString(window->x11.ic, - &event->xkey, - chars, count, - NULL, &status); - } - - if (status == XLookupChars || status == XLookupBoth) - { - int i; - for (i = 0; i < count; i++) - _glfwInputChar(window, chars[i], mods, plain); - } -#endif /*X_HAVE_UTF8_STRING*/ - - if (chars != buffer) - free(chars); - } - } - else - { - KeySym keysym; - XLookupString(&event->xkey, NULL, 0, &keysym, NULL); - - _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); - - const uint32_t codepoint = _glfwKeySym2Unicode(keysym); - if (codepoint != GLFW_INVALID_CODEPOINT) - _glfwInputChar(window, codepoint, mods, plain); - } - - return; - } - - case KeyRelease: - { - const int key = translateKey(keycode); - const int mods = translateState(event->xkey.state); - - if (!_glfw.x11.xkb.detectable) - { - // HACK: Key repeat events will arrive as KeyRelease/KeyPress - // pairs with similar or identical time stamps - // The key repeat logic in _glfwInputKey expects only key - // presses to repeat, so detect and discard release events - if (XEventsQueued(_glfw.x11.display, QueuedAfterReading)) - { - XEvent next; - XPeekEvent(_glfw.x11.display, &next); - - if (next.type == KeyPress && - next.xkey.window == event->xkey.window && - next.xkey.keycode == keycode) - { - // HACK: The time of repeat events sometimes doesn't - // match that of the press event, so add an - // epsilon - // Toshiyuki Takahashi can press a button - // 16 times per second so it's fairly safe to - // assume that no human is pressing the key 50 - // times per second (value is ms) - if ((next.xkey.time - event->xkey.time) < 20) - { - // This is very likely a server-generated key repeat - // event, so ignore it - return; - } - } - } - } - - _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods); - return; - } - - case ButtonPress: - { - const int mods = translateState(event->xbutton.state); - - if (event->xbutton.button == Button1) - _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); - else if (event->xbutton.button == Button2) - _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods); - else if (event->xbutton.button == Button3) - _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods); - - // Modern X provides scroll events as mouse button presses - else if (event->xbutton.button == Button4) - _glfwInputScroll(window, 0.0, 1.0); - else if (event->xbutton.button == Button5) - _glfwInputScroll(window, 0.0, -1.0); - else if (event->xbutton.button == Button6) - _glfwInputScroll(window, 1.0, 0.0); - else if (event->xbutton.button == Button7) - _glfwInputScroll(window, -1.0, 0.0); - - else - { - // Additional buttons after 7 are treated as regular buttons - // We subtract 4 to fill the gap left by scroll input above - _glfwInputMouseClick(window, - event->xbutton.button - Button1 - 4, - GLFW_PRESS, - mods); - } - - return; - } - - case ButtonRelease: - { - const int mods = translateState(event->xbutton.state); - - if (event->xbutton.button == Button1) - { - _glfwInputMouseClick(window, - GLFW_MOUSE_BUTTON_LEFT, - GLFW_RELEASE, - mods); - } - else if (event->xbutton.button == Button2) - { - _glfwInputMouseClick(window, - GLFW_MOUSE_BUTTON_MIDDLE, - GLFW_RELEASE, - mods); - } - else if (event->xbutton.button == Button3) - { - _glfwInputMouseClick(window, - GLFW_MOUSE_BUTTON_RIGHT, - GLFW_RELEASE, - mods); - } - else if (event->xbutton.button > Button7) - { - // Additional buttons after 7 are treated as regular buttons - // We subtract 4 to fill the gap left by scroll input above - _glfwInputMouseClick(window, - event->xbutton.button - Button1 - 4, - GLFW_RELEASE, - mods); - } - - return; - } - - case EnterNotify: - { - // XEnterWindowEvent is XCrossingEvent - const int x = event->xcrossing.x; - const int y = event->xcrossing.y; - - // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise - // ignore the defined cursor for hidden cursor mode - if (window->cursorMode == GLFW_CURSOR_HIDDEN) - updateCursorImage(window); - - _glfwInputCursorEnter(window, GLFW_TRUE); - _glfwInputCursorPos(window, x, y); - - window->x11.lastCursorPosX = x; - window->x11.lastCursorPosY = y; - return; - } - - case LeaveNotify: - { - _glfwInputCursorEnter(window, GLFW_FALSE); - return; - } - - case MotionNotify: - { - const int x = event->xmotion.x; - const int y = event->xmotion.y; - - if (x != window->x11.warpCursorPosX || - y != window->x11.warpCursorPosY) - { - // The cursor was moved by something other than GLFW - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - if (_glfw.x11.disabledCursorWindow != window) - return; - if (window->rawMouseMotion) - return; - - const int dx = x - window->x11.lastCursorPosX; - const int dy = y - window->x11.lastCursorPosY; - - _glfwInputCursorPos(window, - window->virtualCursorPosX + dx, - window->virtualCursorPosY + dy); - } - else - _glfwInputCursorPos(window, x, y); - } - - window->x11.lastCursorPosX = x; - window->x11.lastCursorPosY = y; - return; - } - - case ConfigureNotify: - { - if (event->xconfigure.width != window->x11.width || - event->xconfigure.height != window->x11.height) - { - _glfwInputFramebufferSize(window, - event->xconfigure.width, - event->xconfigure.height); - - _glfwInputWindowSize(window, - event->xconfigure.width, - event->xconfigure.height); - - window->x11.width = event->xconfigure.width; - window->x11.height = event->xconfigure.height; - } - - int xpos = event->xconfigure.x; - int ypos = event->xconfigure.y; - - // NOTE: ConfigureNotify events from the server are in local - // coordinates, so if we are reparented we need to translate - // the position into root (screen) coordinates - if (!event->xany.send_event && window->x11.parent != _glfw.x11.root) - { - _glfwGrabErrorHandlerX11(); - - Window dummy; - XTranslateCoordinates(_glfw.x11.display, - window->x11.parent, - _glfw.x11.root, - xpos, ypos, - &xpos, &ypos, - &dummy); - - _glfwReleaseErrorHandlerX11(); - if (_glfw.x11.errorCode == BadWindow) - return; - } - - if (xpos != window->x11.xpos || ypos != window->x11.ypos) - { - _glfwInputWindowPos(window, xpos, ypos); - window->x11.xpos = xpos; - window->x11.ypos = ypos; - } - - return; - } - - case ClientMessage: - { - // Custom client message, probably from the window manager - - if (filtered) - return; - - if (event->xclient.message_type == None) - return; - - if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS) - { - const Atom protocol = event->xclient.data.l[0]; - if (protocol == None) - return; - - if (protocol == _glfw.x11.WM_DELETE_WINDOW) - { - // The window manager was asked to close the window, for - // example by the user pressing a 'close' window decoration - // button - _glfwInputWindowCloseRequest(window); - } - else if (protocol == _glfw.x11.NET_WM_PING) - { - // The window manager is pinging the application to ensure - // it's still responding to events - - XEvent reply = *event; - reply.xclient.window = _glfw.x11.root; - - XSendEvent(_glfw.x11.display, _glfw.x11.root, - False, - SubstructureNotifyMask | SubstructureRedirectMask, - &reply); - } - } - else if (event->xclient.message_type == _glfw.x11.XdndEnter) - { - // A drag operation has entered the window - unsigned long i, count; - Atom* formats = NULL; - const GLFWbool list = event->xclient.data.l[1] & 1; - - _glfw.x11.xdnd.source = event->xclient.data.l[0]; - _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24; - _glfw.x11.xdnd.format = None; - - if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) - return; - - if (list) - { - count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source, - _glfw.x11.XdndTypeList, - XA_ATOM, - (unsigned char**) &formats); - } - else - { - count = 3; - formats = (Atom*) event->xclient.data.l + 2; - } - - for (i = 0; i < count; i++) - { - if (formats[i] == _glfw.x11.text_uri_list) - { - _glfw.x11.xdnd.format = _glfw.x11.text_uri_list; - break; - } - } - - if (list && formats) - XFree(formats); - } - else if (event->xclient.message_type == _glfw.x11.XdndDrop) - { - // The drag operation has finished by dropping on the window - Time time = CurrentTime; - - if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) - return; - - if (_glfw.x11.xdnd.format) - { - if (_glfw.x11.xdnd.version >= 1) - time = event->xclient.data.l[2]; - - // Request the chosen format from the source window - XConvertSelection(_glfw.x11.display, - _glfw.x11.XdndSelection, - _glfw.x11.xdnd.format, - _glfw.x11.XdndSelection, - window->x11.handle, - time); - } - else if (_glfw.x11.xdnd.version >= 2) - { - XEvent reply = { ClientMessage }; - reply.xclient.window = _glfw.x11.xdnd.source; - reply.xclient.message_type = _glfw.x11.XdndFinished; - reply.xclient.format = 32; - reply.xclient.data.l[0] = window->x11.handle; - reply.xclient.data.l[1] = 0; // The drag was rejected - reply.xclient.data.l[2] = None; - - XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, - False, NoEventMask, &reply); - XFlush(_glfw.x11.display); - } - } - else if (event->xclient.message_type == _glfw.x11.XdndPosition) - { - // The drag operation has moved over the window - const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff; - const int yabs = (event->xclient.data.l[2]) & 0xffff; - Window dummy; - int xpos, ypos; - - if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) - return; - - XTranslateCoordinates(_glfw.x11.display, - _glfw.x11.root, - window->x11.handle, - xabs, yabs, - &xpos, &ypos, - &dummy); - - _glfwInputCursorPos(window, xpos, ypos); - - XEvent reply = { ClientMessage }; - reply.xclient.window = _glfw.x11.xdnd.source; - reply.xclient.message_type = _glfw.x11.XdndStatus; - reply.xclient.format = 32; - reply.xclient.data.l[0] = window->x11.handle; - reply.xclient.data.l[2] = 0; // Specify an empty rectangle - reply.xclient.data.l[3] = 0; - - if (_glfw.x11.xdnd.format) - { - // Reply that we are ready to copy the dragged data - reply.xclient.data.l[1] = 1; // Accept with no rectangle - if (_glfw.x11.xdnd.version >= 2) - reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; - } - - XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, - False, NoEventMask, &reply); - XFlush(_glfw.x11.display); - } - - return; - } - - case SelectionNotify: - { - if (event->xselection.property == _glfw.x11.XdndSelection) - { - // The converted data from the drag operation has arrived - char* data; - const unsigned long result = - _glfwGetWindowPropertyX11(event->xselection.requestor, - event->xselection.property, - event->xselection.target, - (unsigned char**) &data); - - if (result) - { - int i, count; - char** paths = _glfwParseUriList(data, &count); - - _glfwInputDrop(window, count, (const char**) paths); - - for (i = 0; i < count; i++) - free(paths[i]); - free(paths); - } - - if (data) - XFree(data); - - if (_glfw.x11.xdnd.version >= 2) - { - XEvent reply = { ClientMessage }; - reply.xclient.window = _glfw.x11.xdnd.source; - reply.xclient.message_type = _glfw.x11.XdndFinished; - reply.xclient.format = 32; - reply.xclient.data.l[0] = window->x11.handle; - reply.xclient.data.l[1] = result; - reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; - - XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, - False, NoEventMask, &reply); - XFlush(_glfw.x11.display); - } - } - - return; - } - - case FocusIn: - { - if (event->xfocus.mode == NotifyGrab || - event->xfocus.mode == NotifyUngrab) - { - // Ignore focus events from popup indicator windows, window menu - // key chords and window dragging - return; - } - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - disableCursor(window); - - if (window->x11.ic) - XSetICFocus(window->x11.ic); - - _glfwInputWindowFocus(window, GLFW_TRUE); - return; - } - - case FocusOut: - { - if (event->xfocus.mode == NotifyGrab || - event->xfocus.mode == NotifyUngrab) - { - // Ignore focus events from popup indicator windows, window menu - // key chords and window dragging - return; - } - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - enableCursor(window); - - if (window->x11.ic) - XUnsetICFocus(window->x11.ic); - - if (window->monitor && window->autoIconify) - _glfwPlatformIconifyWindow(window); - - _glfwInputWindowFocus(window, GLFW_FALSE); - return; - } - - case Expose: - { - _glfwInputWindowDamage(window); - return; - } - - case PropertyNotify: - { - if (event->xproperty.state != PropertyNewValue) - return; - - if (event->xproperty.atom == _glfw.x11.WM_STATE) - { - const int state = getWindowState(window); - if (state != IconicState && state != NormalState) - return; - - const GLFWbool iconified = (state == IconicState); - if (window->x11.iconified != iconified) - { - if (window->monitor) - { - if (iconified) - releaseMonitor(window); - else - acquireMonitor(window); - } - - window->x11.iconified = iconified; - _glfwInputWindowIconify(window, iconified); - } - } - else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE) - { - const GLFWbool maximized = _glfwPlatformWindowMaximized(window); - if (window->x11.maximized != maximized) - { - window->x11.maximized = maximized; - _glfwInputWindowMaximize(window, maximized); - } - } - - return; - } - - case DestroyNotify: - return; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Retrieve a single window property of the specified type -// Inspired by fghGetWindowProperty from freeglut -// -unsigned long _glfwGetWindowPropertyX11(Window window, - Atom property, - Atom type, - unsigned char** value) -{ - Atom actualType; - int actualFormat; - unsigned long itemCount, bytesAfter; - - XGetWindowProperty(_glfw.x11.display, - window, - property, - 0, - LONG_MAX, - False, - type, - &actualType, - &actualFormat, - &itemCount, - &bytesAfter, - value); - - return itemCount; -} - -GLFWbool _glfwIsVisualTransparentX11(Visual* visual) -{ - if (!_glfw.x11.xrender.available) - return GLFW_FALSE; - - XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual); - return pf && pf->direct.alphaMask; -} - -// Push contents of our selection to clipboard manager -// -void _glfwPushSelectionToManagerX11(void) -{ - XConvertSelection(_glfw.x11.display, - _glfw.x11.CLIPBOARD_MANAGER, - _glfw.x11.SAVE_TARGETS, - None, - _glfw.x11.helperWindowHandle, - CurrentTime); - - for (;;) - { - XEvent event; - - while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) - { - switch (event.type) - { - case SelectionRequest: - handleSelectionRequest(&event); - break; - - case SelectionNotify: - { - if (event.xselection.target == _glfw.x11.SAVE_TARGETS) - { - // This means one of two things; either the selection - // was not owned, which means there is no clipboard - // manager, or the transfer to the clipboard manager has - // completed - // In either case, it means we are done here - return; - } - - break; - } - } - } - - waitForX11Event(NULL); - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - Visual* visual = NULL; - int depth; - - if (ctxconfig->client != GLFW_NO_API) - { - if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) - { - if (!_glfwInitGLX()) - return GLFW_FALSE; - if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth)) - return GLFW_FALSE; - } - else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) - { - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth)) - return GLFW_FALSE; - } - else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) - { - if (!_glfwInitOSMesa()) - return GLFW_FALSE; - } - } - - if (!visual) - { - visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); - depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); - } - - if (!createNativeWindow(window, wndconfig, visual, depth)) - return GLFW_FALSE; - - if (ctxconfig->client != GLFW_NO_API) - { - if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) - { - if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) - { - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) - { - if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - - if (!_glfwRefreshContextAttribs(window, ctxconfig)) - return GLFW_FALSE; - } - - if (window->monitor) - { - _glfwPlatformShowWindow(window); - updateWindowMode(window); - acquireMonitor(window); - - if (wndconfig->centerCursor) - _glfwCenterCursorInContentArea(window); - } - else - { - if (wndconfig->visible) - { - _glfwPlatformShowWindow(window); - if (wndconfig->focused) - _glfwPlatformFocusWindow(window); - } - } - - XFlush(_glfw.x11.display); - return GLFW_TRUE; -} - -void _glfwPlatformDestroyWindow(_GLFWwindow* window) -{ - if (_glfw.x11.disabledCursorWindow == window) - _glfw.x11.disabledCursorWindow = NULL; - - if (window->monitor) - releaseMonitor(window); - - if (window->x11.ic) - { - XDestroyIC(window->x11.ic); - window->x11.ic = NULL; - } - - if (window->context.destroy) - window->context.destroy(window); - - if (window->x11.handle) - { - XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context); - XUnmapWindow(_glfw.x11.display, window->x11.handle); - XDestroyWindow(_glfw.x11.display, window->x11.handle); - window->x11.handle = (Window) 0; - } - - if (window->x11.colormap) - { - XFreeColormap(_glfw.x11.display, window->x11.colormap); - window->x11.colormap = (Colormap) 0; - } - - XFlush(_glfw.x11.display); -} - -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) -{ -#if defined(X_HAVE_UTF8_STRING) - Xutf8SetWMProperties(_glfw.x11.display, - window->x11.handle, - title, title, - NULL, 0, - NULL, NULL, NULL); -#else - // This may be a slightly better fallback than using XStoreName and - // XSetIconName, which always store their arguments using STRING - XmbSetWMProperties(_glfw.x11.display, - window->x11.handle, - title, title, - NULL, 0, - NULL, NULL, NULL); -#endif - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8, - PropModeReplace, - (unsigned char*) title, strlen(title)); - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8, - PropModeReplace, - (unsigned char*) title, strlen(title)); - - XFlush(_glfw.x11.display); -} - -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images) -{ - if (count) - { - int i, j, longCount = 0; - - for (i = 0; i < count; i++) - longCount += 2 + images[i].width * images[i].height; - - unsigned long* icon = calloc(longCount, sizeof(unsigned long)); - unsigned long* target = icon; - - for (i = 0; i < count; i++) - { - *target++ = images[i].width; - *target++ = images[i].height; - - for (j = 0; j < images[i].width * images[i].height; j++) - { - *target++ = (((unsigned long) images[i].pixels[j * 4 + 0]) << 16) | - (((unsigned long) images[i].pixels[j * 4 + 1]) << 8) | - (((unsigned long) images[i].pixels[j * 4 + 2]) << 0) | - (((unsigned long) images[i].pixels[j * 4 + 3]) << 24); - } - } - - // NOTE: XChangeProperty expects 32-bit values like the image data above to be - // placed in the 32 least significant bits of individual longs. This is - // true even if long is 64-bit and a WM protocol calls for "packed" data. - // This is because of a historical mistake that then became part of the Xlib - // ABI. Xlib will pack these values into a regular array of 32-bit values - // before sending it over the wire. - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_ICON, - XA_CARDINAL, 32, - PropModeReplace, - (unsigned char*) icon, - longCount); - - free(icon); - } - else - { - XDeleteProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_ICON); - } - - XFlush(_glfw.x11.display); -} - -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) -{ - Window dummy; - int x, y; - - XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root, - 0, 0, &x, &y, &dummy); - - if (xpos) - *xpos = x; - if (ypos) - *ypos = y; -} - -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) -{ - // HACK: Explicitly setting PPosition to any value causes some WMs, notably - // Compiz and Metacity, to honor the position of unmapped windows - if (!_glfwPlatformWindowVisible(window)) - { - long supplied; - XSizeHints* hints = XAllocSizeHints(); - - if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied)) - { - hints->flags |= PPosition; - hints->x = hints->y = 0; - - XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); - } - - XFree(hints); - } - - XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos); - XFlush(_glfw.x11.display); -} - -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) -{ - XWindowAttributes attribs; - XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs); - - if (width) - *width = attribs.width; - if (height) - *height = attribs.height; -} - -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) -{ - if (window->monitor) - { - if (window->monitor->window == window) - acquireMonitor(window); - } - else - { - if (!window->resizable) - updateNormalHints(window, width, height); - - XResizeWindow(_glfw.x11.display, window->x11.handle, width, height); - } - - XFlush(_glfw.x11.display); -} - -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) -{ - int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - updateNormalHints(window, width, height); - XFlush(_glfw.x11.display); -} - -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) -{ - int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - updateNormalHints(window, width, height); - XFlush(_glfw.x11.display); -} - -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) -{ - _glfwPlatformGetWindowSize(window, width, height); -} - -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) -{ - long* extents = NULL; - - if (window->monitor || !window->decorated) - return; - - if (_glfw.x11.NET_FRAME_EXTENTS == None) - return; - - if (!_glfwPlatformWindowVisible(window) && - _glfw.x11.NET_REQUEST_FRAME_EXTENTS) - { - XEvent event; - double timeout = 0.5; - - // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to - // function before the window is mapped - sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS, - 0, 0, 0, 0, 0); - - // HACK: Use a timeout because earlier versions of some window managers - // (at least Unity, Fluxbox and Xfwm) failed to send the reply - // They have been fixed but broken versions are still in the wild - // If you are affected by this and your window manager is NOT - // listed above, PLEASE report it to their and our issue trackers - while (!XCheckIfEvent(_glfw.x11.display, - &event, - isFrameExtentsEvent, - (XPointer) window)) - { - if (!waitForX11Event(&timeout)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue"); - return; - } - } - } - - if (_glfwGetWindowPropertyX11(window->x11.handle, - _glfw.x11.NET_FRAME_EXTENTS, - XA_CARDINAL, - (unsigned char**) &extents) == 4) - { - if (left) - *left = extents[0]; - if (top) - *top = extents[2]; - if (right) - *right = extents[1]; - if (bottom) - *bottom = extents[3]; - } - - if (extents) - XFree(extents); -} - -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) -{ - if (xscale) - *xscale = _glfw.x11.contentScaleX; - if (yscale) - *yscale = _glfw.x11.contentScaleY; -} - -void _glfwPlatformIconifyWindow(_GLFWwindow* window) -{ - if (window->x11.overrideRedirect) - { - // Override-redirect windows cannot be iconified or restored, as those - // tasks are performed by the window manager - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); - return; - } - - XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen); - XFlush(_glfw.x11.display); -} - -void _glfwPlatformRestoreWindow(_GLFWwindow* window) -{ - if (window->x11.overrideRedirect) - { - // Override-redirect windows cannot be iconified or restored, as those - // tasks are performed by the window manager - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); - return; - } - - if (_glfwPlatformWindowIconified(window)) - { - XMapWindow(_glfw.x11.display, window->x11.handle); - waitForVisibilityNotify(window); - } - else if (_glfwPlatformWindowVisible(window)) - { - if (_glfw.x11.NET_WM_STATE && - _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && - _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) - { - sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - _NET_WM_STATE_REMOVE, - _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, - _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, - 1, 0); - } - } - - XFlush(_glfw.x11.display); -} - -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) -{ - if (!_glfw.x11.NET_WM_STATE || - !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || - !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) - { - return; - } - - if (_glfwPlatformWindowVisible(window)) - { - sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - _NET_WM_STATE_ADD, - _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, - _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, - 1, 0); - } - else - { - Atom* states = NULL; - unsigned long count = - _glfwGetWindowPropertyX11(window->x11.handle, - _glfw.x11.NET_WM_STATE, - XA_ATOM, - (unsigned char**) &states); - - // NOTE: We don't check for failure as this property may not exist yet - // and that's fine (and we'll create it implicitly with append) - - Atom missing[2] = - { - _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, - _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ - }; - unsigned long missingCount = 2; - - for (unsigned long i = 0; i < count; i++) - { - for (unsigned long j = 0; j < missingCount; j++) - { - if (states[i] == missing[j]) - { - missing[j] = missing[missingCount - 1]; - missingCount--; - } - } - } - - if (states) - XFree(states); - - if (!missingCount) - return; - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_STATE, XA_ATOM, 32, - PropModeAppend, - (unsigned char*) missing, - missingCount); - } - - XFlush(_glfw.x11.display); -} - -void _glfwPlatformShowWindow(_GLFWwindow* window) -{ - if (_glfwPlatformWindowVisible(window)) - return; - - XMapWindow(_glfw.x11.display, window->x11.handle); - waitForVisibilityNotify(window); -} - -void _glfwPlatformHideWindow(_GLFWwindow* window) -{ - XUnmapWindow(_glfw.x11.display, window->x11.handle); - XFlush(_glfw.x11.display); -} - -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) -{ - if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION) - return; - - sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - _NET_WM_STATE_ADD, - _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION, - 0, 1, 0); -} - -void _glfwPlatformFocusWindow(_GLFWwindow* window) -{ - if (_glfw.x11.NET_ACTIVE_WINDOW) - sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); - else if (_glfwPlatformWindowVisible(window)) - { - XRaiseWindow(_glfw.x11.display, window->x11.handle); - XSetInputFocus(_glfw.x11.display, window->x11.handle, - RevertToParent, CurrentTime); - } - - XFlush(_glfw.x11.display); -} - -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) -{ - if (window->monitor == monitor) - { - if (monitor) - { - if (monitor->window == window) - acquireMonitor(window); - } - else - { - if (!window->resizable) - updateNormalHints(window, width, height); - - XMoveResizeWindow(_glfw.x11.display, window->x11.handle, - xpos, ypos, width, height); - } - - XFlush(_glfw.x11.display); - return; - } - - if (window->monitor) - { - _glfwPlatformSetWindowDecorated(window, window->decorated); - _glfwPlatformSetWindowFloating(window, window->floating); - releaseMonitor(window); - } - - _glfwInputWindowMonitor(window, monitor); - updateNormalHints(window, width, height); - - if (window->monitor) - { - if (!_glfwPlatformWindowVisible(window)) - { - XMapRaised(_glfw.x11.display, window->x11.handle); - waitForVisibilityNotify(window); - } - - updateWindowMode(window); - acquireMonitor(window); - } - else - { - updateWindowMode(window); - XMoveResizeWindow(_glfw.x11.display, window->x11.handle, - xpos, ypos, width, height); - } - - XFlush(_glfw.x11.display); -} - -int _glfwPlatformWindowFocused(_GLFWwindow* window) -{ - Window focused; - int state; - - XGetInputFocus(_glfw.x11.display, &focused, &state); - return window->x11.handle == focused; -} - -int _glfwPlatformWindowIconified(_GLFWwindow* window) -{ - return getWindowState(window) == IconicState; -} - -int _glfwPlatformWindowVisible(_GLFWwindow* window) -{ - XWindowAttributes wa; - XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa); - return wa.map_state == IsViewable; -} - -int _glfwPlatformWindowMaximized(_GLFWwindow* window) -{ - Atom* states; - unsigned long i; - GLFWbool maximized = GLFW_FALSE; - - if (!_glfw.x11.NET_WM_STATE || - !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || - !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) - { - return maximized; - } - - const unsigned long count = - _glfwGetWindowPropertyX11(window->x11.handle, - _glfw.x11.NET_WM_STATE, - XA_ATOM, - (unsigned char**) &states); - - for (i = 0; i < count; i++) - { - if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || - states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) - { - maximized = GLFW_TRUE; - break; - } - } - - if (states) - XFree(states); - - return maximized; -} - -int _glfwPlatformWindowHovered(_GLFWwindow* window) -{ - Window w = _glfw.x11.root; - while (w) - { - Window root; - int rootX, rootY, childX, childY; - unsigned int mask; - - _glfwGrabErrorHandlerX11(); - - const Bool result = XQueryPointer(_glfw.x11.display, w, - &root, &w, &rootX, &rootY, - &childX, &childY, &mask); - - _glfwReleaseErrorHandlerX11(); - - if (_glfw.x11.errorCode == BadWindow) - w = _glfw.x11.root; - else if (!result) - return GLFW_FALSE; - else if (w == window->x11.handle) - return GLFW_TRUE; - } - - return GLFW_FALSE; -} - -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) -{ - if (!window->x11.transparent) - return GLFW_FALSE; - - return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None; -} - -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) -{ - int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - updateNormalHints(window, width, height); -} - -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) -{ - struct - { - unsigned long flags; - unsigned long functions; - unsigned long decorations; - long input_mode; - unsigned long status; - } hints = {0}; - - hints.flags = MWM_HINTS_DECORATIONS; - hints.decorations = enabled ? MWM_DECOR_ALL : 0; - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.MOTIF_WM_HINTS, - _glfw.x11.MOTIF_WM_HINTS, 32, - PropModeReplace, - (unsigned char*) &hints, - sizeof(hints) / sizeof(long)); -} - -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) -{ - if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE) - return; - - if (_glfwPlatformWindowVisible(window)) - { - const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; - sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - action, - _glfw.x11.NET_WM_STATE_ABOVE, - 0, 1, 0); - } - else - { - Atom* states = NULL; - unsigned long i, count; - - count = _glfwGetWindowPropertyX11(window->x11.handle, - _glfw.x11.NET_WM_STATE, - XA_ATOM, - (unsigned char**) &states); - - // NOTE: We don't check for failure as this property may not exist yet - // and that's fine (and we'll create it implicitly with append) - - if (enabled) - { - for (i = 0; i < count; i++) - { - if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) - break; - } - - if (i == count) - { - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_STATE, XA_ATOM, 32, - PropModeAppend, - (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, - 1); - } - } - else if (states) - { - for (i = 0; i < count; i++) - { - if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) - break; - } - - if (i < count) - { - states[i] = states[count - 1]; - count--; - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_STATE, XA_ATOM, 32, - PropModeReplace, (unsigned char*) states, count); - } - } - - if (states) - XFree(states); - } - - XFlush(_glfw.x11.display); -} - -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) -{ - float opacity = 1.f; - - if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx)) - { - CARD32* value = NULL; - - if (_glfwGetWindowPropertyX11(window->x11.handle, - _glfw.x11.NET_WM_WINDOW_OPACITY, - XA_CARDINAL, - (unsigned char**) &value)) - { - opacity = (float) (*value / (double) 0xffffffffu); - } - - if (value) - XFree(value); - } - - return opacity; -} - -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) -{ - const CARD32 value = (CARD32) (0xffffffffu * (double) opacity); - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, - PropModeReplace, (unsigned char*) &value, 1); -} - -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) -{ - if (!_glfw.x11.xi.available) - return; - - if (_glfw.x11.disabledCursorWindow != window) - return; - - if (enabled) - enableRawMouseMotion(window); - else - disableRawMouseMotion(window); -} - -GLFWbool _glfwPlatformRawMouseMotionSupported(void) -{ - return _glfw.x11.xi.available; -} - -void _glfwPlatformPollEvents(void) -{ - drainEmptyEvents(); - -#if defined(__linux__) - _glfwDetectJoystickConnectionLinux(); -#endif - XPending(_glfw.x11.display); - - while (XQLength(_glfw.x11.display)) - { - XEvent event; - XNextEvent(_glfw.x11.display, &event); - processEvent(&event); - } - - _GLFWwindow* window = _glfw.x11.disabledCursorWindow; - if (window) - { - int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - - // NOTE: Re-center the cursor only if it has moved since the last call, - // to avoid breaking glfwWaitEvents with MotionNotify - if (window->x11.lastCursorPosX != width / 2 || - window->x11.lastCursorPosY != height / 2) - { - _glfwPlatformSetCursorPos(window, width / 2, height / 2); - } - } - - XFlush(_glfw.x11.display); -} - -void _glfwPlatformWaitEvents(void) -{ - waitForAnyEvent(NULL); - _glfwPlatformPollEvents(); -} - -void _glfwPlatformWaitEventsTimeout(double timeout) -{ - waitForAnyEvent(&timeout); - _glfwPlatformPollEvents(); -} - -void _glfwPlatformPostEmptyEvent(void) -{ - writeEmptyEvent(); -} - -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) -{ - Window root, child; - int rootX, rootY, childX, childY; - unsigned int mask; - - XQueryPointer(_glfw.x11.display, window->x11.handle, - &root, &child, - &rootX, &rootY, &childX, &childY, - &mask); - - if (xpos) - *xpos = childX; - if (ypos) - *ypos = childY; -} - -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) -{ - // Store the new position so it can be recognized later - window->x11.warpCursorPosX = (int) x; - window->x11.warpCursorPosY = (int) y; - - XWarpPointer(_glfw.x11.display, None, window->x11.handle, - 0,0,0,0, (int) x, (int) y); - XFlush(_glfw.x11.display); -} - -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) -{ - if (mode == GLFW_CURSOR_DISABLED) - { - if (_glfwPlatformWindowFocused(window)) - disableCursor(window); - } - else if (_glfw.x11.disabledCursorWindow == window) - enableCursor(window); - else - updateCursorImage(window); - - XFlush(_glfw.x11.display); -} - -const char* _glfwPlatformGetScancodeName(int scancode) -{ - if (!_glfw.x11.xkb.available) - return NULL; - - if (scancode < 0 || scancode > 0xff || - _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN) - { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); - return NULL; - } - - const int key = _glfw.x11.keycodes[scancode]; - const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, - scancode, _glfw.x11.xkb.group, 0); - if (keysym == NoSymbol) - return NULL; - - const uint32_t codepoint = _glfwKeySym2Unicode(keysym); - if (codepoint == GLFW_INVALID_CODEPOINT) - return NULL; - - const size_t count = _glfwEncodeUTF8(_glfw.x11.keynames[key], codepoint); - if (count == 0) - return NULL; - - _glfw.x11.keynames[key][count] = '\0'; - return _glfw.x11.keynames[key]; -} - -int _glfwPlatformGetKeyScancode(int key) -{ - return _glfw.x11.scancodes[key]; -} - -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) -{ - cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot); - if (!cursor->x11.handle) - return GLFW_FALSE; - - return GLFW_TRUE; -} - -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) -{ - int native = 0; - - if (shape == GLFW_ARROW_CURSOR) - native = XC_left_ptr; - else if (shape == GLFW_IBEAM_CURSOR) - native = XC_xterm; - else if (shape == GLFW_CROSSHAIR_CURSOR) - native = XC_crosshair; - else if (shape == GLFW_HAND_CURSOR) - native = XC_hand2; - else if (shape == GLFW_HRESIZE_CURSOR) - native = XC_sb_h_double_arrow; - else if (shape == GLFW_VRESIZE_CURSOR) - native = XC_sb_v_double_arrow; - else - return GLFW_FALSE; - - cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); - if (!cursor->x11.handle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to create standard cursor"); - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) -{ - if (cursor->x11.handle) - XFreeCursor(_glfw.x11.display, cursor->x11.handle); -} - -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) -{ - if (window->cursorMode == GLFW_CURSOR_NORMAL) - { - updateCursorImage(window); - XFlush(_glfw.x11.display); - } -} - -void _glfwPlatformSetClipboardString(const char* string) -{ - char* copy = _glfw_strdup(string); - free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = copy; - - XSetSelectionOwner(_glfw.x11.display, - _glfw.x11.CLIPBOARD, - _glfw.x11.helperWindowHandle, - CurrentTime); - - if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) != - _glfw.x11.helperWindowHandle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to become owner of clipboard selection"); - } -} - -const char* _glfwPlatformGetClipboardString(void) -{ - return getSelectionString(_glfw.x11.CLIPBOARD); -} - -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) -{ - if (!_glfw.vk.KHR_surface) - return; - - if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle) - { - if (!_glfw.vk.KHR_xlib_surface) - return; - } - - extensions[0] = "VK_KHR_surface"; - - // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but - // not correctly implementing VK_KHR_xlib_surface - if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) - extensions[1] = "VK_KHR_xcb_surface"; - else - extensions[1] = "VK_KHR_xlib_surface"; -} - -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) -{ - VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display, - _glfw.x11.screen)); - - if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) - { - PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR - vkGetPhysicalDeviceXcbPresentationSupportKHR = - (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); - if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); - return GLFW_FALSE; - } - - xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); - if (!connection) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to retrieve XCB connection"); - return GLFW_FALSE; - } - - return vkGetPhysicalDeviceXcbPresentationSupportKHR(device, - queuefamily, - connection, - visualID); - } - else - { - PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR - vkGetPhysicalDeviceXlibPresentationSupportKHR = - (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); - if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); - return GLFW_FALSE; - } - - return vkGetPhysicalDeviceXlibPresentationSupportKHR(device, - queuefamily, - _glfw.x11.display, - visualID); - } -} - -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) -{ - if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) - { - VkResult err; - VkXcbSurfaceCreateInfoKHR sci; - PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; - - xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); - if (!connection) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to retrieve XCB connection"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR) - vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR"); - if (!vkCreateXcbSurfaceKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - memset(&sci, 0, sizeof(sci)); - sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; - sci.connection = connection; - sci.window = window->x11.handle; - - err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface); - if (err) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to create Vulkan XCB surface: %s", - _glfwGetVulkanResultString(err)); - } - - return err; - } - else - { - VkResult err; - VkXlibSurfaceCreateInfoKHR sci; - PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; - - vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR) - vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR"); - if (!vkCreateXlibSurfaceKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - memset(&sci, 0, sizeof(sci)); - sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - sci.dpy = _glfw.x11.display; - sci.window = window->x11.handle; - - err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface); - if (err) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to create Vulkan X11 surface: %s", - _glfwGetVulkanResultString(err)); - } - - return err; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI Display* glfwGetX11Display(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfw.x11.display; -} - -GLFWAPI Window glfwGetX11Window(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(None); - return window->x11.handle; -} - -GLFWAPI void glfwSetX11SelectionString(const char* string) -{ - _GLFW_REQUIRE_INIT(); - - free(_glfw.x11.primarySelectionString); - _glfw.x11.primarySelectionString = _glfw_strdup(string); - - XSetSelectionOwner(_glfw.x11.display, - _glfw.x11.PRIMARY, - _glfw.x11.helperWindowHandle, - CurrentTime); - - if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) != - _glfw.x11.helperWindowHandle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to become owner of primary selection"); - } -} - -GLFWAPI const char* glfwGetX11SelectionString(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return getSelectionString(_glfw.x11.PRIMARY); -} - diff --git a/libs/zglfw/libs/glfw/src/xkb_unicode.c b/libs/zglfw/libs/glfw/src/xkb_unicode.c deleted file mode 100644 index 859bedcae..000000000 --- a/libs/zglfw/libs/glfw/src/xkb_unicode.c +++ /dev/null @@ -1,942 +0,0 @@ -//======================================================================== -// GLFW 3.3 X11 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== -// It is fine to use C99 in this file because it will not be built with VS -//======================================================================== - -#include "internal.h" - - -/* - * Marcus: This code was originally written by Markus G. Kuhn. - * I have made some slight changes (trimmed it down a bit from >60 KB to - * 20 KB), but the functionality is the same. - */ - -/* - * This module converts keysym values into the corresponding ISO 10646 - * (UCS, Unicode) values. - * - * The array keysymtab[] contains pairs of X11 keysym values for graphical - * characters and the corresponding Unicode value. The function - * _glfwKeySym2Unicode() maps a keysym onto a Unicode value using a binary - * search, therefore keysymtab[] must remain SORTED by keysym value. - * - * We allow to represent any UCS character in the range U-00000000 to - * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. - * This admittedly does not cover the entire 31-bit space of UCS, but - * it does cover all of the characters up to U-10FFFF, which can be - * represented by UTF-16, and more, and it is very unlikely that higher - * UCS codes will ever be assigned by ISO. So to get Unicode character - * U+ABCD you can directly use keysym 0x0100abcd. - * - * Original author: Markus G. Kuhn , University of - * Cambridge, April 2001 - * - * Special thanks to Richard Verhoeven for preparing - * an initial draft of the mapping table. - * - */ - - -//************************************************************************ -//**** KeySym to Unicode mapping table **** -//************************************************************************ - -static const struct codepair { - unsigned short keysym; - unsigned short ucs; -} keysymtab[] = { - { 0x01a1, 0x0104 }, - { 0x01a2, 0x02d8 }, - { 0x01a3, 0x0141 }, - { 0x01a5, 0x013d }, - { 0x01a6, 0x015a }, - { 0x01a9, 0x0160 }, - { 0x01aa, 0x015e }, - { 0x01ab, 0x0164 }, - { 0x01ac, 0x0179 }, - { 0x01ae, 0x017d }, - { 0x01af, 0x017b }, - { 0x01b1, 0x0105 }, - { 0x01b2, 0x02db }, - { 0x01b3, 0x0142 }, - { 0x01b5, 0x013e }, - { 0x01b6, 0x015b }, - { 0x01b7, 0x02c7 }, - { 0x01b9, 0x0161 }, - { 0x01ba, 0x015f }, - { 0x01bb, 0x0165 }, - { 0x01bc, 0x017a }, - { 0x01bd, 0x02dd }, - { 0x01be, 0x017e }, - { 0x01bf, 0x017c }, - { 0x01c0, 0x0154 }, - { 0x01c3, 0x0102 }, - { 0x01c5, 0x0139 }, - { 0x01c6, 0x0106 }, - { 0x01c8, 0x010c }, - { 0x01ca, 0x0118 }, - { 0x01cc, 0x011a }, - { 0x01cf, 0x010e }, - { 0x01d0, 0x0110 }, - { 0x01d1, 0x0143 }, - { 0x01d2, 0x0147 }, - { 0x01d5, 0x0150 }, - { 0x01d8, 0x0158 }, - { 0x01d9, 0x016e }, - { 0x01db, 0x0170 }, - { 0x01de, 0x0162 }, - { 0x01e0, 0x0155 }, - { 0x01e3, 0x0103 }, - { 0x01e5, 0x013a }, - { 0x01e6, 0x0107 }, - { 0x01e8, 0x010d }, - { 0x01ea, 0x0119 }, - { 0x01ec, 0x011b }, - { 0x01ef, 0x010f }, - { 0x01f0, 0x0111 }, - { 0x01f1, 0x0144 }, - { 0x01f2, 0x0148 }, - { 0x01f5, 0x0151 }, - { 0x01f8, 0x0159 }, - { 0x01f9, 0x016f }, - { 0x01fb, 0x0171 }, - { 0x01fe, 0x0163 }, - { 0x01ff, 0x02d9 }, - { 0x02a1, 0x0126 }, - { 0x02a6, 0x0124 }, - { 0x02a9, 0x0130 }, - { 0x02ab, 0x011e }, - { 0x02ac, 0x0134 }, - { 0x02b1, 0x0127 }, - { 0x02b6, 0x0125 }, - { 0x02b9, 0x0131 }, - { 0x02bb, 0x011f }, - { 0x02bc, 0x0135 }, - { 0x02c5, 0x010a }, - { 0x02c6, 0x0108 }, - { 0x02d5, 0x0120 }, - { 0x02d8, 0x011c }, - { 0x02dd, 0x016c }, - { 0x02de, 0x015c }, - { 0x02e5, 0x010b }, - { 0x02e6, 0x0109 }, - { 0x02f5, 0x0121 }, - { 0x02f8, 0x011d }, - { 0x02fd, 0x016d }, - { 0x02fe, 0x015d }, - { 0x03a2, 0x0138 }, - { 0x03a3, 0x0156 }, - { 0x03a5, 0x0128 }, - { 0x03a6, 0x013b }, - { 0x03aa, 0x0112 }, - { 0x03ab, 0x0122 }, - { 0x03ac, 0x0166 }, - { 0x03b3, 0x0157 }, - { 0x03b5, 0x0129 }, - { 0x03b6, 0x013c }, - { 0x03ba, 0x0113 }, - { 0x03bb, 0x0123 }, - { 0x03bc, 0x0167 }, - { 0x03bd, 0x014a }, - { 0x03bf, 0x014b }, - { 0x03c0, 0x0100 }, - { 0x03c7, 0x012e }, - { 0x03cc, 0x0116 }, - { 0x03cf, 0x012a }, - { 0x03d1, 0x0145 }, - { 0x03d2, 0x014c }, - { 0x03d3, 0x0136 }, - { 0x03d9, 0x0172 }, - { 0x03dd, 0x0168 }, - { 0x03de, 0x016a }, - { 0x03e0, 0x0101 }, - { 0x03e7, 0x012f }, - { 0x03ec, 0x0117 }, - { 0x03ef, 0x012b }, - { 0x03f1, 0x0146 }, - { 0x03f2, 0x014d }, - { 0x03f3, 0x0137 }, - { 0x03f9, 0x0173 }, - { 0x03fd, 0x0169 }, - { 0x03fe, 0x016b }, - { 0x047e, 0x203e }, - { 0x04a1, 0x3002 }, - { 0x04a2, 0x300c }, - { 0x04a3, 0x300d }, - { 0x04a4, 0x3001 }, - { 0x04a5, 0x30fb }, - { 0x04a6, 0x30f2 }, - { 0x04a7, 0x30a1 }, - { 0x04a8, 0x30a3 }, - { 0x04a9, 0x30a5 }, - { 0x04aa, 0x30a7 }, - { 0x04ab, 0x30a9 }, - { 0x04ac, 0x30e3 }, - { 0x04ad, 0x30e5 }, - { 0x04ae, 0x30e7 }, - { 0x04af, 0x30c3 }, - { 0x04b0, 0x30fc }, - { 0x04b1, 0x30a2 }, - { 0x04b2, 0x30a4 }, - { 0x04b3, 0x30a6 }, - { 0x04b4, 0x30a8 }, - { 0x04b5, 0x30aa }, - { 0x04b6, 0x30ab }, - { 0x04b7, 0x30ad }, - { 0x04b8, 0x30af }, - { 0x04b9, 0x30b1 }, - { 0x04ba, 0x30b3 }, - { 0x04bb, 0x30b5 }, - { 0x04bc, 0x30b7 }, - { 0x04bd, 0x30b9 }, - { 0x04be, 0x30bb }, - { 0x04bf, 0x30bd }, - { 0x04c0, 0x30bf }, - { 0x04c1, 0x30c1 }, - { 0x04c2, 0x30c4 }, - { 0x04c3, 0x30c6 }, - { 0x04c4, 0x30c8 }, - { 0x04c5, 0x30ca }, - { 0x04c6, 0x30cb }, - { 0x04c7, 0x30cc }, - { 0x04c8, 0x30cd }, - { 0x04c9, 0x30ce }, - { 0x04ca, 0x30cf }, - { 0x04cb, 0x30d2 }, - { 0x04cc, 0x30d5 }, - { 0x04cd, 0x30d8 }, - { 0x04ce, 0x30db }, - { 0x04cf, 0x30de }, - { 0x04d0, 0x30df }, - { 0x04d1, 0x30e0 }, - { 0x04d2, 0x30e1 }, - { 0x04d3, 0x30e2 }, - { 0x04d4, 0x30e4 }, - { 0x04d5, 0x30e6 }, - { 0x04d6, 0x30e8 }, - { 0x04d7, 0x30e9 }, - { 0x04d8, 0x30ea }, - { 0x04d9, 0x30eb }, - { 0x04da, 0x30ec }, - { 0x04db, 0x30ed }, - { 0x04dc, 0x30ef }, - { 0x04dd, 0x30f3 }, - { 0x04de, 0x309b }, - { 0x04df, 0x309c }, - { 0x05ac, 0x060c }, - { 0x05bb, 0x061b }, - { 0x05bf, 0x061f }, - { 0x05c1, 0x0621 }, - { 0x05c2, 0x0622 }, - { 0x05c3, 0x0623 }, - { 0x05c4, 0x0624 }, - { 0x05c5, 0x0625 }, - { 0x05c6, 0x0626 }, - { 0x05c7, 0x0627 }, - { 0x05c8, 0x0628 }, - { 0x05c9, 0x0629 }, - { 0x05ca, 0x062a }, - { 0x05cb, 0x062b }, - { 0x05cc, 0x062c }, - { 0x05cd, 0x062d }, - { 0x05ce, 0x062e }, - { 0x05cf, 0x062f }, - { 0x05d0, 0x0630 }, - { 0x05d1, 0x0631 }, - { 0x05d2, 0x0632 }, - { 0x05d3, 0x0633 }, - { 0x05d4, 0x0634 }, - { 0x05d5, 0x0635 }, - { 0x05d6, 0x0636 }, - { 0x05d7, 0x0637 }, - { 0x05d8, 0x0638 }, - { 0x05d9, 0x0639 }, - { 0x05da, 0x063a }, - { 0x05e0, 0x0640 }, - { 0x05e1, 0x0641 }, - { 0x05e2, 0x0642 }, - { 0x05e3, 0x0643 }, - { 0x05e4, 0x0644 }, - { 0x05e5, 0x0645 }, - { 0x05e6, 0x0646 }, - { 0x05e7, 0x0647 }, - { 0x05e8, 0x0648 }, - { 0x05e9, 0x0649 }, - { 0x05ea, 0x064a }, - { 0x05eb, 0x064b }, - { 0x05ec, 0x064c }, - { 0x05ed, 0x064d }, - { 0x05ee, 0x064e }, - { 0x05ef, 0x064f }, - { 0x05f0, 0x0650 }, - { 0x05f1, 0x0651 }, - { 0x05f2, 0x0652 }, - { 0x06a1, 0x0452 }, - { 0x06a2, 0x0453 }, - { 0x06a3, 0x0451 }, - { 0x06a4, 0x0454 }, - { 0x06a5, 0x0455 }, - { 0x06a6, 0x0456 }, - { 0x06a7, 0x0457 }, - { 0x06a8, 0x0458 }, - { 0x06a9, 0x0459 }, - { 0x06aa, 0x045a }, - { 0x06ab, 0x045b }, - { 0x06ac, 0x045c }, - { 0x06ae, 0x045e }, - { 0x06af, 0x045f }, - { 0x06b0, 0x2116 }, - { 0x06b1, 0x0402 }, - { 0x06b2, 0x0403 }, - { 0x06b3, 0x0401 }, - { 0x06b4, 0x0404 }, - { 0x06b5, 0x0405 }, - { 0x06b6, 0x0406 }, - { 0x06b7, 0x0407 }, - { 0x06b8, 0x0408 }, - { 0x06b9, 0x0409 }, - { 0x06ba, 0x040a }, - { 0x06bb, 0x040b }, - { 0x06bc, 0x040c }, - { 0x06be, 0x040e }, - { 0x06bf, 0x040f }, - { 0x06c0, 0x044e }, - { 0x06c1, 0x0430 }, - { 0x06c2, 0x0431 }, - { 0x06c3, 0x0446 }, - { 0x06c4, 0x0434 }, - { 0x06c5, 0x0435 }, - { 0x06c6, 0x0444 }, - { 0x06c7, 0x0433 }, - { 0x06c8, 0x0445 }, - { 0x06c9, 0x0438 }, - { 0x06ca, 0x0439 }, - { 0x06cb, 0x043a }, - { 0x06cc, 0x043b }, - { 0x06cd, 0x043c }, - { 0x06ce, 0x043d }, - { 0x06cf, 0x043e }, - { 0x06d0, 0x043f }, - { 0x06d1, 0x044f }, - { 0x06d2, 0x0440 }, - { 0x06d3, 0x0441 }, - { 0x06d4, 0x0442 }, - { 0x06d5, 0x0443 }, - { 0x06d6, 0x0436 }, - { 0x06d7, 0x0432 }, - { 0x06d8, 0x044c }, - { 0x06d9, 0x044b }, - { 0x06da, 0x0437 }, - { 0x06db, 0x0448 }, - { 0x06dc, 0x044d }, - { 0x06dd, 0x0449 }, - { 0x06de, 0x0447 }, - { 0x06df, 0x044a }, - { 0x06e0, 0x042e }, - { 0x06e1, 0x0410 }, - { 0x06e2, 0x0411 }, - { 0x06e3, 0x0426 }, - { 0x06e4, 0x0414 }, - { 0x06e5, 0x0415 }, - { 0x06e6, 0x0424 }, - { 0x06e7, 0x0413 }, - { 0x06e8, 0x0425 }, - { 0x06e9, 0x0418 }, - { 0x06ea, 0x0419 }, - { 0x06eb, 0x041a }, - { 0x06ec, 0x041b }, - { 0x06ed, 0x041c }, - { 0x06ee, 0x041d }, - { 0x06ef, 0x041e }, - { 0x06f0, 0x041f }, - { 0x06f1, 0x042f }, - { 0x06f2, 0x0420 }, - { 0x06f3, 0x0421 }, - { 0x06f4, 0x0422 }, - { 0x06f5, 0x0423 }, - { 0x06f6, 0x0416 }, - { 0x06f7, 0x0412 }, - { 0x06f8, 0x042c }, - { 0x06f9, 0x042b }, - { 0x06fa, 0x0417 }, - { 0x06fb, 0x0428 }, - { 0x06fc, 0x042d }, - { 0x06fd, 0x0429 }, - { 0x06fe, 0x0427 }, - { 0x06ff, 0x042a }, - { 0x07a1, 0x0386 }, - { 0x07a2, 0x0388 }, - { 0x07a3, 0x0389 }, - { 0x07a4, 0x038a }, - { 0x07a5, 0x03aa }, - { 0x07a7, 0x038c }, - { 0x07a8, 0x038e }, - { 0x07a9, 0x03ab }, - { 0x07ab, 0x038f }, - { 0x07ae, 0x0385 }, - { 0x07af, 0x2015 }, - { 0x07b1, 0x03ac }, - { 0x07b2, 0x03ad }, - { 0x07b3, 0x03ae }, - { 0x07b4, 0x03af }, - { 0x07b5, 0x03ca }, - { 0x07b6, 0x0390 }, - { 0x07b7, 0x03cc }, - { 0x07b8, 0x03cd }, - { 0x07b9, 0x03cb }, - { 0x07ba, 0x03b0 }, - { 0x07bb, 0x03ce }, - { 0x07c1, 0x0391 }, - { 0x07c2, 0x0392 }, - { 0x07c3, 0x0393 }, - { 0x07c4, 0x0394 }, - { 0x07c5, 0x0395 }, - { 0x07c6, 0x0396 }, - { 0x07c7, 0x0397 }, - { 0x07c8, 0x0398 }, - { 0x07c9, 0x0399 }, - { 0x07ca, 0x039a }, - { 0x07cb, 0x039b }, - { 0x07cc, 0x039c }, - { 0x07cd, 0x039d }, - { 0x07ce, 0x039e }, - { 0x07cf, 0x039f }, - { 0x07d0, 0x03a0 }, - { 0x07d1, 0x03a1 }, - { 0x07d2, 0x03a3 }, - { 0x07d4, 0x03a4 }, - { 0x07d5, 0x03a5 }, - { 0x07d6, 0x03a6 }, - { 0x07d7, 0x03a7 }, - { 0x07d8, 0x03a8 }, - { 0x07d9, 0x03a9 }, - { 0x07e1, 0x03b1 }, - { 0x07e2, 0x03b2 }, - { 0x07e3, 0x03b3 }, - { 0x07e4, 0x03b4 }, - { 0x07e5, 0x03b5 }, - { 0x07e6, 0x03b6 }, - { 0x07e7, 0x03b7 }, - { 0x07e8, 0x03b8 }, - { 0x07e9, 0x03b9 }, - { 0x07ea, 0x03ba }, - { 0x07eb, 0x03bb }, - { 0x07ec, 0x03bc }, - { 0x07ed, 0x03bd }, - { 0x07ee, 0x03be }, - { 0x07ef, 0x03bf }, - { 0x07f0, 0x03c0 }, - { 0x07f1, 0x03c1 }, - { 0x07f2, 0x03c3 }, - { 0x07f3, 0x03c2 }, - { 0x07f4, 0x03c4 }, - { 0x07f5, 0x03c5 }, - { 0x07f6, 0x03c6 }, - { 0x07f7, 0x03c7 }, - { 0x07f8, 0x03c8 }, - { 0x07f9, 0x03c9 }, - { 0x08a1, 0x23b7 }, - { 0x08a2, 0x250c }, - { 0x08a3, 0x2500 }, - { 0x08a4, 0x2320 }, - { 0x08a5, 0x2321 }, - { 0x08a6, 0x2502 }, - { 0x08a7, 0x23a1 }, - { 0x08a8, 0x23a3 }, - { 0x08a9, 0x23a4 }, - { 0x08aa, 0x23a6 }, - { 0x08ab, 0x239b }, - { 0x08ac, 0x239d }, - { 0x08ad, 0x239e }, - { 0x08ae, 0x23a0 }, - { 0x08af, 0x23a8 }, - { 0x08b0, 0x23ac }, - { 0x08bc, 0x2264 }, - { 0x08bd, 0x2260 }, - { 0x08be, 0x2265 }, - { 0x08bf, 0x222b }, - { 0x08c0, 0x2234 }, - { 0x08c1, 0x221d }, - { 0x08c2, 0x221e }, - { 0x08c5, 0x2207 }, - { 0x08c8, 0x223c }, - { 0x08c9, 0x2243 }, - { 0x08cd, 0x21d4 }, - { 0x08ce, 0x21d2 }, - { 0x08cf, 0x2261 }, - { 0x08d6, 0x221a }, - { 0x08da, 0x2282 }, - { 0x08db, 0x2283 }, - { 0x08dc, 0x2229 }, - { 0x08dd, 0x222a }, - { 0x08de, 0x2227 }, - { 0x08df, 0x2228 }, - { 0x08ef, 0x2202 }, - { 0x08f6, 0x0192 }, - { 0x08fb, 0x2190 }, - { 0x08fc, 0x2191 }, - { 0x08fd, 0x2192 }, - { 0x08fe, 0x2193 }, - { 0x09e0, 0x25c6 }, - { 0x09e1, 0x2592 }, - { 0x09e2, 0x2409 }, - { 0x09e3, 0x240c }, - { 0x09e4, 0x240d }, - { 0x09e5, 0x240a }, - { 0x09e8, 0x2424 }, - { 0x09e9, 0x240b }, - { 0x09ea, 0x2518 }, - { 0x09eb, 0x2510 }, - { 0x09ec, 0x250c }, - { 0x09ed, 0x2514 }, - { 0x09ee, 0x253c }, - { 0x09ef, 0x23ba }, - { 0x09f0, 0x23bb }, - { 0x09f1, 0x2500 }, - { 0x09f2, 0x23bc }, - { 0x09f3, 0x23bd }, - { 0x09f4, 0x251c }, - { 0x09f5, 0x2524 }, - { 0x09f6, 0x2534 }, - { 0x09f7, 0x252c }, - { 0x09f8, 0x2502 }, - { 0x0aa1, 0x2003 }, - { 0x0aa2, 0x2002 }, - { 0x0aa3, 0x2004 }, - { 0x0aa4, 0x2005 }, - { 0x0aa5, 0x2007 }, - { 0x0aa6, 0x2008 }, - { 0x0aa7, 0x2009 }, - { 0x0aa8, 0x200a }, - { 0x0aa9, 0x2014 }, - { 0x0aaa, 0x2013 }, - { 0x0aae, 0x2026 }, - { 0x0aaf, 0x2025 }, - { 0x0ab0, 0x2153 }, - { 0x0ab1, 0x2154 }, - { 0x0ab2, 0x2155 }, - { 0x0ab3, 0x2156 }, - { 0x0ab4, 0x2157 }, - { 0x0ab5, 0x2158 }, - { 0x0ab6, 0x2159 }, - { 0x0ab7, 0x215a }, - { 0x0ab8, 0x2105 }, - { 0x0abb, 0x2012 }, - { 0x0abc, 0x2329 }, - { 0x0abe, 0x232a }, - { 0x0ac3, 0x215b }, - { 0x0ac4, 0x215c }, - { 0x0ac5, 0x215d }, - { 0x0ac6, 0x215e }, - { 0x0ac9, 0x2122 }, - { 0x0aca, 0x2613 }, - { 0x0acc, 0x25c1 }, - { 0x0acd, 0x25b7 }, - { 0x0ace, 0x25cb }, - { 0x0acf, 0x25af }, - { 0x0ad0, 0x2018 }, - { 0x0ad1, 0x2019 }, - { 0x0ad2, 0x201c }, - { 0x0ad3, 0x201d }, - { 0x0ad4, 0x211e }, - { 0x0ad6, 0x2032 }, - { 0x0ad7, 0x2033 }, - { 0x0ad9, 0x271d }, - { 0x0adb, 0x25ac }, - { 0x0adc, 0x25c0 }, - { 0x0add, 0x25b6 }, - { 0x0ade, 0x25cf }, - { 0x0adf, 0x25ae }, - { 0x0ae0, 0x25e6 }, - { 0x0ae1, 0x25ab }, - { 0x0ae2, 0x25ad }, - { 0x0ae3, 0x25b3 }, - { 0x0ae4, 0x25bd }, - { 0x0ae5, 0x2606 }, - { 0x0ae6, 0x2022 }, - { 0x0ae7, 0x25aa }, - { 0x0ae8, 0x25b2 }, - { 0x0ae9, 0x25bc }, - { 0x0aea, 0x261c }, - { 0x0aeb, 0x261e }, - { 0x0aec, 0x2663 }, - { 0x0aed, 0x2666 }, - { 0x0aee, 0x2665 }, - { 0x0af0, 0x2720 }, - { 0x0af1, 0x2020 }, - { 0x0af2, 0x2021 }, - { 0x0af3, 0x2713 }, - { 0x0af4, 0x2717 }, - { 0x0af5, 0x266f }, - { 0x0af6, 0x266d }, - { 0x0af7, 0x2642 }, - { 0x0af8, 0x2640 }, - { 0x0af9, 0x260e }, - { 0x0afa, 0x2315 }, - { 0x0afb, 0x2117 }, - { 0x0afc, 0x2038 }, - { 0x0afd, 0x201a }, - { 0x0afe, 0x201e }, - { 0x0ba3, 0x003c }, - { 0x0ba6, 0x003e }, - { 0x0ba8, 0x2228 }, - { 0x0ba9, 0x2227 }, - { 0x0bc0, 0x00af }, - { 0x0bc2, 0x22a5 }, - { 0x0bc3, 0x2229 }, - { 0x0bc4, 0x230a }, - { 0x0bc6, 0x005f }, - { 0x0bca, 0x2218 }, - { 0x0bcc, 0x2395 }, - { 0x0bce, 0x22a4 }, - { 0x0bcf, 0x25cb }, - { 0x0bd3, 0x2308 }, - { 0x0bd6, 0x222a }, - { 0x0bd8, 0x2283 }, - { 0x0bda, 0x2282 }, - { 0x0bdc, 0x22a2 }, - { 0x0bfc, 0x22a3 }, - { 0x0cdf, 0x2017 }, - { 0x0ce0, 0x05d0 }, - { 0x0ce1, 0x05d1 }, - { 0x0ce2, 0x05d2 }, - { 0x0ce3, 0x05d3 }, - { 0x0ce4, 0x05d4 }, - { 0x0ce5, 0x05d5 }, - { 0x0ce6, 0x05d6 }, - { 0x0ce7, 0x05d7 }, - { 0x0ce8, 0x05d8 }, - { 0x0ce9, 0x05d9 }, - { 0x0cea, 0x05da }, - { 0x0ceb, 0x05db }, - { 0x0cec, 0x05dc }, - { 0x0ced, 0x05dd }, - { 0x0cee, 0x05de }, - { 0x0cef, 0x05df }, - { 0x0cf0, 0x05e0 }, - { 0x0cf1, 0x05e1 }, - { 0x0cf2, 0x05e2 }, - { 0x0cf3, 0x05e3 }, - { 0x0cf4, 0x05e4 }, - { 0x0cf5, 0x05e5 }, - { 0x0cf6, 0x05e6 }, - { 0x0cf7, 0x05e7 }, - { 0x0cf8, 0x05e8 }, - { 0x0cf9, 0x05e9 }, - { 0x0cfa, 0x05ea }, - { 0x0da1, 0x0e01 }, - { 0x0da2, 0x0e02 }, - { 0x0da3, 0x0e03 }, - { 0x0da4, 0x0e04 }, - { 0x0da5, 0x0e05 }, - { 0x0da6, 0x0e06 }, - { 0x0da7, 0x0e07 }, - { 0x0da8, 0x0e08 }, - { 0x0da9, 0x0e09 }, - { 0x0daa, 0x0e0a }, - { 0x0dab, 0x0e0b }, - { 0x0dac, 0x0e0c }, - { 0x0dad, 0x0e0d }, - { 0x0dae, 0x0e0e }, - { 0x0daf, 0x0e0f }, - { 0x0db0, 0x0e10 }, - { 0x0db1, 0x0e11 }, - { 0x0db2, 0x0e12 }, - { 0x0db3, 0x0e13 }, - { 0x0db4, 0x0e14 }, - { 0x0db5, 0x0e15 }, - { 0x0db6, 0x0e16 }, - { 0x0db7, 0x0e17 }, - { 0x0db8, 0x0e18 }, - { 0x0db9, 0x0e19 }, - { 0x0dba, 0x0e1a }, - { 0x0dbb, 0x0e1b }, - { 0x0dbc, 0x0e1c }, - { 0x0dbd, 0x0e1d }, - { 0x0dbe, 0x0e1e }, - { 0x0dbf, 0x0e1f }, - { 0x0dc0, 0x0e20 }, - { 0x0dc1, 0x0e21 }, - { 0x0dc2, 0x0e22 }, - { 0x0dc3, 0x0e23 }, - { 0x0dc4, 0x0e24 }, - { 0x0dc5, 0x0e25 }, - { 0x0dc6, 0x0e26 }, - { 0x0dc7, 0x0e27 }, - { 0x0dc8, 0x0e28 }, - { 0x0dc9, 0x0e29 }, - { 0x0dca, 0x0e2a }, - { 0x0dcb, 0x0e2b }, - { 0x0dcc, 0x0e2c }, - { 0x0dcd, 0x0e2d }, - { 0x0dce, 0x0e2e }, - { 0x0dcf, 0x0e2f }, - { 0x0dd0, 0x0e30 }, - { 0x0dd1, 0x0e31 }, - { 0x0dd2, 0x0e32 }, - { 0x0dd3, 0x0e33 }, - { 0x0dd4, 0x0e34 }, - { 0x0dd5, 0x0e35 }, - { 0x0dd6, 0x0e36 }, - { 0x0dd7, 0x0e37 }, - { 0x0dd8, 0x0e38 }, - { 0x0dd9, 0x0e39 }, - { 0x0dda, 0x0e3a }, - { 0x0ddf, 0x0e3f }, - { 0x0de0, 0x0e40 }, - { 0x0de1, 0x0e41 }, - { 0x0de2, 0x0e42 }, - { 0x0de3, 0x0e43 }, - { 0x0de4, 0x0e44 }, - { 0x0de5, 0x0e45 }, - { 0x0de6, 0x0e46 }, - { 0x0de7, 0x0e47 }, - { 0x0de8, 0x0e48 }, - { 0x0de9, 0x0e49 }, - { 0x0dea, 0x0e4a }, - { 0x0deb, 0x0e4b }, - { 0x0dec, 0x0e4c }, - { 0x0ded, 0x0e4d }, - { 0x0df0, 0x0e50 }, - { 0x0df1, 0x0e51 }, - { 0x0df2, 0x0e52 }, - { 0x0df3, 0x0e53 }, - { 0x0df4, 0x0e54 }, - { 0x0df5, 0x0e55 }, - { 0x0df6, 0x0e56 }, - { 0x0df7, 0x0e57 }, - { 0x0df8, 0x0e58 }, - { 0x0df9, 0x0e59 }, - { 0x0ea1, 0x3131 }, - { 0x0ea2, 0x3132 }, - { 0x0ea3, 0x3133 }, - { 0x0ea4, 0x3134 }, - { 0x0ea5, 0x3135 }, - { 0x0ea6, 0x3136 }, - { 0x0ea7, 0x3137 }, - { 0x0ea8, 0x3138 }, - { 0x0ea9, 0x3139 }, - { 0x0eaa, 0x313a }, - { 0x0eab, 0x313b }, - { 0x0eac, 0x313c }, - { 0x0ead, 0x313d }, - { 0x0eae, 0x313e }, - { 0x0eaf, 0x313f }, - { 0x0eb0, 0x3140 }, - { 0x0eb1, 0x3141 }, - { 0x0eb2, 0x3142 }, - { 0x0eb3, 0x3143 }, - { 0x0eb4, 0x3144 }, - { 0x0eb5, 0x3145 }, - { 0x0eb6, 0x3146 }, - { 0x0eb7, 0x3147 }, - { 0x0eb8, 0x3148 }, - { 0x0eb9, 0x3149 }, - { 0x0eba, 0x314a }, - { 0x0ebb, 0x314b }, - { 0x0ebc, 0x314c }, - { 0x0ebd, 0x314d }, - { 0x0ebe, 0x314e }, - { 0x0ebf, 0x314f }, - { 0x0ec0, 0x3150 }, - { 0x0ec1, 0x3151 }, - { 0x0ec2, 0x3152 }, - { 0x0ec3, 0x3153 }, - { 0x0ec4, 0x3154 }, - { 0x0ec5, 0x3155 }, - { 0x0ec6, 0x3156 }, - { 0x0ec7, 0x3157 }, - { 0x0ec8, 0x3158 }, - { 0x0ec9, 0x3159 }, - { 0x0eca, 0x315a }, - { 0x0ecb, 0x315b }, - { 0x0ecc, 0x315c }, - { 0x0ecd, 0x315d }, - { 0x0ece, 0x315e }, - { 0x0ecf, 0x315f }, - { 0x0ed0, 0x3160 }, - { 0x0ed1, 0x3161 }, - { 0x0ed2, 0x3162 }, - { 0x0ed3, 0x3163 }, - { 0x0ed4, 0x11a8 }, - { 0x0ed5, 0x11a9 }, - { 0x0ed6, 0x11aa }, - { 0x0ed7, 0x11ab }, - { 0x0ed8, 0x11ac }, - { 0x0ed9, 0x11ad }, - { 0x0eda, 0x11ae }, - { 0x0edb, 0x11af }, - { 0x0edc, 0x11b0 }, - { 0x0edd, 0x11b1 }, - { 0x0ede, 0x11b2 }, - { 0x0edf, 0x11b3 }, - { 0x0ee0, 0x11b4 }, - { 0x0ee1, 0x11b5 }, - { 0x0ee2, 0x11b6 }, - { 0x0ee3, 0x11b7 }, - { 0x0ee4, 0x11b8 }, - { 0x0ee5, 0x11b9 }, - { 0x0ee6, 0x11ba }, - { 0x0ee7, 0x11bb }, - { 0x0ee8, 0x11bc }, - { 0x0ee9, 0x11bd }, - { 0x0eea, 0x11be }, - { 0x0eeb, 0x11bf }, - { 0x0eec, 0x11c0 }, - { 0x0eed, 0x11c1 }, - { 0x0eee, 0x11c2 }, - { 0x0eef, 0x316d }, - { 0x0ef0, 0x3171 }, - { 0x0ef1, 0x3178 }, - { 0x0ef2, 0x317f }, - { 0x0ef3, 0x3181 }, - { 0x0ef4, 0x3184 }, - { 0x0ef5, 0x3186 }, - { 0x0ef6, 0x318d }, - { 0x0ef7, 0x318e }, - { 0x0ef8, 0x11eb }, - { 0x0ef9, 0x11f0 }, - { 0x0efa, 0x11f9 }, - { 0x0eff, 0x20a9 }, - { 0x13a4, 0x20ac }, - { 0x13bc, 0x0152 }, - { 0x13bd, 0x0153 }, - { 0x13be, 0x0178 }, - { 0x20ac, 0x20ac }, - { 0xfe50, '`' }, - { 0xfe51, 0x00b4 }, - { 0xfe52, '^' }, - { 0xfe53, '~' }, - { 0xfe54, 0x00af }, - { 0xfe55, 0x02d8 }, - { 0xfe56, 0x02d9 }, - { 0xfe57, 0x00a8 }, - { 0xfe58, 0x02da }, - { 0xfe59, 0x02dd }, - { 0xfe5a, 0x02c7 }, - { 0xfe5b, 0x00b8 }, - { 0xfe5c, 0x02db }, - { 0xfe5d, 0x037a }, - { 0xfe5e, 0x309b }, - { 0xfe5f, 0x309c }, - { 0xfe63, '/' }, - { 0xfe64, 0x02bc }, - { 0xfe65, 0x02bd }, - { 0xfe66, 0x02f5 }, - { 0xfe67, 0x02f3 }, - { 0xfe68, 0x02cd }, - { 0xfe69, 0xa788 }, - { 0xfe6a, 0x02f7 }, - { 0xfe6e, ',' }, - { 0xfe6f, 0x00a4 }, - { 0xfe80, 'a' }, // XK_dead_a - { 0xfe81, 'A' }, // XK_dead_A - { 0xfe82, 'e' }, // XK_dead_e - { 0xfe83, 'E' }, // XK_dead_E - { 0xfe84, 'i' }, // XK_dead_i - { 0xfe85, 'I' }, // XK_dead_I - { 0xfe86, 'o' }, // XK_dead_o - { 0xfe87, 'O' }, // XK_dead_O - { 0xfe88, 'u' }, // XK_dead_u - { 0xfe89, 'U' }, // XK_dead_U - { 0xfe8a, 0x0259 }, - { 0xfe8b, 0x018f }, - { 0xfe8c, 0x00b5 }, - { 0xfe90, '_' }, - { 0xfe91, 0x02c8 }, - { 0xfe92, 0x02cc }, - { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, - { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 }, - { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 }, - { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 }, - { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 }, - { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 }, - { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 }, - { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 }, - { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 }, - { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 }, - { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 }, - { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, - { 0xffab /*XKB_KEY_KP_Add*/, '+' }, - { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, - { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, - { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, - { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, - { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, - { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, - { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, - { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, - { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, - { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, - { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, - { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, - { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, - { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, - { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } -}; - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Convert XKB KeySym to Unicode -// -uint32_t _glfwKeySym2Unicode(unsigned int keysym) -{ - int min = 0; - int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; - int mid; - - // First check for Latin-1 characters (1:1 mapping) - if ((keysym >= 0x0020 && keysym <= 0x007e) || - (keysym >= 0x00a0 && keysym <= 0x00ff)) - { - return keysym; - } - - // Also check for directly encoded 24-bit UCS characters - if ((keysym & 0xff000000) == 0x01000000) - return keysym & 0x00ffffff; - - // Binary search in table - while (max >= min) - { - mid = (min + max) / 2; - if (keysymtab[mid].keysym < keysym) - min = mid + 1; - else if (keysymtab[mid].keysym > keysym) - max = mid - 1; - else - return keysymtab[mid].ucs; - } - - // No matching Unicode value found - return GLFW_INVALID_CODEPOINT; -} - diff --git a/libs/zglfw/libs/glfw/src/xkb_unicode.h b/libs/zglfw/libs/glfw/src/xkb_unicode.h deleted file mode 100644 index be97cdcb8..000000000 --- a/libs/zglfw/libs/glfw/src/xkb_unicode.h +++ /dev/null @@ -1,30 +0,0 @@ -//======================================================================== -// GLFW 3.3 Linux - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014 Jonas Ã…dahl -// -// 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. -// -//======================================================================== - -#define GLFW_INVALID_CODEPOINT 0xffffffffu - -uint32_t _glfwKeySym2Unicode(unsigned int keysym); - diff --git a/libs/zglfw/src/zglfw.zig b/libs/zglfw/src/zglfw.zig deleted file mode 100644 index df4c59b96..000000000 --- a/libs/zglfw/src/zglfw.zig +++ /dev/null @@ -1,519 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -pub fn init() Error!void { - if (glfwInit() != 0) return; - try maybeError(); - unreachable; -} -extern fn glfwInit() i32; - -pub const terminate = glfwTerminate; -extern fn glfwTerminate() void; - -pub const pollEvents = glfwPollEvents; -extern fn glfwPollEvents() void; - -pub fn vulkanSupported() bool { - return if (glfwVulkanSupported() == 0) false else true; -} -extern fn glfwVulkanSupported() i32; - -pub fn getRequiredInstanceExtensions() Error![][*:0]const u8 { - var count: u32 = 0; - if (glfwGetRequiredInstanceExtensions(&count)) |extensions| { - return @ptrCast([*][*:0]const u8, extensions)[0..count]; - } - try maybeError(); - unreachable; -} -extern fn glfwGetRequiredInstanceExtensions(count: *u32) ?*?[*:0]const u8; - -pub const getTime = glfwGetTime; -extern fn glfwGetTime() f64; -//-------------------------------------------------------------------------------------------------- -// -// Keyboard/Mouse -// -//-------------------------------------------------------------------------------------------------- -pub const Action = enum(i32) { - release, - press, - repeat, -}; - -pub const MouseButton = enum(i32) { - left, - right, - middle, - four, - five, - six, - seven, - eight, -}; - -pub const Key = enum(i32) { - unknown = -1, - - space = 32, - apostrophe = 39, - comma = 44, - minus = 45, - period = 46, - slash = 47, - zero = 48, - one = 49, - two = 50, - three = 51, - four = 52, - five = 53, - six = 54, - seven = 55, - eight = 56, - nine = 57, - semicolon = 59, - equal = 61, - a = 65, - b = 66, - c = 67, - d = 68, - e = 69, - f = 70, - g = 71, - h = 72, - i = 73, - j = 74, - k = 75, - l = 76, - m = 77, - n = 78, - o = 79, - p = 80, - q = 81, - r = 82, - s = 83, - t = 84, - u = 85, - v = 86, - w = 87, - x = 88, - y = 89, - z = 90, - left_bracket = 91, - backslash = 92, - right_bracket = 93, - grave_accent = 96, - world_1 = 161, - world_2 = 162, - - escape = 256, - enter = 257, - tab = 258, - backspace = 259, - insert = 260, - delete = 261, - right = 262, - left = 263, - down = 264, - up = 265, - page_up = 266, - page_down = 267, - home = 268, - end = 269, - caps_lock = 280, - scroll_lock = 281, - num_lock = 282, - print_screen = 283, - pause = 284, - F1 = 290, - F2 = 291, - F3 = 292, - F4 = 293, - F5 = 294, - F6 = 295, - F7 = 296, - F8 = 297, - F9 = 298, - F10 = 299, - F11 = 300, - F12 = 301, - F13 = 302, - F14 = 303, - F15 = 304, - F16 = 305, - F17 = 306, - F18 = 307, - F19 = 308, - F20 = 309, - F21 = 310, - F22 = 311, - F23 = 312, - F24 = 313, - F25 = 314, - kp_0 = 320, - kp_1 = 321, - kp_2 = 322, - kp_3 = 323, - kp_4 = 324, - kp_5 = 325, - kp_6 = 326, - kp_7 = 327, - kp_8 = 328, - kp_9 = 329, - kp_decimal = 330, - kp_divide = 331, - kp_multiply = 332, - kp_subtract = 333, - kp_add = 334, - kp_enter = 335, - kp_equal = 336, - left_shift = 340, - left_control = 341, - left_alt = 342, - left_super = 343, - right_shift = 344, - right_control = 345, - right_alt = 346, - right_super = 347, - menu = 348, -}; - -pub const Mods = packed struct(i32) { - shift: bool = false, - control: bool = false, - alt: bool = false, - super: bool = false, - caps_lock: bool = false, - num_lock: bool = false, - _padding: i26 = 0, -}; -//-------------------------------------------------------------------------------------------------- -// -// Error -// -//-------------------------------------------------------------------------------------------------- -pub const Error = error{ - NotInitialized, - NoCurrentContext, - InvalidEnum, - InvalidValue, - OutOfMemory, - APIUnavailable, - VersionUnavailable, - PlatformError, - FormatUnavailable, - NoWindowContext, - Unknown, -}; - -pub fn maybeError() Error!void { - return switch (glfwGetError(null)) { - 0 => {}, - 0x00010001 => Error.NotInitialized, - 0x00010002 => Error.NoCurrentContext, - 0x00010003 => Error.InvalidEnum, - 0x00010004 => Error.InvalidValue, - 0x00010005 => Error.OutOfMemory, - 0x00010006 => Error.APIUnavailable, - 0x00010007 => Error.VersionUnavailable, - 0x00010008 => Error.PlatformError, - 0x00010009 => Error.FormatUnavailable, - 0x0001000A => Error.NoWindowContext, - else => Error.Unknown, - }; -} -extern fn glfwGetError(description: ?*?[*:0]const u8) i32; -//-------------------------------------------------------------------------------------------------- -// -// Monitor -// -//-------------------------------------------------------------------------------------------------- -pub const Monitor = *opaque { - pub fn getPos(monitor: Monitor) [2]i32 { - var xpos: i32 = 0; - var ypos: i32 = 0; - glfwGetMonitorPos(monitor, &xpos, &ypos); - return .{ xpos, ypos }; - } - extern fn glfwGetMonitorPos(monitor: Monitor, xpos: *i32, ypos: *i32) void; -}; - -pub const getPrimaryMonitor = glfwGetPrimaryMonitor; -extern fn glfwGetPrimaryMonitor() ?Monitor; - -pub fn getMonitors() ?[]Monitor { - var count: i32 = 0; - if (glfwGetMonitors(&count)) |monitors| { - return monitors[0..@intCast(usize, count)]; - } - return null; -} -extern fn glfwGetMonitors(count: *i32) ?[*]Monitor; -//-------------------------------------------------------------------------------------------------- -// -// Window -// -//-------------------------------------------------------------------------------------------------- -pub const WindowHint = enum(i32) { - client_api = 0x00022001, - cocoa_retina_framebuffer = 0x00023001, -}; - -pub const Window = *opaque { - pub fn shouldClose(window: Window) bool { - return if (glfwWindowShouldClose(window) == 0) false else true; - } - extern fn glfwWindowShouldClose(window: Window) i32; - - pub const destroy = glfwDestroyWindow; - extern fn glfwDestroyWindow(window: Window) void; - - pub const setSizeLimits = glfwSetWindowSizeLimits; - extern fn glfwSetWindowSizeLimits( - window: Window, - minwidth: i32, - minheight: i32, - maxwidth: i32, - maxheight: i32, - ) void; - - pub fn getContentScale(window: Window) [2]f32 { - var xscale: f32 = 0.0; - var yscale: f32 = 0.0; - glfwGetWindowContentScale(window, &xscale, &yscale); - return .{ xscale, yscale }; - } - extern fn glfwGetWindowContentScale(window: Window, xscale: *f32, yscale: *f32) void; - - pub const getKey = glfwGetKey; - extern fn glfwGetKey(window: Window, key: Key) Action; - - pub const getMouseButton = glfwGetMouseButton; - extern fn glfwGetMouseButton(window: Window, button: MouseButton) Action; - - pub fn getCursorPos(window: Window) [2]f64 { - var xpos: f64 = 0.0; - var ypos: f64 = 0.0; - glfwGetCursorPos(window, &xpos, &ypos); - return .{ xpos, ypos }; - } - extern fn glfwGetCursorPos(window: Window, xpos: *f64, ypos: *f64) void; - - pub fn getFramebufferSize(window: Window) [2]i32 { - var width: i32 = 0.0; - var height: i32 = 0.0; - glfwGetFramebufferSize(window, &width, &height); - return .{ width, height }; - } - extern fn glfwGetFramebufferSize(window: Window, width: *i32, height: *i32) void; - - pub fn getSize(window: Window) [2]i32 { - var width: i32 = 0.0; - var height: i32 = 0.0; - glfwGetWindowSize(window, &width, &height); - return .{ width, height }; - } - extern fn glfwGetWindowSize(window: Window, width: *i32, height: *i32) void; - - pub const setKeyCallback = glfwSetKeyCallback; - extern fn glfwSetKeyCallback( - window: Window, - callback: ?*const fn ( - window: Window, - key: Key, - scancode: i32, - action: Action, - mods: Mods, - ) callconv(.C) void, - ) void; - - pub const setMouseButtonCallback = glfwSetMouseButtonCallback; - extern fn glfwSetMouseButtonCallback( - window: Window, - callback: ?*const fn (window: Window, button: MouseButton, action: Action, mods: Mods) callconv(.C) void, - ) void; - - pub const setCursorPosCallback = glfwSetCursorPosCallback; - extern fn glfwSetCursorPosCallback( - window: Window, - callback: ?*const fn (Window, xpos: f64, ypos: f64) callconv(.C) void, - ) void; - - pub const setScrollCallback = glfwSetScrollCallback; - extern fn glfwSetScrollCallback( - window: Window, - callback: ?*const fn ( - window: Window, - xoffset: f64, - yoffset: f64, - ) callconv(.C) void, - ) void; -}; - -pub fn createWindow( - width: i32, - height: i32, - title: [*:0]const u8, - monitor: ?Monitor, - share: ?Window, -) Error!Window { - if (glfwCreateWindow(width, height, title, monitor, share)) |window| return window; - try maybeError(); - unreachable; -} -extern fn glfwCreateWindow( - width: i32, - height: i32, - title: [*:0]const u8, - monitor: ?Monitor, - share: ?Window, -) ?Window; - -pub const windowHint = glfwWindowHint; -extern fn glfwWindowHint(hint: WindowHint, value: i32) void; - -pub const defaultWindowHints = glfwDefaultWindowHints; -extern fn glfwDefaultWindowHints() void; -//-------------------------------------------------------------------------------------------------- -// -// Native -// -//-------------------------------------------------------------------------------------------------- -pub fn getWin32Adapter(monitor: Monitor) Error![*:0]const u8 { - if (glfwGetWin32Adapter(monitor)) |adapter| return adapter; - try maybeError(); - unreachable; -} -extern fn glfwGetWin32Adapter(monitor: Monitor) ?[*:0]const u8; - -pub fn getWin32Window(window: Window) Error!std.os.windows.HWND { - if (glfwGetWin32Window(window)) |hwnd| return hwnd; - try maybeError(); - unreachable; -} -extern fn glfwGetWin32Window(window: Window) ?std.os.windows.HWND; - -pub fn getX11Adapter(monitor: Monitor) Error!u32 { - const adapter = glfwGetX11Adapter(monitor); - if (adapter != 0) return adapter; - try maybeError(); - unreachable; -} -extern fn glfwGetX11Adapter(monitor: Monitor) u32; - -pub fn getX11Display() Error!*anyopaque { - if (glfwGetX11Display()) |display| return display; - try maybeError(); - unreachable; -} -extern fn glfwGetX11Display() ?*anyopaque; - -pub fn getX11Window(window: Window) Error!u32 { - const window_native = glfwGetX11Window(window); - if (window_native != 0) return window_native; - try maybeError(); - unreachable; -} -extern fn glfwGetX11Window(window: Window) u32; - -pub fn getCocoaWindow(window: Window) Error!*anyopaque { - if (glfwGetCocoaWindow(window)) |window_native| return window_native; - try maybeError(); - unreachable; -} -extern fn glfwGetCocoaWindow(window: Window) ?*anyopaque; -//-------------------------------------------------------------------------------------------------- -// -// Test -// -//-------------------------------------------------------------------------------------------------- -const expect = std.testing.expect; - -fn cursorPosCallback(window: Window, xpos: f64, ypos: f64) callconv(.C) void { - _ = window; - _ = xpos; - _ = ypos; -} - -fn mouseButtonCallback(window: Window, button: MouseButton, action: Action, mods: Mods) callconv(.C) void { - _ = window; - _ = button; - _ = action; - _ = mods; -} - -fn scrollCallback(window: Window, xoffset: f64, yoffset: f64) callconv(.C) void { - _ = window; - _ = xoffset; - _ = yoffset; -} - -fn keyCallback(window: Window, key: Key, scancode: i32, action: Action, mods: Mods) callconv(.C) void { - _ = window; - _ = key; - _ = scancode; - _ = action; - _ = mods; -} - -test "zglfw.basic" { - try init(); - defer terminate(); - - if (vulkanSupported()) { - _ = try getRequiredInstanceExtensions(); - } - - _ = getTime(); - - const primary_monitor = getPrimaryMonitor(); - if (primary_monitor) |monitor| { - const monitors = getMonitors().?; - try expect(monitor == monitors[0]); - const pos = monitor.getPos(); - _ = pos[0]; - _ = pos[1]; - - const adapter = switch (@import("builtin").target.os.tag) { - .windows => try getWin32Adapter(monitor), - .linux => try getX11Adapter(monitor), - else => {}, - }; - _ = adapter; - } - - defaultWindowHints(); - windowHint(.cocoa_retina_framebuffer, 1); - windowHint(.client_api, 0); - const window = try createWindow(200, 200, "test", null, null); - defer window.destroy(); - - window.setCursorPosCallback(cursorPosCallback); - window.setMouseButtonCallback(mouseButtonCallback); - window.setKeyCallback(keyCallback); - window.setScrollCallback(scrollCallback); - window.setKeyCallback(null); - - if (window.getKey(.a) == .press) {} - if (window.getMouseButton(.right) == .press) {} - const cursor_pos = window.getCursorPos(); - _ = cursor_pos[0]; - _ = cursor_pos[1]; - - const window_native = try switch (@import("builtin").target.os.tag) { - .windows => getWin32Window(window), - .linux => getX11Window(window), - .macos => getCocoaWindow(window), - else => unreachable, - }; - _ = window_native; - - window.setSizeLimits(10, 10, 300, 300); - const content_scale = window.getContentScale(); - _ = content_scale[0]; - _ = content_scale[1]; - pollEvents(); - try maybeError(); -} -//-------------------------------------------------------------------------------------------------- diff --git a/libs/zglfw/system_sdk.zig b/libs/zglfw/system_sdk.zig deleted file mode 100644 index 68e85977e..000000000 --- a/libs/zglfw/system_sdk.zig +++ /dev/null @@ -1,300 +0,0 @@ -//! Mach system SDK inclusion -//! -//! This file contains all that you need to include the Mach system SDKs in your own build.zig, -//! allowing you to cross-compile most OpenGL/Vulkan applications with ease. -//! -//! The SDKs used by this script by default are: -//! -//! * Windows: https://github.com/hexops/sdk-windows-x86_64 (~7MB, updated DirectX headers for Zig/MinGW) -//! * Linux: https://github.com/hexops/sdk-linux-x86_64 (~40MB, X11, Wayland, etc. development libraries) -//! * MacOS (most frameworks you'd find in the XCode SDK): -//! * https://github.com/hexops/sdk-macos-11.3 (~160MB, default) -//! * https://github.com/hexops/sdk-macos-12.0 (~112MB, only if you specify a macOS 12 target triple.) -//! -//! You may supply your own SDKs via the Options struct if needed, although the Mach versions above -//! will generally work for most OpenGL/Vulkan applications. -//! -//! How it works: When `include` is called, the compilation target is detected. If it does not -//! already exist, the SDK repository for the target platform is cloned via `git clone`. If the -//! target is MacOS, an interactive license agreement prompt (agreeing to the XCode SDK terms) -//! will appear. You can also set the environment variable `AGREE=true` to dismiss this. -//! -//! Once downloaded, `include` will add the SDK library, header, etc. directions to the build step -//! so that you can just include and link against libraries/frameworks as if they were there, and -//! you may then cross-compile your code with ease. See https://github.com/hexops/mach-glfw for an -//! example. -//! -//! Best way to get this file in your own repository? We suggest just copying it, or importing it -//! from a project that includes it if you're using one (e.g. mach-glfw) -//! -//! version: Mar 4, 2022 - -const std = @import("std"); -const Builder = std.build.Builder; - -pub const Options = struct { - /// The github org to find repositories in. - github_org: []const u8 = "hexops", - - /// The MacOS 12 SDK repository name. - macos_sdk_12: []const u8 = "sdk-macos-12.0", - macos_sdk_12_revision: []const u8 = "14613b4917c7059dad8f3789f55bb13a2548f83d", - - /// The MacOS 11 SDK repository name. - macos_sdk_11: []const u8 = "sdk-macos-11.3", - macos_sdk_11_revision: []const u8 = "ccbaae84cc39469a6792108b24480a4806e09d59", - - /// The Linux x86-64 SDK repository name. - linux_x86_64: []const u8 = "sdk-linux-x86_64", - linux_x86_64_revision: []const u8 = "1c2ea7f968bff3fbe4ddb0b605eef6b626e181ea", - - /// The Linux aarch64 SDK repository name. - linux_aarch64: []const u8 = "sdk-linux-aarch64", - linux_aarch64_revision: []const u8 = "dbb05673a01050a937cbc8ad47a407111cac146c", - - /// The Windows x86-64 SDK repository name. - windows_x86_64: []const u8 = "sdk-windows-x86_64", - windows_x86_64_revision: []const u8 = "13dcda7fe3f1aec0fc6130527226ad7ae0f4b792", - - /// If true, the Builder.sysroot will set to the SDK path. This has the drawback of preventing - /// you from including headers, libraries, etc. from outside the SDK generally. However, it can - /// be useful in order to identify which libraries, headers, frameworks, etc. may be missing in - /// your SDK for cross compilation. - set_sysroot: bool = false, -}; - -pub fn include(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void { - const target = (std.zig.system.NativeTargetInfo.detect(step.target) catch unreachable).target; - switch (target.os.tag) { - .windows => includeSdkWindowsX8664(b, step, options), - .macos => includeSdkMacOS(b, step, options), - else => switch (target.cpu.arch) { // Assume Linux-like for now - .aarch64 => includeSdkLinuxAarch64(b, step, options), - else => includeSdkLinuxX8664(b, step, options), // Assume x86-64 for now - }, - } -} - -fn includeSdkMacOS(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void { - const target = (std.zig.system.NativeTargetInfo.detect(step.target) catch unreachable).target; - const mac_12 = target.os.version_range.semver.isAtLeast(.{ .major = 12, .minor = 0 }) orelse false; - const sdk_name = if (mac_12) options.macos_sdk_12 else options.macos_sdk_11; - const sdk_revision = if (mac_12) options.macos_sdk_12_revision else options.macos_sdk_11_revision; - const sdk_root_dir = getSdkRoot(b.allocator, step, options.github_org, sdk_name, sdk_revision) catch unreachable; - - if (options.set_sysroot) { - step.addFrameworkPath("/System/Library/Frameworks"); - step.addSystemIncludePath("/usr/include"); - step.addLibraryPath("/usr/lib"); - - var sdk_sysroot = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/" }) catch unreachable; - b.sysroot = sdk_sysroot; - return; - } - - var sdk_framework_dir = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/System/Library/Frameworks" }) catch unreachable; - step.addFrameworkPath(sdk_framework_dir); - - var sdk_include_dir = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/include" }) catch unreachable; - step.addSystemIncludePath(sdk_include_dir); - - var sdk_lib_dir = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/lib" }) catch unreachable; - step.addLibraryPath(sdk_lib_dir); -} - -fn includeSdkLinuxX8664(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void { - const sdk_root_dir = getSdkRoot(b.allocator, step, options.github_org, options.linux_x86_64, options.linux_x86_64_revision) catch unreachable; - - if (options.set_sysroot) { - var sdk_sysroot = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/" }) catch unreachable; - b.sysroot = sdk_sysroot; - return; - } - - var sdk_root_includes = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/include" }) catch unreachable; - var wayland_protocols_include = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/share/wayland-generated" }) catch unreachable; - var sdk_root_libs = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/lib/x86_64-linux-gnu" }) catch unreachable; - defer { - b.allocator.free(sdk_root_includes); - b.allocator.free(wayland_protocols_include); - b.allocator.free(sdk_root_libs); - } - step.addSystemIncludePath(sdk_root_includes); - step.addSystemIncludePath(wayland_protocols_include); - step.addLibraryPath(sdk_root_libs); -} - -fn includeSdkLinuxAarch64(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void { - const sdk_root_dir = getSdkRoot(b.allocator, step, options.github_org, options.linux_aarch64, options.linux_aarch64_revision) catch unreachable; - - if (options.set_sysroot) { - var sdk_sysroot = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/" }) catch unreachable; - b.sysroot = sdk_sysroot; - return; - } - - var sdk_root_includes = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/include" }) catch unreachable; - var wayland_protocols_include = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/share/wayland-generated" }) catch unreachable; - var sdk_root_libs = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "root/usr/lib/aarch64-linux-gnu" }) catch unreachable; - defer { - b.allocator.free(sdk_root_includes); - b.allocator.free(wayland_protocols_include); - b.allocator.free(sdk_root_libs); - } - step.addSystemIncludePath(sdk_root_includes); - step.addSystemIncludePath(wayland_protocols_include); - step.addLibraryPath(sdk_root_libs); -} - -fn includeSdkWindowsX8664(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void { - const sdk_root_dir = getSdkRoot(b.allocator, step, options.github_org, options.windows_x86_64, options.windows_x86_64_revision) catch unreachable; - - if (options.set_sysroot) { - // We have no sysroot for Windows, but we still set one to prevent inclusion of other system - // libs (if set_sysroot is set, don't want to accidentally depend on system libs.) - var sdk_sysroot = std.fs.path.join(b.allocator, &.{sdk_root_dir}) catch unreachable; - b.sysroot = sdk_sysroot; - } - - var sdk_includes = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "include" }) catch unreachable; - var sdk_libs = std.fs.path.join(b.allocator, &.{ sdk_root_dir, "lib" }) catch unreachable; - defer { - b.allocator.free(sdk_includes); - b.allocator.free(sdk_libs); - } - - step.addIncludePath(sdk_includes); - step.addLibraryPath(sdk_libs); -} - -var cached_sdk_roots: ?std.AutoHashMap(*std.build.LibExeObjStep, []const u8) = null; - -/// returns the SDK root path, determining it iff necessary. In a real application, this may be -/// tens or hundreds of times and so the result is cached in-memory (this also means the result -/// cannot be freed until the result will never be used again, which is fine as the Zig build system -/// Builder.allocator is an arena, you don't need to free.) -fn getSdkRoot(allocator: std.mem.Allocator, step: *std.build.LibExeObjStep, org: []const u8, name: []const u8, revision: []const u8) ![]const u8 { - if (cached_sdk_roots == null) { - cached_sdk_roots = std.AutoHashMap(*std.build.LibExeObjStep, []const u8).init(allocator); - } - - var entry = try cached_sdk_roots.?.getOrPut(step); - if (entry.found_existing) return entry.value_ptr.*; - const sdk_root = try determineSdkRoot(allocator, org, name, revision); - entry.value_ptr.* = sdk_root; - return sdk_root; -} - -fn determineSdkRoot(allocator: std.mem.Allocator, org: []const u8, name: []const u8, revision: []const u8) ![]const u8 { - // Find the directory where the SDK should be located. We'll consider two locations: - // - // 1. $SDK_PATH/ (if set, e.g. for testing changes to SDKs easily) - // 2. / (default) - // - // Where `` is the name of the SDK, e.g. `sdk-macos-12.0`. - var sdk_root_dir: []const u8 = undefined; - var sdk_path_dir: []const u8 = undefined; - var custom_sdk_path = false; - if (std.process.getEnvVarOwned(allocator, "SDK_PATH")) |sdk_path| { - custom_sdk_path = true; - sdk_path_dir = sdk_path; - sdk_root_dir = try std.fs.path.join(allocator, &.{ sdk_path, name }); - } else |err| switch (err) { - error.EnvironmentVariableNotFound => { - sdk_path_dir = try std.fs.getAppDataDir(allocator, org); - sdk_root_dir = try std.fs.path.join(allocator, &.{ sdk_path_dir, name }); - }, - else => |e| return e, - } - - ensureGit(allocator); - - // If the SDK exists, return it. Otherwise, clone it. - if (std.fs.openDirAbsolute(sdk_root_dir, .{})) { - const current_revision = try getCurrentGitRevision(allocator, sdk_root_dir); - if (!std.mem.eql(u8, current_revision, revision)) { - // Update the SDK to the target revision. This may be either forward or backwards in - // history (e.g. if building an old project) and so we use a hard reset. - // - // No reset is performed if specifying a custom SDK_PATH, as that is a development/debug - // option and could wipe out dev history. - exec(allocator, &[_][]const u8{ "git", "fetch" }, sdk_root_dir) catch |err| std.debug.print("warning: failed to check for updates to {s}/{s}: {s}\n", .{ org, name, @errorName(err) }); - if (!custom_sdk_path) try exec(allocator, &[_][]const u8{ "git", "reset", "--quiet", "--hard", revision }, sdk_root_dir); - } - return sdk_root_dir; - } else |err| return switch (err) { - error.FileNotFound => { - std.log.info("cloning required sdk..\ngit clone https://github.com/{s}/{s} '{s}'..\n", .{ org, name, sdk_root_dir }); - if (std.mem.startsWith(u8, name, "sdk-macos-")) { - if (!try confirmAppleSDKAgreement(allocator)) @panic("cannot continue"); - } - try std.fs.cwd().makePath(sdk_path_dir); - - var buf: [1000]u8 = undefined; - var repo_url_fbs = std.io.fixedBufferStream(&buf); - try std.fmt.format(repo_url_fbs.writer(), "https://github.com/{s}/{s}", .{ org, name }); - - try exec(allocator, &[_][]const u8{ "git", "clone", "-c", "core.longpaths=true", repo_url_fbs.getWritten() }, sdk_path_dir); - return sdk_root_dir; - }, - else => err, - }; -} - -fn exec(allocator: std.mem.Allocator, argv: []const []const u8, cwd: []const u8) !void { - var child = std.ChildProcess.init(argv, allocator); - child.cwd = cwd; - _ = try child.spawnAndWait(); -} - -fn getCurrentGitRevision(allocator: std.mem.Allocator, cwd: []const u8) ![]const u8 { - const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = &.{ "git", "rev-parse", "HEAD" }, .cwd = cwd }); - allocator.free(result.stderr); - if (result.stdout.len > 0) return result.stdout[0 .. result.stdout.len - 1]; // trim newline - return result.stdout; -} - -fn confirmAppleSDKAgreement(allocator: std.mem.Allocator) !bool { - if (std.process.getEnvVarOwned(allocator, "AGREE")) |agree| { - return std.mem.eql(u8, agree, "true"); - } else |err| switch (err) { - error.EnvironmentVariableNotFound => {}, - else => |e| return e, - } - - const stdin = std.io.getStdIn().reader(); - const stdout = std.io.getStdOut().writer(); - var buf: [10]u8 = undefined; - try stdout.print("This SDK is distributed under the terms of the Xcode and Apple SDKs agreement:\n", .{}); - try stdout.print(" https://www.apple.com/legal/sla/docs/xcode.pdf\n", .{}); - try stdout.print("\n", .{}); - try stdout.print("Do you agree to those terms? [Y/n] ", .{}); - if (try stdin.readUntilDelimiterOrEof(buf[0..], '\n')) |user_input| { - try stdout.print("\n", .{}); - var in = user_input; - if (in.len > 0 and in[in.len - 1] == '\r') in = in[0 .. in.len - 1]; - return std.mem.eql(u8, in, "y") or std.mem.eql(u8, in, "Y") or std.mem.eql(u8, in, "yes") or std.mem.eql(u8, in, ""); - } else { - return false; - } -} - -fn ensureGit(allocator: std.mem.Allocator) void { - const argv = &[_][]const u8{ "git", "--version" }; - const result = std.ChildProcess.exec(.{ - .allocator = allocator, - .argv = argv, - .cwd = ".", - }) catch { // e.g. FileNotFound - std.log.err("mach: error: 'git --version' failed. Is git not installed?", .{}); - std.process.exit(1); - }; - defer { - allocator.free(result.stderr); - allocator.free(result.stdout); - } - if (result.term.Exited != 0) { - std.log.err("mach: error: 'git --version' failed. Is git not installed?", .{}); - std.process.exit(1); - } -} diff --git a/libs/zgpu/README.md b/libs/zgpu/README.md deleted file mode 100644 index 97357c95d..000000000 --- a/libs/zgpu/README.md +++ /dev/null @@ -1,147 +0,0 @@ -# zgpu v0.2 - Cross-platform graphics layer - -`zgpu` is a cross-platform (Windows/Linux/macOS) graphics layer built on top of native wgpu API (Dawn). - -## Features: - -* Zero-overhead wgpu API bindings ([source code](https://github.com/michal-z/zig-gamedev/blob/main/libs/zgpu/src/wgpu.zig)) -* Uniform buffer pool for fast CPU->GPU transfers -* Resource pools and resources identified by 32-bit integer handles -* Async shader compilation -* GPU mipmap generator - -For more details please see below. - -## Getting started - -Copy `zgpu`, `zpool` and `zglfw` folders to a `libs` subdirectory of the root of your project. - -Then in your `build.zig` add: -```zig -const zgpu = @import("libs/zgpu/build.zig"); -const zpool = @import("libs/zpool/build.zig"); -const zglfw = @import("libs/zglfw/build.zig"); - -pub fn build(b: *std.build.Builder) void { - const zgpu_pkg = zgpu.getPkg(&.{ zpool.pkg, zglfw.pkg }); - - exe.addPackage(zgpu_pkg); - exe.addPackage(zglfw.pkg); - - zgpu.link(exe); - zglfw.link(exe); -} -``` -**NOTE** - -`zgpu/libs/dawn` folder contains large binary files - Dawn/WebGPU static libs compiled for several platforms/architectures. -To avoid storing those files in your repo it is recommended to create a submodule pointing to the [dawn-bin](https://github.com/michal-z/dawn-bin) repo. - -To create the submodule run below commands in the root of your project: -``` -rm -rf libs/zgpu/libs -git submodule add -b main https://github.com/michal-z/dawn-bin libs/zgpu/libs/dawn -git submodule update --init --remote -``` -## Sample applications - -* [gui test (wgpu)](https://github.com/michal-z/zig-gamedev/tree/main/samples/gui_test_wgpu) -* [physically based rendering (wgpu)](https://github.com/michal-z/zig-gamedev/tree/main/samples/physically_based_rendering_wgpu) -* [bullet physics test (wgpu)](https://github.com/michal-z/zig-gamedev/tree/main/samples/bullet_physics_test_wgpu) -* [procedural mesh (wgpu)](https://github.com/michal-z/zig-gamedev/tree/main/samples/procedural_mesh_wgpu) -* [textured quad (wgpu)](https://github.com/michal-z/zig-gamedev/tree/main/samples/textured_quad_wgpu) -* [triangle (wgpu)](https://github.com/michal-z/zig-gamedev/tree/main/samples/triangle_wgpu) - -## Library overview - -Below you can find an overview of main `zgpu` features. - -### Init -```zig -const gctx = try zgpu.GraphicsContext.init(allocator, window); - -// When you are done: -gctx.deinit(allocator); -``` -### Uniforms - -* Implemented as a uniform buffer pool -* Easy to use -* Efficient - only one copy operation per frame - -```zig -struct DrawUniforms = extern struct { - object_to_world: zm.Mat, -}; -const mem = gctx.uniformsAllocate(DrawUniforms, 1); -mem.slice[0] = .{ .object_to_world = zm.transpose(zm.translation(...)) }; - -pass.setBindGroup(0, bind_group, &.{mem.offset}); -pass.drawIndexed(...); - -// When you are done encoding all commands for a frame: -gctx.submit(...); // Injects *one* copy operation to transfer *all* allocated uniforms -``` - -### Resource pools - -* Every GPU resource is identified by 32-bit integer handle -* All resources are stored in one system -* We keep basic info about each resource (size of the buffer, format of the texture, etc.) -* You can always check if resource is valid (very useful for async operations) -* System keeps basic info about resource dependencies, for example, `TextureViewHandle` knows about its -parent texture and becomes invalid when parent texture becomes invalid; `BindGroupHandle` knows -about all resources it binds so it becomes invalid if any of those resources become invalid - -```zig -const buffer_handle = gctx.createBuffer(...); - -if (gctx.isResourceValid(buffer_handle)) { - const buffer = gctx.lookupResource(buffer_handle).?; // Returns `wgpu.Buffer` - - const buffer_info = gctx.lookupResourceInfo(buffer_handle).?; // Returns `zgpu.BufferInfo` - std.debug.print("Buffer size is: {d}", .{buffer_info.size}); -} - -// If you want to destroy a resource before shutting down graphics context: -gctx.destroyResource(buffer_handle); - -``` -### Async shader compilation - -* Thanks to resource pools and resources identified by handles we can easily async compile all our shaders - -```zig -const DemoState = struct { - pipeline_handle: zgpu.PipelineLayoutHandle = .{}, - ... -}; -const demo = try allocator.create(DemoState); - -// Below call schedules pipeline compilation and returns immediately. When compilation is complete -// valid pipeline handle will be stored in `demo.pipeline_handle`. -gctx.createRenderPipelineAsync(allocator, pipeline_layout, pipeline_descriptor, &demo.pipeline_handle); - -// Pass using our pipeline will be skipped until compilation is ready -pass: { - const pipeline = gctx.lookupResource(demo.pipeline_handle) orelse break :pass; - ... - - pass.setPipeline(pipeline); - pass.drawIndexed(...); -} -``` - -### Mipmap generation on the GPU - -* wgpu API does not provide mipmap generator -* zgpu provides decent mipmap generator implemented in a compute shader -* It supports 2D textures, array textures and cubemap textures of any format -(`rgba8_unorm`, `rg16_float`, `rgba32_float`, etc.) -* Currently it requires that: `texture_width == texture_height and isPowerOfTwo(texture_width)` -* It takes ~260 microsec to generate all mips for 1024x1024 `rgba8_unorm` texture on GTX 1660 - -```zig -// Usage: -gctx.generateMipmaps(arena, command_encoder, texture_handle); -``` diff --git a/libs/zgpu/build.zig b/libs/zgpu/build.zig deleted file mode 100644 index 1c2c33829..000000000 --- a/libs/zgpu/build.zig +++ /dev/null @@ -1,99 +0,0 @@ -const std = @import("std"); - -pub fn getPkg(dependencies: []const std.build.Pkg) std.build.Pkg { - return .{ - .name = "zgpu", - .source = .{ .path = thisDir() ++ "/src/zgpu.zig" }, - .dependencies = dependencies, - }; -} - -pub fn buildTests( - b: *std.build.Builder, - build_mode: std.builtin.Mode, - target: std.zig.CrossTarget, -) *std.build.LibExeObjStep { - const tests = b.addTest(thisDir() ++ "/src/zgpu.zig"); - tests.setBuildMode(build_mode); - tests.setTarget(target); - link(tests); - return tests; -} - -pub fn link(exe: *std.build.LibExeObjStep) void { - const target = (std.zig.system.NativeTargetInfo.detect(exe.target) catch unreachable).target; - - const binaries_available = switch (target.os.tag) { - .windows => target.cpu.arch.isX86() and target.abi.isGnu(), - .linux => (target.cpu.arch.isX86() or target.cpu.arch.isAARCH64()) and target.abi.isGnu(), - .macos => blk: { - if (!target.cpu.arch.isX86() and !target.cpu.arch.isAARCH64()) break :blk false; - - // If min. target macOS version is lesser than the min version we have available, then - // our binary is incompatible with the target. - const min_available = std.builtin.Version{ .major = 12, .minor = 0 }; - if (target.os.version_range.semver.min.order(min_available) == .lt) break :blk false; - break :blk true; - }, - else => false, - }; - if (!binaries_available) { - const zig_triple = target.zigTriple(exe.builder.allocator) catch unreachable; - std.debug.print("Dawn binaries for {s} not available.", .{zig_triple}); - if (target.os.tag == .macos) { - if (target.cpu.arch.isX86()) std.debug.print( - "-> Did you mean to use -Dtarget=x86_64-macos.12 ?", - .{}, - ); - if (target.cpu.arch.isAARCH64()) std.debug.print( - "-> Did you mean to use -Dtarget=aarch64-macos.12 ?", - .{}, - ); - } - std.process.exit(1); - } - - switch (target.os.tag) { - .windows => { - exe.addLibraryPath(thisDir() ++ "/libs/dawn/x86_64-windows-gnu"); - - exe.linkSystemLibraryName("ole32"); - exe.linkSystemLibraryName("dxguid"); - }, - .linux => { - if (target.cpu.arch.isX86()) - exe.addLibraryPath(thisDir() ++ "/libs/dawn/x86_64-linux-gnu") - else - exe.addLibraryPath(thisDir() ++ "/libs/dawn/aarch64-linux-gnu"); - - exe.linkSystemLibraryName("X11"); - }, - .macos => { - if (target.cpu.arch.isX86()) - exe.addLibraryPath(thisDir() ++ "/libs/dawn/x86_64-macos-none") - else - exe.addLibraryPath(thisDir() ++ "/libs/dawn/aarch64-macos-none"); - - exe.linkFramework("Metal"); - exe.linkFramework("CoreGraphics"); - exe.linkFramework("Foundation"); - exe.linkFramework("IOKit"); - exe.linkFramework("IOSurface"); - exe.linkFramework("QuartzCore"); - }, - else => unreachable, - } - - exe.linkSystemLibraryName("dawn"); - exe.linkSystemLibraryName("c"); - exe.linkSystemLibraryName("c++"); - - exe.addIncludePath(thisDir() ++ "/libs/dawn/include"); - exe.addIncludePath(thisDir() ++ "/src"); - - exe.addCSourceFile(thisDir() ++ "/src/dawn.cpp", &.{"-std=c++17"}); -} - -inline fn thisDir() []const u8 { - return comptime std.fs.path.dirname(@src().file) orelse "."; -} diff --git a/libs/zgpu/libs/dawn b/libs/zgpu/libs/dawn deleted file mode 160000 index 5197842a5..000000000 --- a/libs/zgpu/libs/dawn +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5197842a596fce583892bf6d5945f9286d6ef497 diff --git a/libs/zgpu/src/common_wgsl.zig b/libs/zgpu/src/common_wgsl.zig deleted file mode 100644 index 5a91398d2..000000000 --- a/libs/zgpu/src/common_wgsl.zig +++ /dev/null @@ -1,99 +0,0 @@ -const std = @import("std"); - -pub fn csGenerateMipmaps(allocator: std.mem.Allocator, format: []const u8) [:0]const u8 { - const s0 = std.fmt.allocPrint( - allocator, - \\ @group(0) @binding(2) var dst_mipmap1: texture_storage_2d<{s}, write>; - \\ @group(0) @binding(3) var dst_mipmap2: texture_storage_2d<{s}, write>; - \\ @group(0) @binding(4) var dst_mipmap3: texture_storage_2d<{s}, write>; - \\ @group(0) @binding(5) var dst_mipmap4: texture_storage_2d<{s}, write>; - , - .{ format, format, format, format }, - ) catch unreachable; - defer allocator.free(s0); - return std.mem.joinZ(allocator, "\n\n", &.{ s0, cs_generate_mipmaps }) catch unreachable; -} - -// zig fmt: off -const cs_generate_mipmaps = -\\ struct Uniforms { -\\ src_mip_level: i32, -\\ num_mip_levels: u32, -\\ } -\\ @group(0) @binding(0) var uniforms: Uniforms; -\\ @group(0) @binding(1) var src_image: texture_2d; -\\ -\\ var red: array; -\\ var green: array; -\\ var blue: array; -\\ var alpha: array; -\\ -\\ fn storeColor(index: u32, color: vec4) { -\\ red[index] = color.x; -\\ green[index] = color.y; -\\ blue[index] = color.z; -\\ alpha[index] = color.w; -\\ } -\\ -\\ fn loadColor(index: u32) -> vec4 { -\\ return vec4(red[index], green[index], blue[index], alpha[index]); -\\ } -\\ -\\ @stage(compute) @workgroup_size(8, 8, 1) -\\ fn main( -\\ @builtin(global_invocation_id) global_invocation_id: vec3, -\\ @builtin(local_invocation_index) local_invocation_index : u32, -\\ ) { -\\ let x = i32(global_invocation_id.x * 2u); -\\ let y = i32(global_invocation_id.y * 2u); -\\ -\\ var s00 = textureLoad(src_image, vec2(x, y), uniforms.src_mip_level); -\\ var s10 = textureLoad(src_image, vec2(x + 1, y), uniforms.src_mip_level); -\\ var s01 = textureLoad(src_image, vec2(x, y + 1), uniforms.src_mip_level); -\\ var s11 = textureLoad(src_image, vec2(x + 1, y + 1), uniforms.src_mip_level); -\\ s00 = 0.25 * (s00 + s01 + s10 + s11); -\\ -\\ textureStore(dst_mipmap1, vec2(global_invocation_id.xy), s00); -\\ storeColor(local_invocation_index, s00); -\\ if (uniforms.num_mip_levels == 1u) { -\\ return; -\\ } -\\ workgroupBarrier(); -\\ -\\ if ((local_invocation_index & 0x9u) == 0u) { -\\ s10 = loadColor(local_invocation_index + 1u); -\\ s01 = loadColor(local_invocation_index + 8u); -\\ s11 = loadColor(local_invocation_index + 9u); -\\ s00 = 0.25 * (s00 + s01 + s10 + s11); -\\ textureStore(dst_mipmap2, vec2(global_invocation_id.xy / 2u), s00); -\\ storeColor(local_invocation_index, s00); -\\ } -\\ if (uniforms.num_mip_levels == 2u) { -\\ return; -\\ } -\\ workgroupBarrier(); -\\ -\\ if ((local_invocation_index & 0x1Bu) == 0u) { -\\ s10 = loadColor(local_invocation_index + 2u); -\\ s01 = loadColor(local_invocation_index + 16u); -\\ s11 = loadColor(local_invocation_index + 18u); -\\ s00 = 0.25 * (s00 + s01 + s10 + s11); -\\ textureStore(dst_mipmap3, vec2(global_invocation_id.xy / 4u), s00); -\\ storeColor(local_invocation_index, s00); -\\ } -\\ if (uniforms.num_mip_levels == 3u) { -\\ return; -\\ } -\\ workgroupBarrier(); -\\ -\\ if (local_invocation_index == 0u) { -\\ s10 = loadColor(local_invocation_index + 4u); -\\ s01 = loadColor(local_invocation_index + 32u); -\\ s11 = loadColor(local_invocation_index + 36u); -\\ s00 = 0.25 * (s00 + s01 + s10 + s11); -\\ textureStore(dst_mipmap4, vec2(global_invocation_id.xy / 8u), s00); -\\ storeColor(local_invocation_index, s00); -\\ } -\\ } -; -// zig fmt: on diff --git a/libs/zgpu/src/dawn.cpp b/libs/zgpu/src/dawn.cpp deleted file mode 100644 index c2eae484a..000000000 --- a/libs/zgpu/src/dawn.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct DawnNativeInstanceImpl* DawnNativeInstance; - -DawnNativeInstance dawnNativeCreateInstance(void) { - return reinterpret_cast(new dawn_native::Instance()); -} - -void dawnNativeDestroyInstance(DawnNativeInstance instance) { - delete reinterpret_cast(instance); -} - -WGPUInstance dawnNativeGetWgpuInstance(DawnNativeInstance instance) { - return reinterpret_cast(instance)->Get(); -} - -void dawnNativeDiscoverDefaultAdapters(DawnNativeInstance instance) { - dawn_native::Instance* self = reinterpret_cast(instance); - self->DiscoverDefaultAdapters(); -} - -const DawnProcTable* dawnNativeGetProcs(void) { - return &dawn_native::GetProcs(); -} - -#ifdef __cplusplus -} -#endif diff --git a/libs/zgpu/src/wgpu.zig b/libs/zgpu/src/wgpu.zig deleted file mode 100644 index d92916d84..000000000 --- a/libs/zgpu/src/wgpu.zig +++ /dev/null @@ -1,2775 +0,0 @@ -const std = @import("std"); - -pub const AdapterType = enum(u32) { - discrete_gpu, - integrated_gpu, - cpu, - unknown, -}; - -pub const AddressMode = enum(u32) { - repeat = 0x00000000, - mirror_repeat = 0x00000001, - clamp_to_edge = 0x00000002, -}; - -pub const AlphaMode = enum(u32) { - premultiplied = 0x00000000, - unpremultiplied = 0x00000001, - opaq = 0x00000002, -}; - -pub const BackendType = enum(u32) { - nul, - webgpu, - d3d11, - d3d12, - metal, - vulkan, - opengl, - opengles, -}; - -pub const BlendFactor = enum(u32) { - zero = 0x00000000, - one = 0x00000001, - src = 0x00000002, - one_minus_src = 0x00000003, - src_alpha = 0x00000004, - one_minus_src_alpha = 0x00000005, - dst = 0x00000006, - one_minus_dst = 0x00000007, - dst_alpha = 0x00000008, - one_minus_dst_alpha = 0x00000009, - src_alpha_saturated = 0x0000000A, - constant = 0x0000000B, - one_minus_constant = 0x0000000C, -}; - -pub const BlendOperation = enum(u32) { - add = 0x00000000, - subtract = 0x00000001, - reverse_subtract = 0x00000002, - min = 0x00000003, - max = 0x00000004, -}; - -pub const BufferBindingType = enum(u32) { - undef = 0x00000000, - uniform = 0x00000001, - storage = 0x00000002, - read_only_storage = 0x00000003, -}; - -pub const BufferMapAsyncStatus = enum(u32) { - success = 0x00000000, - err = 0x00000001, - unknown = 0x00000002, - device_lost = 0x00000003, - destroyed_before_callback = 0x00000004, - unmapped_before_callback = 0x00000005, -}; - -pub const CompareFunction = enum(u32) { - undef = 0x00000000, - never = 0x00000001, - less = 0x00000002, - less_equal = 0x00000003, - greater = 0x00000004, - greater_equal = 0x00000005, - equal = 0x00000006, - not_equal = 0x00000007, - always = 0x00000008, -}; - -pub const CompilationInfoRequestStatus = enum(u32) { - success = 0x00000000, - err = 0x00000001, - device_lost = 0x00000002, - unknown = 0x00000003, -}; - -pub const CompilationMessageType = enum(u32) { - err = 0x00000000, - warning = 0x00000001, - info = 0x00000002, -}; - -pub const ComputePassTimestampLocation = enum(u32) { - beginning = 0x00000000, - end = 0x00000001, -}; - -pub const CreatePipelineAsyncStatus = enum(u32) { - success = 0x00000000, - err = 0x00000001, - device_lost = 0x00000002, - device_destroyed = 0x00000003, - unknown = 0x00000004, -}; - -pub const CullMode = enum(u32) { - none = 0x00000000, - front = 0x00000001, - back = 0x00000002, -}; - -pub const DeviceLostReason = enum(u32) { - undef = 0x00000000, - destroyed = 0x00000001, -}; - -pub const ErrorFilter = enum(u32) { - validation = 0x00000000, - out_of_memory = 0x00000001, -}; - -pub const ErrorType = enum(u32) { - no_error = 0x00000000, - validation = 0x00000001, - out_of_memory = 0x00000002, - unknown = 0x00000003, - device_lost = 0x00000004, -}; - -pub const FeatureName = enum(u32) { - undef = 0x00000000, - depth24_unorm_stencil8 = 0x00000002, - depth32_float_stencil8 = 0x00000003, - timestamp_query = 0x00000004, - pipeline_statistics_query = 0x00000005, - texture_compression_bc = 0x00000006, - texture_compression_etc2 = 0x00000007, - texture_compression_astc = 0x00000008, - indirect_first_instance = 0x00000009, - depth_clamping = 0x000003E8, - dawn_shader_float16 = 0x000003E9, - dawn_internal_usages = 0x000003EA, - dawn_multi_planar_formats = 0x000003EB, - dawn_native = 0x000003EC, - chromium_experimental_dp4a = 0x000003ED, -}; - -pub const FilterMode = enum(u32) { - nearest = 0x00000000, - linear = 0x00000001, -}; - -pub const FrontFace = enum(u32) { - ccw = 0x00000000, - cw = 0x00000001, -}; - -pub const IndexFormat = enum(u32) { - undef = 0x00000000, - uint16 = 0x00000001, - uint32 = 0x00000002, -}; - -pub const LoadOp = enum(u32) { - undef = 0x00000000, - clear = 0x00000001, - load = 0x00000002, -}; - -pub const LoggingType = enum(u32) { - verbose = 0x00000000, - info = 0x00000001, - warning = 0x00000002, - err = 0x00000003, -}; - -pub const PipelineStatisticName = enum(u32) { - vertex_shader_invocations = 0x00000000, - clipper_invocations = 0x00000001, - clipper_primitives_out = 0x00000002, - fragment_shader_invocations = 0x00000003, - compute_shader_invocations = 0x00000004, -}; - -pub const PowerPreference = enum(u32) { - undef = 0x00000000, - low_power = 0x00000001, - high_performance = 0x00000002, -}; - -pub const PresentMode = enum(u32) { - immediate = 0x00000000, - mailbox = 0x00000001, - fifo = 0x00000002, -}; - -pub const PrimitiveTopology = enum(u32) { - point_list = 0x00000000, - line_list = 0x00000001, - line_strip = 0x00000002, - triangle_list = 0x00000003, - triangle_strip = 0x00000004, -}; - -pub const QueryType = enum(u32) { - occlusion = 0x00000000, - pipeline_statistics = 0x00000001, - timestamp = 0x00000002, -}; - -pub const QueueWorkDoneStatus = enum(u32) { - success = 0x00000000, - err = 0x00000001, - unknown = 0x00000002, - device_lost = 0x00000003, -}; - -pub const RenderPassTimestampLocation = enum(u32) { - beginning = 0x00000000, - end = 0x00000001, -}; - -pub const RequestAdapterStatus = enum(u32) { - success = 0x00000000, - unavailable = 0x00000001, - err = 0x00000002, - unknown = 0x00000003, -}; - -pub const RequestDeviceStatus = enum(u32) { - success = 0x00000000, - err = 0x00000001, - unknown = 0x00000002, -}; - -pub const SurfaceDescriptorFromMetalLayer = extern struct { - chain: ChainedStruct, - layer: *anyopaque, -}; - -pub const SurfaceDescriptorFromWindowsHWND = extern struct { - chain: ChainedStruct, - hinstance: *anyopaque, - hwnd: *anyopaque, -}; - -pub const SurfaceDescriptorFromXlibWindow = extern struct { - chain: ChainedStruct, - display: *anyopaque, - window: u32, -}; - -pub const SurfaceDescriptorFromWindowsCoreWindow = extern struct { - chain: ChainedStruct, - core_window: *anyopaque, -}; - -pub const SurfaceDescriptorFromWindowsSwapChainPanel = extern struct { - chain: ChainedStruct, - swap_chain_panel: *anyopaque, -}; - -pub const SurfaceDescriptorFromCanvasHTMLSelector = extern struct { - chain: ChainedStruct, - selector: [*:0]const u8, -}; - -pub const StructType = enum(u32) { - invalid = 0x00000000, - surface_descriptor_from_metal_layer = 0x00000001, - surface_descriptor_from_windows_hwnd = 0x00000002, - surface_descriptor_from_xlib_window = 0x00000003, - surface_descriptor_from_canvas_html_selector = 0x00000004, - shader_module_spirv_descriptor = 0x00000005, - shader_module_wgsl_descriptor = 0x00000006, - surface_descriptor_from_wayland_surface = 0x00000008, - surface_descriptor_from_android_native_window = 0x00000009, - surface_descriptor_from_windows_core_window = 0x0000000B, - external_texture_binding_entry = 0x0000000C, - external_texture_binding_layout = 0x0000000D, - surface_descriptor_from_windows_swap_chain_panel = 0x0000000E, - dawn_texture_internal_usage_descriptor = 0x000003E8, - primitive_depth_clamping_state = 0x000003E9, - dawn_toggles_device_descriptor = 0x000003EA, - dawn_encoder_internal_usage_descriptor = 0x000003EB, - dawn_instance_descriptor = 0x000003EC, - dawn_cache_device_descriptor = 0x000003ED, -}; - -pub const SamplerBindingType = enum(u32) { - undef = 0x00000000, - filtering = 0x00000001, - non_filtering = 0x00000002, - comparison = 0x00000003, -}; - -pub const StencilOperation = enum(u32) { - keep = 0x00000000, - zero = 0x00000001, - replace = 0x00000002, - invert = 0x00000003, - increment_lamp = 0x00000004, - decrement_clamp = 0x00000005, - increment_wrap = 0x00000006, - decrement_wrap = 0x00000007, -}; - -pub const StorageTextureAccess = enum(u32) { - undef = 0x00000000, - write_only = 0x00000001, -}; - -pub const StoreOp = enum(u32) { - undef = 0x00000000, - store = 0x00000001, - discard = 0x00000002, -}; - -pub const TextureAspect = enum(u32) { - all = 0x00000000, - stencil_only = 0x00000001, - depth_only = 0x00000002, - plane0_only = 0x00000003, - plane1_only = 0x00000004, -}; - -pub const TextureComponentType = enum(u32) { - float = 0x00000000, - sint = 0x00000001, - uint = 0x00000002, - depth_comparison = 0x00000003, -}; - -pub const TextureDimension = enum(u32) { - tdim_1d = 0x00000000, - tdim_2d = 0x00000001, - tdim_3d = 0x00000002, -}; - -pub const TextureFormat = enum(u32) { - undef = 0x00000000, - r8_unorm = 0x00000001, - r8_snorm = 0x00000002, - r8_uint = 0x00000003, - r8_sint = 0x00000004, - r16_uint = 0x00000005, - r16_sint = 0x00000006, - r16_float = 0x00000007, - rg8_unorm = 0x00000008, - rg8_snorm = 0x00000009, - rg8_uint = 0x0000000a, - rg8_sint = 0x0000000b, - r32_float = 0x0000000c, - r32_uint = 0x0000000d, - r32_sint = 0x0000000e, - rg16_uint = 0x0000000f, - rg16_sint = 0x00000010, - rg16_float = 0x00000011, - rgba8_unorm = 0x00000012, - rgba8_unorm_srgb = 0x00000013, - rgba8_snorm = 0x00000014, - rgba8_uint = 0x00000015, - rgba8_sint = 0x00000016, - bgra8_unorm = 0x00000017, - bgra8_unorm_srgb = 0x00000018, - rgb10_a2_unorm = 0x00000019, - rg11_b10_ufloat = 0x0000001a, - rgb9_e5_ufloat = 0x0000001b, - rg32_float = 0x0000001c, - rg32_uint = 0x0000001d, - rg32_sint = 0x0000001e, - rgba16_uint = 0x0000001f, - rgba16_sint = 0x00000020, - rgba16_float = 0x00000021, - rgba32_float = 0x00000022, - rgba32_uint = 0x00000023, - rgba32_sint = 0x00000024, - stencil8 = 0x00000025, - depth16_unorm = 0x00000026, - depth24_plus = 0x00000027, - depth24_plus_stencil8 = 0x00000028, - depth32_float = 0x00000029, - depth32_float_stencil8 = 0x0000002a, - bc1_rgba_unorm = 0x0000002b, - bc1_rgba_unorm_srgb = 0x0000002c, - bc2_rgba_unorm = 0x0000002d, - bc2_rgba_unorm_srgb = 0x0000002e, - bc3_rgba_unorm = 0x0000002f, - bc3_rgba_unorm_srgb = 0x00000030, - bc4_runorm = 0x00000031, - bc4_rsnorm = 0x00000032, - bc5_rg_unorm = 0x00000033, - bc5_rg_snorm = 0x00000034, - bc6_hrgb_ufloat = 0x00000035, - bc6_hrgb_float = 0x00000036, - bc7_rgba_unorm = 0x00000037, - bc7_rgba_unorm_srgb = 0x00000038, - etc2_rgb8_unorm = 0x00000039, - etc2_rgb8_unorm_srgb = 0x0000003a, - etc2_rgb8_a1_unorm = 0x0000003b, - etc2_rgb8_a1_unorm_srgb = 0x0000003c, - etc2_rgba8_unorm = 0x0000003d, - etc2_rgba8_unorm_srgb = 0x0000003e, - eacr11_unorm = 0x0000003f, - eacr11_snorm = 0x00000040, - eacrg11_unorm = 0x00000041, - eacrg11_snorm = 0x00000042, - astc4x4_unorm = 0x00000043, - astc4x4_unorm_srgb = 0x00000044, - astc5x4_unorm = 0x00000045, - astc5x4_unorm_srgb = 0x00000046, - astc5x5_unorm = 0x00000047, - astc5x5_unorm_srgb = 0x00000048, - astc6x5_unorm = 0x00000049, - astc6x5_unorm_srgb = 0x0000004a, - astc6x6_unorm = 0x0000004b, - astc6x6_unorm_srgb = 0x0000004c, - astc8x5_unorm = 0x0000004d, - astc8x5_unorm_srgb = 0x0000004e, - astc8x6_unorm = 0x0000004f, - astc8x6_unorm_srgb = 0x00000050, - astc8x8_unorm = 0x00000051, - astc8x8_unorm_srgb = 0x00000052, - astc10x5_unorm = 0x00000053, - astc10x5_unorm_srgb = 0x00000054, - astc10x6_unorm = 0x00000055, - astc10x6_unorm_srgb = 0x00000056, - astc10x8_unorm = 0x00000057, - astc10x8_unorm_srgb = 0x00000058, - astc10x10_unorm = 0x00000059, - astc10x10_unorm_srgb = 0x0000005a, - astc12x10_unorm = 0x0000005b, - astc12x10_unorm_srgb = 0x0000005c, - astc12x12_unorm = 0x0000005d, - astc12x12_unorm_srgb = 0x0000005e, - r8_bg8_biplanar420_unorm = 0x0000005f, -}; - -pub const TextureSampleType = enum(u32) { - undef = 0x00000000, - float = 0x00000001, - unfilterable_float = 0x00000002, - depth = 0x00000003, - sint = 0x00000004, - uint = 0x00000005, -}; - -pub const TextureViewDimension = enum(u32) { - undef = 0x00000000, - tvdim_1d = 0x00000001, - tvdim_2d = 0x00000002, - tvdim_2d_array = 0x00000003, - tvdim_cube = 0x00000004, - tvdim_cube_array = 0x00000005, - tvdim_3d = 0x00000006, -}; - -pub const VertexFormat = enum(u32) { - undef = 0x00000000, - uint8x2 = 0x00000001, - uint8x4 = 0x00000002, - sint8x2 = 0x00000003, - sint8x4 = 0x00000004, - unorm8x2 = 0x00000005, - unorm8x4 = 0x00000006, - snorm8x2 = 0x00000007, - snorm8x4 = 0x00000008, - uint16x2 = 0x00000009, - uint16x4 = 0x0000000A, - sint16x2 = 0x0000000B, - sint16x4 = 0x0000000C, - unorm16x2 = 0x0000000D, - unorm16x4 = 0x0000000E, - snorm16x2 = 0x0000000F, - snorm16x4 = 0x00000010, - float16x2 = 0x00000011, - float16x4 = 0x00000012, - float32 = 0x00000013, - float32x2 = 0x00000014, - float32x3 = 0x00000015, - float32x4 = 0x00000016, - uint32 = 0x00000017, - uint32x2 = 0x00000018, - uint32x3 = 0x00000019, - uint32x4 = 0x0000001A, - sint32 = 0x0000001B, - sint32x2 = 0x0000001C, - sint32x3 = 0x0000001D, - sint32x4 = 0x0000001E, -}; - -pub const VertexStepMode = enum(u32) { - vertex = 0x00000000, - instance = 0x00000001, - vertex_buffer_not_used = 0x00000002, -}; - -pub const BufferUsage = packed struct(u32) { - map_read: bool = false, - map_write: bool = false, - copy_src: bool = false, - copy_dst: bool = false, - index: bool = false, - vertex: bool = false, - uniform: bool = false, - storage: bool = false, - indirect: bool = false, - query_resolve: bool = false, - _padding: u22 = 0, -}; - -pub const ColorWriteMask = packed struct(u32) { - red: bool = false, - green: bool = false, - blue: bool = false, - alpha: bool = false, - _padding: u28 = 0, - - pub const all = ColorWriteMask{ .red = true, .green = true, .blue = true, .alpha = true }; -}; - -pub const MapMode = packed struct(u32) { - read: bool = false, - write: bool = false, - _padding: u30 = 0, -}; - -pub const ShaderStage = packed struct(u32) { - vertex: bool = false, - fragment: bool = false, - compute: bool = false, - _padding: u29 = 0, -}; - -pub const TextureUsage = packed struct(u32) { - copy_src: bool = false, - copy_dst: bool = false, - texture_binding: bool = false, - storage_binding: bool = false, - render_attachment: bool = false, - present: bool = false, - _padding: u26 = 0, -}; - -pub const ChainedStruct = extern struct { - next: ?*const ChainedStruct, - struct_type: StructType, -}; - -pub const ChainedStructOut = extern struct { - next: ?*ChainedStructOut, - struct_type: StructType, -}; - -pub const AdapterProperties = extern struct { - next_in_chain: ?*ChainedStructOut = null, - vendor_id: u32, - vendor_name: [*:0]const u8, - architecture: [*:0]const u8, - device_id: u32, - name: [*:0]const u8, - driver_description: [*:0]const u8, - adapter_type: AdapterType, - backend_type: BackendType, -}; - -pub const BindGroupEntry = extern struct { - next_in_chain: ?*const ChainedStruct = null, - binding: u32, - buffer: ?Buffer = null, - offset: u64 = 0, - size: u64, - sampler: ?Sampler = null, - texture_view: ?TextureView = null, -}; - -pub const BindGroupDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - layout: BindGroupLayout, - entry_count: u32, - entries: ?[*]const BindGroupEntry, -}; - -pub const BufferBindingLayout = extern struct { - next_in_chain: ?*const ChainedStruct = null, - binding_type: BufferBindingType = .uniform, - has_dynamic_offset: bool = false, - min_binding_size: u64 = 0, -}; - -pub const SamplerBindingLayout = extern struct { - next_in_chain: ?*const ChainedStruct = null, - binding_type: SamplerBindingType = .filtering, -}; - -pub const TextureBindingLayout = extern struct { - next_in_chain: ?*const ChainedStruct = null, - sample_type: TextureSampleType = .float, - view_dimension: TextureViewDimension = .tvdim_2d, - multisampled: bool = false, -}; - -pub const StorageTextureBindingLayout = extern struct { - next_in_chain: ?*const ChainedStruct = null, - access: StorageTextureAccess = .write_only, - format: TextureFormat, - view_dimension: TextureViewDimension = .tvdim_2d, -}; - -pub const BindGroupLayoutEntry = extern struct { - next_in_chain: ?*const ChainedStruct = null, - binding: u32, - visibility: ShaderStage, - buffer: BufferBindingLayout = .{ .binding_type = .undef }, - sampler: SamplerBindingLayout = .{ .binding_type = .undef }, - texture: TextureBindingLayout = .{ .sample_type = .undef }, - storage_texture: StorageTextureBindingLayout = .{ .access = .undef, .format = .undef }, - - /// Helper to create a buffer BindGroupLayoutEntry. - pub fn buffer( - binding: u32, - visibility: ShaderStage, - binding_type: BufferBindingType, - has_dynamic_offset: bool, - min_binding_size: u64, - ) BindGroupLayoutEntry { - return .{ - .binding = binding, - .visibility = visibility, - .buffer = .{ - .binding_type = binding_type, - .has_dynamic_offset = has_dynamic_offset, - .min_binding_size = min_binding_size, - }, - }; - } - - /// Helper to create a sampler BindGroupLayoutEntry. - pub fn sampler(binding: u32, visibility: ShaderStage, binding_type: SamplerBindingType) BindGroupLayoutEntry { - return .{ - .binding = binding, - .visibility = visibility, - .sampler = .{ .binding_type = binding_type }, - }; - } - - /// Helper to create a texture BindGroupLayoutEntry. - pub fn texture( - binding: u32, - visibility: ShaderStage, - sample_type: TextureSampleType, - view_dimension: TextureViewDimension, - multisampled: bool, - ) BindGroupLayoutEntry { - return .{ - .binding = binding, - .visibility = visibility, - .texture = .{ - .sample_type = sample_type, - .view_dimension = view_dimension, - .multisampled = multisampled, - }, - }; - } - - /// Helper to create a storage texture BindGroupLayoutEntry. - pub fn storageTexture( - binding: u32, - visibility: ShaderStage, - access: StorageTextureAccess, - format: TextureFormat, - view_dimension: TextureViewDimension, - ) BindGroupLayoutEntry { - return .{ - .binding = binding, - .visibility = visibility, - .storage_texture = .{ - .access = access, - .format = format, - .view_dimension = view_dimension, - }, - }; - } -}; - -pub const BindGroupLayoutDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - entry_count: u32, - entries: ?[*]const BindGroupLayoutEntry, -}; - -pub const BufferDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - usage: BufferUsage, - size: u64, - mapped_at_creation: bool = false, -}; - -pub const CommandEncoderDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, -}; - -pub const ConstantEntry = extern struct { - next_in_chain: ?*const ChainedStruct = null, - key: [*:0]const u8, - value: f64, -}; - -pub const ProgrammableStageDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - module: ShaderModule, - entry_point: [*:0]const u8, - constant_count: u32 = 0, - constants: ?[*]const ConstantEntry = null, -}; - -pub const ComputePipelineDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - layout: ?PipelineLayout = null, - compute: ProgrammableStageDescriptor, -}; - -pub const ExternalTextureDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - plane0: TextureView, - plane1: TextureView, - do_yuv_to_rgb_conversion_only: bool, - yuv_to_rgb_conversion_matrix: ?[*]const f32, - src_transfer_function_parameters: [*]const f32, - dst_transfer_function_parameters: [*]const f32, - gamut_conversion_matrix: [*]const f32, -}; - -pub const PipelineLayoutDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - bind_group_layout_count: u32, - bind_group_layouts: ?[*]const BindGroupLayout, -}; - -pub const QuerySetDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - query_type: QueryType, - count: u32, - pipeline_statistics: ?[*]const PipelineStatisticName, - pipeline_statistics_count: u32, -}; - -pub const RenderBundleEncoderDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - color_formats_count: u32, - color_formats: ?[*]const TextureFormat, - depth_stencil_format: TextureFormat, - sample_count: u32, - depth_read_only: bool, - stencil_read_only: bool, -}; - -pub const VertexAttribute = extern struct { - format: VertexFormat, - offset: u64, - shader_location: u32, -}; - -pub const VertexBufferLayout = extern struct { - array_stride: u64, - step_mode: VertexStepMode = .vertex, - attribute_count: u32, - attributes: [*]const VertexAttribute, -}; - -pub const VertexState = extern struct { - next_in_chain: ?*const ChainedStruct = null, - module: ShaderModule, - entry_point: [*:0]const u8, - constant_count: u32 = 0, - constants: ?[*]const ConstantEntry = null, - buffer_count: u32 = 0, - buffers: ?[*]const VertexBufferLayout = null, -}; - -pub const BlendComponent = extern struct { - operation: BlendOperation = .add, - src_factor: BlendFactor = .one, - dst_factor: BlendFactor = .zero, -}; - -pub const BlendState = extern struct { - color: BlendComponent, - alpha: BlendComponent, -}; - -pub const ColorTargetState = extern struct { - next_in_chain: ?*const ChainedStruct = null, - format: TextureFormat, - blend: ?*const BlendState = null, - write_mask: ColorWriteMask = ColorWriteMask.all, -}; - -pub const FragmentState = extern struct { - next_in_chain: ?*const ChainedStruct = null, - module: ShaderModule, - entry_point: [*:0]const u8, - constant_count: u32 = 0, - constants: ?[*]const ConstantEntry = null, - target_count: u32 = 0, - targets: ?[*]const ColorTargetState = null, -}; - -pub const PrimitiveState = extern struct { - next_in_chain: ?*const ChainedStruct = null, - topology: PrimitiveTopology = .triangle_list, - strip_index_format: IndexFormat = .undef, - front_face: FrontFace = .ccw, - cull_mode: CullMode = .none, -}; - -pub const StencilFaceState = extern struct { - compare: CompareFunction = .always, - fail_op: StencilOperation = .keep, - depth_fail_op: StencilOperation = .keep, - pass_op: StencilOperation = .keep, -}; - -pub const DepthStencilState = extern struct { - next_in_chain: ?*const ChainedStruct = null, - format: TextureFormat, - depth_write_enabled: bool = false, - depth_compare: CompareFunction = .always, - stencil_front: StencilFaceState = .{}, - stencil_back: StencilFaceState = .{}, - stencil_read_mask: u32 = 0xffff_ffff, - stencil_write_mask: u32 = 0xffff_ffff, - depth_bias: i32 = 0, - depth_bias_slope_scale: f32 = 0.0, - depth_bias_clamp: f32 = 0.0, -}; - -pub const MultisampleState = extern struct { - next_in_chain: ?*const ChainedStruct = null, - count: u32 = 1, - mask: u32 = 0xffff_ffff, - alpha_to_coverage_enabled: bool = false, -}; - -pub const RenderPipelineDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - layout: ?PipelineLayout = null, - vertex: VertexState, - primitive: PrimitiveState = .{}, - depth_stencil: ?*const DepthStencilState = null, - multisample: MultisampleState = .{}, - fragment: ?*const FragmentState = null, -}; - -pub const SamplerDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - address_mode_u: AddressMode = .clamp_to_edge, - address_mode_v: AddressMode = .clamp_to_edge, - address_mode_w: AddressMode = .clamp_to_edge, - mag_filter: FilterMode = .nearest, - min_filter: FilterMode = .nearest, - mipmap_filter: FilterMode = .nearest, - lod_min_clamp: f32 = 0.0, - lod_max_clamp: f32 = 32.0, - compare: CompareFunction = .undef, - max_anisotropy: u16 = 1, -}; - -pub const ShaderModuleDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, -}; - -pub const ShaderModuleWgslDescriptor = extern struct { - chain: ChainedStruct, - source: [*:0]const u8, -}; - -pub const SwapChainDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - usage: TextureUsage, - format: TextureFormat, - width: u32, - height: u32, - present_mode: PresentMode, - implementation: u64, -}; - -pub const Extent3D = extern struct { - width: u32, - height: u32 = 1, - depth_or_array_layers: u32 = 1, -}; - -pub const TextureDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - usage: TextureUsage, - dimension: TextureDimension = .tdim_2d, - size: Extent3D, - format: TextureFormat, - mip_level_count: u32 = 1, - sample_count: u32 = 1, - view_format_count: u32 = 0, - view_formats: ?[*]const TextureFormat = null, -}; - -pub const Limits = extern struct { - max_texture_dimension_1d: u32, - max_texture_dimension_2d: u32, - max_texture_dimension_3d: u32, - max_texture_array_layers: u32, - max_bind_groups: u32, - max_dynamic_uniform_buffers_per_pipeline_layout: u32, - max_dynamic_storage_buffers_per_pipeline_layout: u32, - max_sampled_textures_per_shader_stage: u32, - max_samplers_per_shader_stage: u32, - max_storage_buffers_per_shader_stage: u32, - max_storage_textures_per_shader_stage: u32, - max_uniform_buffers_per_shader_stage: u32, - max_uniform_buffer_binding_size: u64, - max_storage_buffer_binding_size: u64, - min_uniform_buffer_offset_alignment: u32, - min_storage_buffer_offset_alignment: u32, - max_vertex_buffers: u32, - max_vertex_attributes: u32, - max_vertex_buffer_array_stride: u32, - max_inter_stage_shader_components: u32, - max_compute_workgroup_storage_size: u32, - max_compute_invocations_per_workgroup: u32, - max_compute_workgroup_size_x: u32, - max_compute_workgroup_size_y: u32, - max_compute_workgroup_size_z: u32, - max_compute_workgroups_per_dimension: u32, -}; - -pub const RequiredLimits = extern struct { - next_in_chain: ?*const ChainedStruct = null, - limits: Limits, -}; - -pub const SupportedLimits = extern struct { - next_in_chain: ?*ChainedStructOut = null, - limits: Limits, -}; - -pub const QueueDescription = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, -}; - -pub const DeviceDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - required_features_count: u32 = 0, - required_features: ?[*]const FeatureName = null, - required_limits: ?[*]const RequiredLimits = null, - default_queue: QueueDescription = .{}, -}; - -pub const SurfaceDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, -}; - -pub const RequestAdapterOptions = extern struct { - next_in_chain: ?*const ChainedStruct = null, - compatible_surface: ?Surface = null, - power_preference: PowerPreference, - force_fallback_adapter: bool = false, -}; - -pub const ComputePassTimestampWrite = extern struct { - query_set: QuerySet, - query_index: u32, - location: ComputePassTimestampLocation, -}; - -pub const RenderPassTimestampWrite = extern struct { - query_set: QuerySet, - query_index: u32, - location: RenderPassTimestampLocation, -}; - -pub const ComputePassDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - timestamp_write_count: u32, - timestamp_writes: ?[*]const ComputePassTimestampWrite, -}; - -pub const Color = extern struct { - r: f64, - g: f64, - b: f64, - a: f64, -}; - -pub const RenderPassColorAttachment = extern struct { - view: TextureView, - resolve_target: ?TextureView = null, - load_op: LoadOp, - store_op: StoreOp, - clear_color: Color = .{ - .r = std.math.nan(f32), - .g = std.math.nan(f32), - .b = std.math.nan(f32), - .a = std.math.nan(f32), - }, - clear_value: Color = .{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 0.0 }, -}; - -pub const RenderPassDepthStencilAttachment = extern struct { - view: TextureView, - depth_load_op: LoadOp = .undef, - depth_store_op: StoreOp = .undef, - clear_depth: f32 = std.math.nan(f32), - depth_clear_value: f32 = 0.0, - depth_read_only: bool = false, - stencil_load_op: LoadOp = .undef, - stencil_store_op: StoreOp = .undef, - clear_stencil: u32 = 0, - stencil_clear_value: u32 = 0, - stencil_read_only: bool = false, -}; - -pub const RenderPassDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - color_attachment_count: u32, - color_attachments: ?[*]const RenderPassColorAttachment, - depth_stencil_attachment: ?*const RenderPassDepthStencilAttachment = null, - occlusion_query_set: ?QuerySet = null, - timestamp_write_count: u32 = 0, - timestamp_writes: ?[*]const RenderPassTimestampWrite = null, -}; - -pub const TextureDataLayout = extern struct { - next_in_chain: ?*const ChainedStruct = null, - offset: u64 = 0, - bytes_per_row: u32, - rows_per_image: u32, -}; - -pub const Origin3D = extern struct { - x: u32 = 0, - y: u32 = 0, - z: u32 = 0, -}; - -pub const ImageCopyBuffer = extern struct { - next_in_chain: ?*const ChainedStruct = null, - layout: TextureDataLayout, - buffer: Buffer, -}; - -pub const ImageCopyTexture = extern struct { - next_in_chain: ?*const ChainedStruct = null, - texture: Texture, - mip_level: u32 = 0, - origin: Origin3D = .{}, - aspect: TextureAspect = .all, -}; - -pub const CommandBufferDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, -}; - -pub const CopyTextureForBrowserOptions = extern struct { - next_in_chain: ?*const ChainedStruct = null, - flip_y: bool, - needs_color_space_conversion: bool, - src_alpha_mode: AlphaMode, - src_transfer_function_parameters: ?[*]const f32, - conversion_matrix: ?[*]const f32, - dst_transfer_function_parameters: ?[*]const f32, - dst_alpha_mode: AlphaMode, - internal_usage: bool, -}; - -pub const TextureViewDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, - format: TextureFormat = .undef, - dimension: TextureViewDimension = .undef, - base_mip_level: u32 = 0, - mip_level_count: u32 = 0xffff_ffff, - base_array_layer: u32 = 0, - array_layer_count: u32 = 0xffff_ffff, - aspect: TextureAspect = .all, -}; - -pub const CompilationMessage = extern struct { - next_in_chain: ?*const ChainedStruct = null, - message: ?[*:0]const u8 = null, - message_type: CompilationMessageType, - line_num: u64, - line_pos: u64, - offset: u64, - length: u64, -}; - -pub const CompilationInfo = extern struct { - next_in_chain: ?*const ChainedStruct = null, - message_count: u32, - messages: ?[*]const CompilationMessage, -}; - -pub const RenderBundleDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, - label: ?[*:0]const u8 = null, -}; - -pub const CreateComputePipelineAsyncCallback = *const fn ( - status: CreatePipelineAsyncStatus, - pipeline: ComputePipeline, - message: ?[*:0]const u8, - userdata: ?*anyopaque, -) callconv(.C) void; - -pub const CreateRenderPipelineAsyncCallback = *const fn ( - status: CreatePipelineAsyncStatus, - pipeline: RenderPipeline, - message: ?[*:0]const u8, - userdata: ?*anyopaque, -) callconv(.C) void; - -pub const ErrorCallback = *const fn ( - err_type: ErrorType, - message: [*:0]const u8, - userdata: ?*anyopaque, -) callconv(.C) void; - -pub const LoggingCallback = *const fn ( - log_type: LoggingType, - message: ?[*:0]const u8, - userdata: ?*anyopaque, -) callconv(.C) void; - -pub const DeviceLostCallback = *const fn ( - reason: DeviceLostReason, - message: ?[*:0]const u8, - userdata: ?*anyopaque, -) callconv(.C) void; - -pub const RequestAdapterCallback = *const fn ( - status: RequestAdapterStatus, - adapter: Adapter, - message: ?[*:0]const u8, - userdata: ?*anyopaque, -) callconv(.C) void; - -pub const RequestDeviceCallback = *const fn ( - status: RequestDeviceStatus, - device: Device, - message: ?[*:0]const u8, - userdata: ?*anyopaque, -) callconv(.C) void; - -pub const BufferMapCallback = *const fn ( - status: BufferMapAsyncStatus, - userdata: ?*anyopaque, -) callconv(.C) void; - -pub const QueueWorkDoneCallback = *const fn ( - status: QueueWorkDoneStatus, - userdata: ?*anyopaque, -) callconv(.C) void; - -pub const CompilationInfoCallback = *const fn ( - status: CompilationInfoRequestStatus, - userdata: ?*anyopaque, -) callconv(.C) void; - -pub const Adapter = *opaque { - pub inline fn createDevice(adapter: Adapter, descriptor: DeviceDescriptor) Device { - return wgpuAdapterCreateDevice(adapter, &descriptor); - } - extern fn wgpuAdapterCreateDevice(adapter: Adapter, descriptor: *const DeviceDescriptor) Device; - - pub inline fn enumerateFeatures(adapter: Adapter, features: ?[*]FeatureName) usize { - return wgpuAdapterEnumerateFeatures(adapter, features); - } - extern fn wgpuAdapterEnumerateFeatures(adapter: Adapter, features: ?[*]FeatureName) usize; - - pub inline fn getLimits(adapter: Adapter, limits: *SupportedLimits) bool { - return wgpuAdapterGetLimits(adapter, limits); - } - extern fn wgpuAdapterGetLimits(adapter: Adapter, limits: *SupportedLimits) bool; - - pub inline fn getProperties(adapter: Adapter, properties: *AdapterProperties) void { - wgpuAdapterGetProperties(adapter, properties); - } - extern fn wgpuAdapterGetProperties(adapter: Adapter, properties: *AdapterProperties) void; - - pub inline fn hasFeature(adapter: Adapter, feature: FeatureName) bool { - return wgpuAdapterHasFeature(adapter, feature); - } - extern fn wgpuAdapterHasFeature(adapter: Adapter, feature: FeatureName) bool; - - pub inline fn requestDevice( - adapter: Adapter, - descriptor: DeviceDescriptor, - callback: RequestDeviceCallback, - userdata: ?*anyopaque, - ) void { - wgpuAdapterRequestDevice(adapter, &descriptor, callback, userdata); - } - extern fn wgpuAdapterRequestDevice( - adapter: Adapter, - descriptor: *const DeviceDescriptor, - callback: RequestDeviceCallback, - userdata: ?*anyopaque, - ) void; - - pub inline fn reference(adapter: Adapter) void { - wgpuAdapterReference(adapter); - } - extern fn wgpuAdapterReference(adapter: Adapter) void; - - pub inline fn release(adapter: Adapter) void { - wgpuAdapterRelease(adapter); - } - extern fn wgpuAdapterRelease(adapter: Adapter) void; -}; - -pub const BindGroup = *opaque { - pub inline fn setLabel(bind_group: BindGroup, label: ?[*:0]const u8) void { - wgpuBindGroupSetLabel(bind_group, label); - } - extern fn wgpuBindGroupSetLabel(bind_group: BindGroup, label: ?[*:0]const u8) void; - - pub inline fn reference(bind_group: BindGroup) void { - wgpuBindGroupReference(bind_group); - } - extern fn wgpuBindGroupReference(bind_group: BindGroup) void; - - pub inline fn release(bind_group: BindGroup) void { - wgpuBindGroupRelease(bind_group); - } - extern fn wgpuBindGroupRelease(bind_group: BindGroup) void; -}; - -pub const BindGroupLayout = *opaque { - pub inline fn setLabel(bind_group_layout: BindGroupLayout, label: ?[*:0]const u8) void { - wgpuBindGroupLayoutSetLabel(bind_group_layout, label); - } - extern fn wgpuBindGroupLayoutSetLabel(bind_group_layout: BindGroupLayout, label: ?[*:0]const u8) void; - - pub inline fn reference(bind_group_layout: BindGroupLayout) void { - wgpuBindGroupLayoutReference(bind_group_layout); - } - extern fn wgpuBindGroupLayoutReference(bind_group_layout: BindGroupLayout) void; - - pub inline fn release(bind_group_layout: BindGroupLayout) void { - wgpuBindGroupLayoutRelease(bind_group_layout); - } - extern fn wgpuBindGroupLayoutRelease(bind_group_layout: BindGroupLayout) void; -}; - -pub const Buffer = *opaque { - pub inline fn destroy(buffer: Buffer) void { - wgpuBufferDestroy(buffer); - } - extern fn wgpuBufferDestroy(buffer: Buffer) void; - - // `offset` has to be a multiple of 8 (otherwise `null` will be returned). - // `@sizeOf(T) * len` has to be a multiple of 4 (otherwise `null` will be returned). - pub inline fn getConstMappedRange(buffer: Buffer, comptime T: type, offset: usize, len: usize) ?[]const T { - if (len == 0) return null; - const ptr = wgpuBufferGetConstMappedRange(buffer, offset, @sizeOf(T) * len); - if (ptr == null) return null; - return @ptrCast([*]const T, @alignCast(@alignOf(T), ptr))[0..len]; - } - extern fn wgpuBufferGetConstMappedRange(buffer: Buffer, offset: usize, size: usize) ?*const anyopaque; - - // `offset` has to be a multiple of 8 (otherwise `null` will be returned). - // `@sizeOf(T) * len` has to be a multiple of 4 (otherwise `null` will be returned). - pub inline fn getMappedRange(buffer: Buffer, comptime T: type, offset: usize, len: usize) ?[]T { - if (len == 0) return null; - const ptr = wgpuBufferGetMappedRange(buffer, offset, @sizeOf(T) * len); - if (ptr == null) return null; - return @ptrCast([*]T, @alignCast(@alignOf(T), ptr))[0..len]; - } - extern fn wgpuBufferGetMappedRange(buffer: Buffer, offset: usize, size: usize) ?*anyopaque; - - // `offset` has to be a multiple of 8 (Dawn's validation layer will warn). - // `size` has to be a multiple of 4 (Dawn's validation layer will warn). - // `size == 0` will map entire range (from 'offset' to the end of the buffer). - pub inline fn mapAsync( - buffer: Buffer, - mode: MapMode, - offset: usize, - size: usize, - callback: BufferMapCallback, - userdata: ?*anyopaque, - ) void { - wgpuBufferMapAsync(buffer, mode, offset, size, callback, userdata); - } - extern fn wgpuBufferMapAsync( - buffer: Buffer, - mode: MapMode, - offset: usize, - size: usize, - callback: BufferMapCallback, - userdata: ?*anyopaque, - ) void; - - pub inline fn setLabel(buffer: Buffer, label: ?[*:0]const u8) void { - wgpuBufferSetLabel(buffer, label); - } - extern fn wgpuBufferSetLabel(buffer: Buffer, label: ?[*:0]const u8) void; - - pub inline fn unmap(buffer: Buffer) void { - wgpuBufferUnmap(buffer); - } - extern fn wgpuBufferUnmap(buffer: Buffer) void; - - pub inline fn reference(buffer: Buffer) void { - wgpuBufferReference(buffer); - } - extern fn wgpuBufferReference(buffer: Buffer) void; - - pub inline fn release(buffer: Buffer) void { - wgpuBufferRelease(buffer); - } - extern fn wgpuBufferRelease(buffer: Buffer) void; -}; - -pub const CommandBuffer = *opaque { - pub inline fn setLabel(command_buffer: CommandBuffer, label: ?[*:0]const u8) void { - wgpuCommandBufferSetLabel(command_buffer, label); - } - extern fn wgpuCommandBufferSetLabel(command_buffer: CommandBuffer, label: ?[*:0]const u8) void; - - pub inline fn reference(command_buffer: CommandBuffer) void { - wgpuCommandBufferReference(command_buffer); - } - extern fn wgpuCommandBufferReference(command_buffer: CommandBuffer) void; - - pub inline fn release(command_buffer: CommandBuffer) void { - wgpuCommandBufferRelease(command_buffer); - } - extern fn wgpuCommandBufferRelease(command_buffer: CommandBuffer) void; -}; - -pub const CommandEncoder = *opaque { - pub inline fn beginComputePass( - command_encoder: CommandEncoder, - descriptor: ?ComputePassDescriptor, - ) ComputePassEncoder { - return wgpuCommandEncoderBeginComputePass(command_encoder, if (descriptor) |d| &d else null); - } - extern fn wgpuCommandEncoderBeginComputePass( - command_encoder: CommandEncoder, - descriptor: ?*const ComputePassDescriptor, - ) ComputePassEncoder; - - pub inline fn beginRenderPass( - command_encoder: CommandEncoder, - descriptor: RenderPassDescriptor, - ) RenderPassEncoder { - return wgpuCommandEncoderBeginRenderPass(command_encoder, &descriptor); - } - extern fn wgpuCommandEncoderBeginRenderPass( - command_encoder: CommandEncoder, - descriptor: *const RenderPassDescriptor, - ) RenderPassEncoder; - - pub inline fn clearBuffer(command_encoder: CommandEncoder, buffer: Buffer, offset: usize, size: usize) void { - wgpuCommandEncoderClearBuffer(command_encoder, buffer, offset, size); - } - extern fn wgpuCommandEncoderClearBuffer( - command_encoder: CommandEncoder, - buffer: Buffer, - offset: usize, - size: usize, - ) void; - - pub inline fn copyBufferToBuffer( - command_encoder: CommandEncoder, - source: Buffer, - source_offset: usize, - destination: Buffer, - destination_offset: usize, - size: usize, - ) void { - wgpuCommandEncoderCopyBufferToBuffer( - command_encoder, - source, - source_offset, - destination, - destination_offset, - size, - ); - } - extern fn wgpuCommandEncoderCopyBufferToBuffer( - command_encoder: CommandEncoder, - source: Buffer, - source_offset: usize, - destination: Buffer, - destination_offset: usize, - size: usize, - ) void; - - pub inline fn copyBufferToTexture( - command_encoder: CommandEncoder, - source: ImageCopyBuffer, - destination: ImageCopyTexture, - copy_size: Extent3D, - ) void { - wgpuCommandEncoderCopyBufferToTexture(command_encoder, &source, &destination, ©_size); - } - extern fn wgpuCommandEncoderCopyBufferToTexture( - command_encoder: CommandEncoder, - source: *const ImageCopyBuffer, - destination: *const ImageCopyTexture, - copy_size: *const Extent3D, - ) void; - - pub inline fn copyTextureToBuffer( - command_encoder: CommandEncoder, - source: ImageCopyTexture, - destination: ImageCopyBuffer, - copy_size: Extent3D, - ) void { - wgpuCommandEncoderCopyTextureToBuffer(command_encoder, &source, &destination, ©_size); - } - extern fn wgpuCommandEncoderCopyTextureToBuffer( - command_encoder: CommandEncoder, - source: *const ImageCopyTexture, - destination: *const ImageCopyBuffer, - copy_size: *const Extent3D, - ) void; - - pub inline fn copyTextureToTexture( - command_encoder: CommandEncoder, - source: ImageCopyTexture, - destination: ImageCopyTexture, - copy_size: Extent3D, - ) void { - wgpuCommandEncoderCopyTextureToTexture(command_encoder, &source, &destination, ©_size); - } - extern fn wgpuCommandEncoderCopyTextureToTexture( - command_encoder: CommandEncoder, - source: *const ImageCopyTexture, - destination: *const ImageCopyTexture, - copy_size: *const Extent3D, - ) void; - - pub inline fn finish(command_encoder: CommandEncoder, descriptor: ?CommandBufferDescriptor) CommandBuffer { - return wgpuCommandEncoderFinish(command_encoder, if (descriptor) |d| &d else null); - } - extern fn wgpuCommandEncoderFinish( - command_encoder: CommandEncoder, - descriptor: ?*const CommandBufferDescriptor, - ) CommandBuffer; - - pub inline fn injectValidationError(command_encoder: CommandEncoder, message: [*:0]const u8) void { - wgpuCommandEncoderInjectValidationError(command_encoder, message); - } - extern fn wgpuCommandEncoderInjectValidationError(command_encoder: CommandEncoder, message: [*:0]const u8) void; - - pub inline fn insertDebugMarker(command_encoder: CommandEncoder, marker_label: [*:0]const u8) void { - wgpuCommandEncoderInsertDebugMarker(command_encoder, marker_label); - } - extern fn wgpuCommandEncoderInsertDebugMarker(command_encoder: CommandEncoder, marker_label: [*:0]const u8) void; - - pub inline fn popDebugGroup(command_encoder: CommandEncoder) void { - wgpuCommandEncoderPopDebugGroup(command_encoder); - } - extern fn wgpuCommandEncoderPopDebugGroup(command_encoder: CommandEncoder) void; - - pub inline fn pushDebugGroup(command_encoder: CommandEncoder, group_label: [*:0]const u8) void { - wgpuCommandEncoderPushDebugGroup(command_encoder, group_label); - } - extern fn wgpuCommandEncoderPushDebugGroup(command_encoder: CommandEncoder, group_label: [*:0]const u8) void; - - pub inline fn resolveQuerySet( - command_encoder: CommandEncoder, - query_set: QuerySet, - first_query: u32, - query_count: u32, - destination: Buffer, - destination_offset: u64, - ) void { - wgpuCommandEncoderResolveQuerySet( - command_encoder, - query_set, - first_query, - query_count, - destination, - destination_offset, - ); - } - extern fn wgpuCommandEncoderResolveQuerySet( - command_encoder: CommandEncoder, - query_set: QuerySet, - first_query: u32, - query_count: u32, - destination: Buffer, - destination_offset: u64, - ) void; - - pub inline fn setLabel(command_encoder: CommandEncoder, label: ?[*:0]const u8) void { - wgpuCommandEncoderSetLabel(command_encoder, label); - } - extern fn wgpuCommandEncoderSetLabel(command_encoder: CommandEncoder, label: ?[*:0]const u8) void; - - pub inline fn writeBuffer( - command_encoder: CommandEncoder, - buffer: Buffer, - buffer_offset: u64, - comptime T: type, - data: []const T, - ) void { - wgpuCommandEncoderWriteBuffer( - command_encoder, - buffer, - buffer_offset, - @ptrCast([*]const u8, data.ptr), - @intCast(u64, data.len) * @sizeOf(T), - ); - } - extern fn wgpuCommandEncoderWriteBuffer( - command_encoder: CommandEncoder, - buffer: Buffer, - buffer_offset: u64, - data: [*]const u8, - size: u64, - ) void; - - pub inline fn writeTimestamp(command_encoder: CommandEncoder, query_set: QuerySet, query_index: u32) void { - wgpuCommandEncoderWriteTimestamp(command_encoder, query_set, query_index); - } - extern fn wgpuCommandEncoderWriteTimestamp( - command_encoder: CommandEncoder, - query_set: QuerySet, - query_index: u32, - ) void; - - pub inline fn reference(command_encoder: CommandEncoder) void { - wgpuCommandEncoderReference(command_encoder); - } - extern fn wgpuCommandEncoderReference(command_encoder: CommandEncoder) void; - - pub inline fn release(command_encoder: CommandEncoder) void { - wgpuCommandEncoderRelease(command_encoder); - } - extern fn wgpuCommandEncoderRelease(command_encoder: CommandEncoder) void; -}; - -pub const ComputePassEncoder = *opaque { - pub inline fn dispatchWorkgroups( - compute_pass_encoder: ComputePassEncoder, - workgroup_count_x: u32, - workgroup_count_y: u32, - workgroup_count_z: u32, - ) void { - wgpuComputePassEncoderDispatchWorkgroups( - compute_pass_encoder, - workgroup_count_x, - workgroup_count_y, - workgroup_count_z, - ); - } - extern fn wgpuComputePassEncoderDispatchWorkgroups( - compute_pass_encoder: ComputePassEncoder, - workgroup_count_x: u32, - workgroup_count_y: u32, - workgroup_count_z: u32, - ) void; - - pub inline fn dispatchWorkgroupsIndirect( - compute_pass_encoder: ComputePassEncoder, - indirect_buffer: Buffer, - indirect_offset: u64, - ) void { - wgpuComputePassEncoderDispatchWorkgroupsIndirect(compute_pass_encoder, indirect_buffer, indirect_offset); - } - extern fn wgpuComputePassEncoderDispatchWorkgroupsIndirect( - compute_pass_encoder: ComputePassEncoder, - indirect_buffer: Buffer, - indirect_offset: u64, - ) void; - - pub inline fn end(compute_pass_encoder: ComputePassEncoder) void { - wgpuComputePassEncoderEnd(compute_pass_encoder); - } - extern fn wgpuComputePassEncoderEnd(compute_pass_encoder: ComputePassEncoder) void; - - pub inline fn insertDebugMarker(compute_pass_encoder: ComputePassEncoder, marker_label: [*:0]const u8) void { - wgpuComputePassEncoderInsertDebugMarker(compute_pass_encoder, marker_label); - } - extern fn wgpuComputePassEncoderInsertDebugMarker( - compute_pass_encoder: ComputePassEncoder, - marker_label: [*:0]const u8, - ) void; - - pub inline fn popDebugGroup(compute_pass_encoder: ComputePassEncoder) void { - wgpuComputePassEncoderPopDebugGroup(compute_pass_encoder); - } - extern fn wgpuComputePassEncoderPopDebugGroup(compute_pass_encoder: ComputePassEncoder) void; - - pub inline fn pushDebugGroup(compute_pass_encoder: ComputePassEncoder, group_label: [*:0]const u8) void { - wgpuComputePassEncoderPushDebugGroup(compute_pass_encoder, group_label); - } - extern fn wgpuComputePassEncoderPushDebugGroup( - compute_pass_encoder: ComputePassEncoder, - group_label: [*:0]const u8, - ) void; - - pub inline fn setBindGroup( - compute_pass_encoder: ComputePassEncoder, - group_index: u32, - bind_group: BindGroup, - dynamic_offsets: ?[]const u32, - ) void { - wgpuComputePassEncoderSetBindGroup( - compute_pass_encoder, - group_index, - bind_group, - if (dynamic_offsets) |dynoff| @intCast(u32, dynoff.len) else 0, - if (dynamic_offsets) |dynoff| dynoff.ptr else null, - ); - } - extern fn wgpuComputePassEncoderSetBindGroup( - compute_pass_encoder: ComputePassEncoder, - group_index: u32, - bind_group: BindGroup, - dynamic_offset_count: u32, - dynamic_offsets: ?[*]const u32, - ) void; - - pub inline fn setLabel(compute_pass_encoder: ComputePassEncoder, label: ?[*:0]const u8) void { - wgpuComputePassEncoderSetLabel(compute_pass_encoder, label); - } - extern fn wgpuComputePassEncoderSetLabel(compute_pass_encoder: ComputePassEncoder, label: ?[*:0]const u8) void; - - pub inline fn setPipeline(compute_pass_encoder: ComputePassEncoder, pipeline: ComputePipeline) void { - wgpuComputePassEncoderSetPipeline(compute_pass_encoder, pipeline); - } - extern fn wgpuComputePassEncoderSetPipeline( - compute_pass_encoder: ComputePassEncoder, - pipeline: ComputePipeline, - ) void; - - pub inline fn writeTimestamp( - compute_pass_encoder: ComputePassEncoder, - query_set: QuerySet, - query_index: u32, - ) void { - wgpuComputePassEncoderWriteTimestamp(compute_pass_encoder, query_set, query_index); - } - extern fn wgpuComputePassEncoderWriteTimestamp( - compute_pass_encoder: ComputePassEncoder, - query_set: QuerySet, - query_index: u32, - ) void; - - pub inline fn reference(compute_pass_encoder: ComputePassEncoder) void { - wgpuComputePassEncoderReference(compute_pass_encoder); - } - extern fn wgpuComputePassEncoderReference(compute_pass_encoder: ComputePassEncoder) void; - - pub inline fn release(compute_pass_encoder: ComputePassEncoder) void { - wgpuComputePassEncoderRelease(compute_pass_encoder); - } - extern fn wgpuComputePassEncoderRelease(compute_pass_encoder: ComputePassEncoder) void; -}; - -pub const ComputePipeline = *opaque { - pub inline fn getBindGroupLayout(compute_pipeline: ComputePipeline, group_index: u32) BindGroupLayout { - return wgpuComputePipelineGetBindGroupLayout(compute_pipeline, group_index); - } - extern fn wgpuComputePipelineGetBindGroupLayout( - compute_pipeline: ComputePipeline, - group_index: u32, - ) BindGroupLayout; - - pub inline fn setLabel(compute_pipeline: ComputePipeline, label: ?[*:0]const u8) void { - wgpuComputePipelineSetLabel(compute_pipeline, label); - } - extern fn wgpuComputePipelineSetLabel(compute_pipeline: ComputePipeline, label: ?[*:0]const u8) void; - - pub inline fn reference(compute_pipeline: ComputePipeline) void { - wgpuComputePipelineReference(compute_pipeline); - } - extern fn wgpuComputePipelineReference(compute_pipeline: ComputePipeline) void; - - pub inline fn release(compute_pipeline: ComputePipeline) void { - wgpuComputePipelineRelease(compute_pipeline); - } - extern fn wgpuComputePipelineRelease(compute_pipeline: ComputePipeline) void; -}; - -pub const Device = *opaque { - pub inline fn createBindGroup(device: Device, descriptor: BindGroupDescriptor) BindGroup { - return wgpuDeviceCreateBindGroup(device, &descriptor); - } - extern fn wgpuDeviceCreateBindGroup(device: Device, descriptor: *const BindGroupDescriptor) BindGroup; - - pub inline fn createBindGroupLayout(device: Device, descriptor: BindGroupLayoutDescriptor) BindGroupLayout { - return wgpuDeviceCreateBindGroupLayout(device, &descriptor); - } - extern fn wgpuDeviceCreateBindGroupLayout( - device: Device, - descriptor: *const BindGroupLayoutDescriptor, - ) BindGroupLayout; - - pub inline fn createBuffer(device: Device, descriptor: BufferDescriptor) Buffer { - return wgpuDeviceCreateBuffer(device, &descriptor); - } - extern fn wgpuDeviceCreateBuffer(device: Device, descriptor: *const BufferDescriptor) Buffer; - - pub inline fn createCommandEncoder(device: Device, descriptor: ?CommandEncoderDescriptor) CommandEncoder { - return wgpuDeviceCreateCommandEncoder(device, if (descriptor) |d| &d else null); - } - extern fn wgpuDeviceCreateCommandEncoder( - device: Device, - descriptor: ?*const CommandEncoderDescriptor, - ) CommandEncoder; - - pub inline fn createComputePipeline(device: Device, descriptor: ComputePipelineDescriptor) ComputePipeline { - return wgpuDeviceCreateComputePipeline(device, &descriptor); - } - extern fn wgpuDeviceCreateComputePipeline( - device: Device, - descriptor: *const ComputePipelineDescriptor, - ) ComputePipeline; - - pub inline fn createComputePipelineAsync( - device: Device, - descriptor: ComputePipelineDescriptor, - callback: CreateComputePipelineAsyncCallback, - userdata: ?*anyopaque, - ) void { - wgpuDeviceCreateComputePipelineAsync(device, &descriptor, callback, userdata); - } - extern fn wgpuDeviceCreateComputePipelineAsync( - device: Device, - descriptor: *const ComputePipelineDescriptor, - callback: CreateComputePipelineAsyncCallback, - userdata: ?*anyopaque, - ) void; - - pub inline fn createErrorBuffer(device: Device) Buffer { - return wgpuDeviceCreateErrorBuffer(device); - } - extern fn wgpuDeviceCreateErrorBuffer(device: Device) Buffer; - - pub inline fn createExternalTexture(device: Device, descriptor: ExternalTextureDescriptor) ExternalTexture { - return wgpuDeviceCreateExternalTexture(device, &descriptor); - } - extern fn wgpuDeviceCreateExternalTexture( - device: Device, - descriptor: *const ExternalTextureDescriptor, - ) ExternalTexture; - - pub inline fn createPipelineLayout(device: Device, descriptor: PipelineLayoutDescriptor) PipelineLayout { - return wgpuDeviceCreatePipelineLayout(device, &descriptor); - } - extern fn wgpuDeviceCreatePipelineLayout( - device: Device, - descriptor: *const PipelineLayoutDescriptor, - ) PipelineLayout; - - pub inline fn createQuerySet(device: Device, descriptor: QuerySetDescriptor) QuerySet { - return wgpuDeviceCreateQuerySet(device, &descriptor); - } - extern fn wgpuDeviceCreateQuerySet(device: Device, descriptor: *const QuerySetDescriptor) QuerySet; - - pub inline fn createRenderBundleEncoder( - device: Device, - descriptor: RenderBundleEncoderDescriptor, - ) RenderBundleEncoder { - return wgpuDeviceCreateRenderBundleEncoder(device, &descriptor); - } - extern fn wgpuDeviceCreateRenderBundleEncoder( - device: Device, - descriptor: *const RenderBundleEncoderDescriptor, - ) RenderBundleEncoder; - - pub inline fn createRenderPipeline(device: Device, descriptor: RenderPipelineDescriptor) RenderPipeline { - return wgpuDeviceCreateRenderPipeline(device, &descriptor); - } - extern fn wgpuDeviceCreateRenderPipeline( - device: Device, - descriptor: *const RenderPipelineDescriptor, - ) RenderPipeline; - - pub inline fn createRenderPipelineAsync( - device: Device, - descriptor: RenderPipelineDescriptor, - callback: CreateRenderPipelineAsyncCallback, - userdata: ?*anyopaque, - ) void { - wgpuDeviceCreateRenderPipelineAsync(device, &descriptor, callback, userdata); - } - extern fn wgpuDeviceCreateRenderPipelineAsync( - device: Device, - descriptor: *const RenderPipelineDescriptor, - callback: CreateRenderPipelineAsyncCallback, - userdata: ?*anyopaque, - ) void; - - pub inline fn createSampler(device: Device, descriptor: SamplerDescriptor) Sampler { - return wgpuDeviceCreateSampler(device, &descriptor); - } - extern fn wgpuDeviceCreateSampler(device: Device, descriptor: *const SamplerDescriptor) Sampler; - - pub inline fn createShaderModule(device: Device, descriptor: ShaderModuleDescriptor) ShaderModule { - return wgpuDeviceCreateShaderModule(device, &descriptor); - } - extern fn wgpuDeviceCreateShaderModule(device: Device, descriptor: *const ShaderModuleDescriptor) ShaderModule; - - pub inline fn createSwapChain(device: Device, surface: Surface, descriptor: SwapChainDescriptor) SwapChain { - return wgpuDeviceCreateSwapChain(device, surface, &descriptor); - } - extern fn wgpuDeviceCreateSwapChain( - device: Device, - surface: Surface, - descriptor: *const SwapChainDescriptor, - ) SwapChain; - - pub inline fn createTexture(device: Device, descriptor: TextureDescriptor) Texture { - return wgpuDeviceCreateTexture(device, &descriptor); - } - extern fn wgpuDeviceCreateTexture(device: Device, descriptor: *const TextureDescriptor) Texture; - - pub inline fn destroy(device: Device) void { - wgpuDeviceDestroy(device); - } - extern fn wgpuDeviceDestroy(device: Device) void; - - pub inline fn enumerateFeatures(device: Device, features: ?[*]FeatureName) usize { - return wgpuDeviceEnumerateFeatures(device, features); - } - extern fn wgpuDeviceEnumerateFeatures(device: Device, features: ?[*]FeatureName) usize; - - pub inline fn getLimits(device: Device, limits: *SupportedLimits) bool { - return wgpuDeviceGetLimits(device, limits); - } - extern fn wgpuDeviceGetLimits(device: Device, limits: *SupportedLimits) bool; - - pub inline fn getQueue(device: Device) Queue { - return wgpuDeviceGetQueue(device); - } - extern fn wgpuDeviceGetQueue(device: Device) Queue; - - pub inline fn hasFeature(device: Device, feature: FeatureName) bool { - return wgpuDeviceHasFeature(device, feature); - } - extern fn wgpuDeviceHasFeature(device: Device, feature: FeatureName) bool; - - pub inline fn injectError(device: Device, err_type: ErrorType, message: ?[*:0]const u8) void { - wgpuDeviceInjectError(device, err_type, message); - } - extern fn wgpuDeviceInjectError(device: Device, err_type: ErrorType, message: ?[*:0]const u8) void; - - pub inline fn loseForTesting(device: Device) void { - wgpuDeviceLoseForTesting(device); - } - extern fn wgpuDeviceLoseForTesting(device: Device) void; - - pub inline fn popErrorScope(device: Device, callback: ErrorCallback, userdata: ?*anyopaque) bool { - return wgpuDevicePopErrorScope(device, callback, userdata); - } - extern fn wgpuDevicePopErrorScope(device: Device, callback: ErrorCallback, userdata: ?*anyopaque) bool; - - pub inline fn pushErrorScope(device: Device, filter: ErrorFilter) void { - wgpuDevicePushErrorScope(device, filter); - } - extern fn wgpuDevicePushErrorScope(device: Device, filter: ErrorFilter) void; - - pub inline fn setDeviceLostCallback( - device: Device, - callback: DeviceLostCallback, - userdata: ?*anyopaque, - ) void { - wgpuDeviceSetDeviceLostCallback(device, callback, userdata); - } - extern fn wgpuDeviceSetDeviceLostCallback( - device: Device, - callback: DeviceLostCallback, - userdata: ?*anyopaque, - ) void; - - pub inline fn setLabel(device: Device, label: ?[*:0]const u8) void { - wgpuDeviceSetLabel(device, label); - } - extern fn wgpuDeviceSetLabel(device: Device, label: ?[*:0]const u8) void; - - pub inline fn setLoggingCallback(device: Device, callback: LoggingCallback, userdata: ?*anyopaque) void { - wgpuDeviceSetLoggingCallback(device, callback, userdata); - } - extern fn wgpuDeviceSetLoggingCallback(device: Device, callback: LoggingCallback, userdata: ?*anyopaque) void; - - pub inline fn setUncapturedErrorCallback(device: Device, callback: ErrorCallback, userdata: ?*anyopaque) void { - wgpuDeviceSetUncapturedErrorCallback(device, callback, userdata); - } - extern fn wgpuDeviceSetUncapturedErrorCallback( - device: Device, - callback: ErrorCallback, - userdata: ?*anyopaque, - ) void; - - pub inline fn tick(device: Device) void { - wgpuDeviceTick(device); - } - extern fn wgpuDeviceTick(device: Device) void; - - pub inline fn reference(device: Device) void { - wgpuDeviceReference(device); - } - extern fn wgpuDeviceReference(device: Device) void; - - pub inline fn release(device: Device) void { - wgpuDeviceRelease(device); - } - extern fn wgpuDeviceRelease(device: Device) void; -}; - -pub const ExternalTexture = *opaque { - pub inline fn destroy(external_texture: ExternalTexture) void { - wgpuExternalTextureDestroy(external_texture); - } - extern fn wgpuExternalTextureDestroy(external_texture: ExternalTexture) void; - - pub inline fn setLabel(external_texture: ExternalTexture, label: ?[*:0]const u8) void { - wgpuExternalTextureSetLabel(external_texture, label); - } - extern fn wgpuExternalTextureSetLabel(external_texture: ExternalTexture, label: ?[*:0]const u8) void; - - pub inline fn reference(external_texture: ExternalTexture) void { - wgpuExternalTextureReference(external_texture); - } - extern fn wgpuExternalTextureReference(external_texture: ExternalTexture) void; - - pub inline fn release(external_texture: ExternalTexture) void { - wgpuExternalTextureRelease(external_texture); - } - extern fn wgpuExternalTextureRelease(external_texture: ExternalTexture) void; -}; - -pub const Instance = *opaque { - pub inline fn createSurface(instance: Instance, descriptor: SurfaceDescriptor) Surface { - return wgpuInstanceCreateSurface(instance, &descriptor); - } - extern fn wgpuInstanceCreateSurface(instance: Instance, descriptor: *const SurfaceDescriptor) Surface; - - pub inline fn requestAdapter( - instance: Instance, - options: RequestAdapterOptions, - callback: RequestAdapterCallback, - userdata: ?*anyopaque, - ) void { - wgpuInstanceRequestAdapter(instance, &options, callback, userdata); - } - extern fn wgpuInstanceRequestAdapter( - instance: Instance, - options: *const RequestAdapterOptions, - callback: RequestAdapterCallback, - userdata: ?*anyopaque, - ) void; - - pub inline fn reference(instance: Instance) void { - wgpuInstanceReference(instance); - } - extern fn wgpuInstanceReference(instance: Instance) void; - - pub inline fn release(instance: Instance) void { - wgpuInstanceRelease(instance); - } - extern fn wgpuInstanceRelease(instance: Instance) void; -}; - -pub const PipelineLayout = *opaque { - pub inline fn setLabel(pipeline_layout: PipelineLayout, label: ?[*:0]const u8) void { - wgpuPipelineLayoutSetLabel(pipeline_layout, label); - } - extern fn wgpuPipelineLayoutSetLabel(pipeline_layout: PipelineLayout, label: ?[*:0]const u8) void; - - pub inline fn reference(pipeline_layout: PipelineLayout) void { - wgpuPipelineLayoutReference(pipeline_layout); - } - extern fn wgpuPipelineLayoutReference(pipeline_layout: PipelineLayout) void; - - pub inline fn release(pipeline_layout: PipelineLayout) void { - wgpuPipelineLayoutRelease(pipeline_layout); - } - extern fn wgpuPipelineLayoutRelease(pipeline_layout: PipelineLayout) void; -}; - -pub const QuerySet = *opaque { - pub inline fn destroy(query_set: QuerySet) void { - wgpuQuerySetDestroy(query_set); - } - extern fn wgpuQuerySetDestroy(query_set: QuerySet) void; - - pub inline fn setLabel(query_set: QuerySet, label: ?[*:0]const u8) void { - wgpuQuerySetSetLabel(query_set, label); - } - extern fn wgpuQuerySetSetLabel(query_set: QuerySet, label: ?[*:0]const u8) void; - - pub inline fn reference(query_set: QuerySet) void { - wgpuQuerySetReference(query_set); - } - extern fn wgpuQuerySetReference(query_set: QuerySet) void; - - pub inline fn release(query_set: QuerySet) void { - wgpuQuerySetRelease(query_set); - } - extern fn wgpuQuerySetRelease(query_set: QuerySet) void; -}; - -pub const Queue = *opaque { - pub inline fn copyTextureForBrowser( - queue: Queue, - source: ImageCopyTexture, - destination: ImageCopyTexture, - copy_size: Extent3D, - options: CopyTextureForBrowserOptions, - ) void { - wgpuQueueCopyTextureForBrowser(queue, &source, &destination, ©_size, &options); - } - extern fn wgpuQueueCopyTextureForBrowser( - queue: Queue, - source: *const ImageCopyTexture, - destination: *const ImageCopyTexture, - copy_size: *const Extent3D, - options: *const CopyTextureForBrowserOptions, - ) void; - - pub inline fn onSubmittedWorkDone( - queue: Queue, - signal_value: u64, - callback: QueueWorkDoneCallback, - userdata: ?*anyopaque, - ) void { - wgpuQueueOnSubmittedWorkDone(queue, signal_value, callback, userdata); - } - extern fn wgpuQueueOnSubmittedWorkDone( - queue: Queue, - signal_value: u64, - callback: QueueWorkDoneCallback, - userdata: ?*anyopaque, - ) void; - - pub inline fn setLabel(queue: Queue, label: ?[*:0]const u8) void { - wgpuQueueSetLabel(queue, label); - } - extern fn wgpuQueueSetLabel(queue: Queue, label: ?[*:0]const u8) void; - - pub inline fn submit(queue: Queue, commands: []const CommandBuffer) void { - wgpuQueueSubmit(queue, @intCast(u32, commands.len), commands.ptr); - } - extern fn wgpuQueueSubmit(queue: Queue, command_count: u32, commands: [*]const CommandBuffer) void; - - pub inline fn writeBuffer( - queue: Queue, - buffer: Buffer, - buffer_offset: u64, - comptime T: type, - data: []const T, - ) void { - wgpuQueueWriteBuffer( - queue, - buffer, - buffer_offset, - @ptrCast(*const anyopaque, data.ptr), - @intCast(u64, data.len) * @sizeOf(T), - ); - } - extern fn wgpuQueueWriteBuffer( - queue: Queue, - buffer: Buffer, - buffer_offset: u64, - data: *const anyopaque, - size: u64, - ) void; - - pub inline fn writeTexture( - queue: Queue, - destination: ImageCopyTexture, - data_layout: TextureDataLayout, - write_size: Extent3D, - comptime T: type, - data: []const T, - ) void { - wgpuQueueWriteTexture( - queue, - &destination, - @ptrCast(*const anyopaque, data.ptr), - @intCast(usize, data.len) * @sizeOf(T), - &data_layout, - &write_size, - ); - } - extern fn wgpuQueueWriteTexture( - queue: Queue, - destination: *const ImageCopyTexture, - data: *const anyopaque, - data_size: u64, - data_layout: *const TextureDataLayout, - write_size: *const Extent3D, - ) void; - - pub inline fn reference(queue: Queue) void { - wgpuQueueReference(queue); - } - extern fn wgpuQueueReference(queue: Queue) void; - - pub inline fn release(queue: Queue) void { - wgpuQueueRelease(queue); - } - extern fn wgpuQueueRelease(queue: Queue) void; -}; - -pub const RenderBundle = *opaque { - pub inline fn reference(render_bundle: RenderBundle) void { - wgpuRenderBundleReference(render_bundle); - } - extern fn wgpuRenderBundleReference(render_bundle: RenderBundle) void; - - pub inline fn release(render_bundle: RenderBundle) void { - wgpuRenderBundleRelease(render_bundle); - } - extern fn wgpuRenderBundleRelease(render_bundle: RenderBundle) void; -}; - -pub const RenderBundleEncoder = *opaque { - pub inline fn draw( - render_bundle_encoder: RenderBundleEncoder, - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, - ) void { - wgpuRenderBundleEncoderDraw( - render_bundle_encoder, - vertex_count, - instance_count, - first_vertex, - first_instance, - ); - } - extern fn wgpuRenderBundleEncoderDraw( - render_bundle_encoder: RenderBundleEncoder, - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, - ) void; - - pub inline fn drawIndexed( - render_bundle_encoder: RenderBundleEncoder, - index_count: u32, - instance_count: u32, - first_index: u32, - base_vertex: i32, - first_instance: u32, - ) void { - wgpuRenderBundleEncoderDrawIndexed( - render_bundle_encoder, - index_count, - instance_count, - first_index, - base_vertex, - first_instance, - ); - } - extern fn wgpuRenderBundleEncoderDrawIndexed( - render_bundle_encoder: RenderBundleEncoder, - index_count: u32, - instance_count: u32, - first_index: u32, - base_vertex: i32, - first_instance: u32, - ) void; - - pub inline fn drawIndexedIndirect( - render_bundle_encoder: RenderBundleEncoder, - indirect_buffer: Buffer, - indirect_offset: u64, - ) void { - wgpuRenderBundleEncoderDrawIndexedIndirect(render_bundle_encoder, indirect_buffer, indirect_offset); - } - extern fn wgpuRenderBundleEncoderDrawIndexedIndirect( - render_bundle_encoder: RenderBundleEncoder, - indirect_buffer: Buffer, - indirect_offset: u64, - ) void; - - pub inline fn drawIndirect( - render_bundle_encoder: RenderBundleEncoder, - indirect_buffer: Buffer, - indirect_offset: u64, - ) void { - wgpuRenderBundleEncoderDrawIndirect(render_bundle_encoder, indirect_buffer, indirect_offset); - } - extern fn wgpuRenderBundleEncoderDrawIndirect( - render_bundle_encoder: RenderBundleEncoder, - indirect_buffer: Buffer, - indirect_offset: u64, - ) void; - - pub inline fn finish( - render_bundle_encoder: RenderBundleEncoder, - descriptor: RenderBundleDescriptor, - ) RenderBundle { - return wgpuRenderBundleEncoderFinish(render_bundle_encoder, &descriptor); - } - extern fn wgpuRenderBundleEncoderFinish( - render_bundle_encoder: RenderBundleEncoder, - descriptor: *const RenderBundleDescriptor, - ) RenderBundle; - - pub inline fn insertDebugMarker( - render_bundle_encoder: RenderBundleEncoder, - marker_label: [*:0]const u8, - ) void { - wgpuRenderBundleEncoderInsertDebugMarker(render_bundle_encoder, marker_label); - } - extern fn wgpuRenderBundleEncoderInsertDebugMarker( - render_bundle_encoder: RenderBundleEncoder, - marker_label: [*:0]const u8, - ) void; - - pub inline fn popDebugGroup(render_bundle_encoder: RenderBundleEncoder) void { - wgpuRenderBundleEncoderPopDebugGroup(render_bundle_encoder); - } - extern fn wgpuRenderBundleEncoderPopDebugGroup(render_bundle_encoder: RenderBundleEncoder) void; - - pub inline fn pushDebugGroup(render_bundle_encoder: RenderBundleEncoder, group_label: [*:0]const u8) void { - wgpuRenderBundleEncoderPushDebugGroup(render_bundle_encoder, group_label); - } - extern fn wgpuRenderBundleEncoderPushDebugGroup( - render_bundle_encoder: RenderBundleEncoder, - group_label: [*:0]const u8, - ) void; - - pub inline fn setBindGroup( - render_bundle_encoder: RenderBundleEncoder, - group_index: u32, - group: BindGroup, - dynamic_offsets: ?[]const u32, - ) void { - wgpuRenderBundleEncoderSetBindGroup( - render_bundle_encoder, - group_index, - group, - if (dynamic_offsets) |dynoff| @intCast(u32, dynoff.len) else 0, - if (dynamic_offsets) |dynoff| dynoff.ptr else null, - ); - } - extern fn wgpuRenderBundleEncoderSetBindGroup( - render_bundle_encoder: RenderBundleEncoder, - group_index: u32, - group: BindGroup, - dynamic_offset_count: u32, - dynamic_offsets: ?[*]const u32, - ) void; - - pub inline fn setIndexBuffer( - render_bundle_encoder: RenderBundleEncoder, - buffer: Buffer, - format: IndexFormat, - offset: u64, - size: u64, - ) void { - wgpuRenderBundleEncoderSetIndexBuffer(render_bundle_encoder, buffer, format, offset, size); - } - extern fn wgpuRenderBundleEncoderSetIndexBuffer( - render_bundle_encoder: RenderBundleEncoder, - buffer: Buffer, - format: IndexFormat, - offset: u64, - size: u64, - ) void; - - pub inline fn setLabel(render_bundle_encoder: RenderBundleEncoder, label: ?[*:0]const u8) void { - wgpuRenderBundleEncoderSetLabel(render_bundle_encoder, label); - } - extern fn wgpuRenderBundleEncoderSetLabel( - render_bundle_encoder: RenderBundleEncoder, - label: ?[*:0]const u8, - ) void; - - pub inline fn setPipeline(render_bundle_encoder: RenderBundleEncoder, pipeline: RenderPipeline) void { - wgpuRenderBundleEncoderSetPipeline(render_bundle_encoder, pipeline); - } - extern fn wgpuRenderBundleEncoderSetPipeline( - render_bundle_encoder: RenderBundleEncoder, - pipeline: RenderPipeline, - ) void; - - pub inline fn setVertexBuffer( - render_bundle_encoder: RenderBundleEncoder, - slot: u32, - buffer: Buffer, - offset: u64, - size: u64, - ) void { - wgpuRenderBundleEncoderSetVertexBuffer(render_bundle_encoder, slot, buffer, offset, size); - } - extern fn wgpuRenderBundleEncoderSetVertexBuffer( - render_bundle_encoder: RenderBundleEncoder, - slot: u32, - buffer: Buffer, - offset: u64, - size: u64, - ) void; - - pub inline fn reference(render_bundle_encoder: RenderBundleEncoder) void { - wgpuRenderBundleEncoderReference(render_bundle_encoder); - } - extern fn wgpuRenderBundleEncoderReference(render_bundle_encoder: RenderBundleEncoder) void; - - pub inline fn release(render_bundle_encoder: RenderBundleEncoder) void { - wgpuRenderBundleEncoderRelease(render_bundle_encoder); - } - extern fn wgpuRenderBundleEncoderRelease(render_bundle_encoder: RenderBundleEncoder) void; -}; - -pub const RenderPassEncoder = *opaque { - pub inline fn beginOcclusionQuery(render_pass_encoder: RenderPassEncoder, query_index: u32) void { - wgpuRenderPassEncoderBeginOcclusionQuery(render_pass_encoder, query_index); - } - extern fn wgpuRenderPassEncoderBeginOcclusionQuery( - render_pass_encoder: RenderPassEncoder, - query_index: u32, - ) void; - - pub inline fn draw( - render_pass_encoder: RenderPassEncoder, - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, - ) void { - wgpuRenderPassEncoderDraw(render_pass_encoder, vertex_count, instance_count, first_vertex, first_instance); - } - extern fn wgpuRenderPassEncoderDraw( - render_pass_encoder: RenderPassEncoder, - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, - ) void; - - pub inline fn drawIndexed( - render_pass_encoder: RenderPassEncoder, - index_count: u32, - instance_count: u32, - first_index: u32, - base_vertex: i32, - first_instance: u32, - ) void { - wgpuRenderPassEncoderDrawIndexed( - render_pass_encoder, - index_count, - instance_count, - first_index, - base_vertex, - first_instance, - ); - } - extern fn wgpuRenderPassEncoderDrawIndexed( - render_pass_encoder: RenderPassEncoder, - index_count: u32, - instance_count: u32, - first_index: u32, - base_vertex: i32, - first_instance: u32, - ) void; - - pub inline fn drawIndexedIndirect( - render_pass_encoder: RenderPassEncoder, - indirect_buffer: Buffer, - indirect_offset: u64, - ) void { - wgpuRenderPassEncoderDrawIndexedIndirect(render_pass_encoder, indirect_buffer, indirect_offset); - } - extern fn wgpuRenderPassEncoderDrawIndexedIndirect( - render_pass_encoder: RenderPassEncoder, - indirect_buffer: Buffer, - indirect_offset: u64, - ) void; - - pub inline fn drawIndirect( - render_pass_encoder: RenderPassEncoder, - indirect_buffer: Buffer, - indirect_offset: u64, - ) void { - wgpuRenderPassEncoderDrawIndirect(render_pass_encoder, indirect_buffer, indirect_offset); - } - extern fn wgpuRenderPassEncoderDrawIndirect( - render_pass_encoder: RenderPassEncoder, - indirect_buffer: Buffer, - indirect_offset: u64, - ) void; - - pub inline fn end(render_pass_encoder: RenderPassEncoder) void { - wgpuRenderPassEncoderEnd(render_pass_encoder); - } - extern fn wgpuRenderPassEncoderEnd(render_pass_encoder: RenderPassEncoder) void; - - pub inline fn endOcclusionQuery(render_pass_encoder: RenderPassEncoder) void { - wgpuRenderPassEncoderEndOcclusionQuery(render_pass_encoder); - } - extern fn wgpuRenderPassEncoderEndOcclusionQuery(render_pass_encoder: RenderPassEncoder) void; - - pub inline fn executeBundles( - render_pass_encoder: RenderPassEncoder, - bundles_count: u32, - bundles: [*]const RenderBundle, - ) void { - wgpuRenderPassEncoderExecuteBundles(render_pass_encoder, bundles_count, bundles); - } - extern fn wgpuRenderPassEncoderExecuteBundles( - render_pass_encoder: RenderPassEncoder, - bundles_count: u32, - bundles: [*]const RenderBundle, - ) void; - - pub inline fn insertDebugMarker(render_pass_encoder: RenderPassEncoder, marker_label: [*:0]const u8) void { - wgpuRenderPassEncoderInsertDebugMarker(render_pass_encoder, marker_label); - } - extern fn wgpuRenderPassEncoderInsertDebugMarker( - render_pass_encoder: RenderPassEncoder, - marker_label: [*:0]const u8, - ) void; - - pub inline fn popDebugGroup(render_pass_encoder: RenderPassEncoder) void { - wgpuRenderPassEncoderPopDebugGroup(render_pass_encoder); - } - extern fn wgpuRenderPassEncoderPopDebugGroup(render_pass_encoder: RenderPassEncoder) void; - - pub inline fn pushDebugGroup(render_pass_encoder: RenderPassEncoder, group_label: [*:0]const u8) void { - wgpuRenderPassEncoderPushDebugGroup(render_pass_encoder, group_label); - } - extern fn wgpuRenderPassEncoderPushDebugGroup( - render_pass_encoder: RenderPassEncoder, - group_label: [*:0]const u8, - ) void; - - pub inline fn setBindGroup( - render_pass_encoder: RenderPassEncoder, - group_index: u32, - group: BindGroup, - dynamic_offsets: ?[]const u32, - ) void { - wgpuRenderPassEncoderSetBindGroup( - render_pass_encoder, - group_index, - group, - if (dynamic_offsets) |dynoff| @intCast(u32, dynoff.len) else 0, - if (dynamic_offsets) |dynoff| dynoff.ptr else null, - ); - } - extern fn wgpuRenderPassEncoderSetBindGroup( - render_pass_encoder: RenderPassEncoder, - group_index: u32, - group: BindGroup, - dynamic_offset_count: u32, - dynamic_offsets: ?[*]const u32, - ) void; - - pub inline fn setBlendConstant(render_pass_encoder: RenderPassEncoder, color: Color) void { - wgpuRenderPassEncoderSetBlendConstant(render_pass_encoder, &color); - } - extern fn wgpuRenderPassEncoderSetBlendConstant( - render_pass_encoder: RenderPassEncoder, - color: *const Color, - ) void; - - pub inline fn setIndexBuffer( - render_pass_encoder: RenderPassEncoder, - buffer: Buffer, - format: IndexFormat, - offset: u64, - size: u64, - ) void { - wgpuRenderPassEncoderSetIndexBuffer(render_pass_encoder, buffer, format, offset, size); - } - extern fn wgpuRenderPassEncoderSetIndexBuffer( - render_pass_encoder: RenderPassEncoder, - buffer: Buffer, - format: IndexFormat, - offset: u64, - size: u64, - ) void; - - pub inline fn setLabel(render_pass_encoder: RenderPassEncoder, label: ?[*:0]const u8) void { - wgpuRenderPassEncoderSetLabel(render_pass_encoder, label); - } - extern fn wgpuRenderPassEncoderSetLabel(render_pass_encoder: RenderPassEncoder, label: ?[*:0]const u8) void; - - pub inline fn setPipeline(render_pass_encoder: RenderPassEncoder, pipeline: RenderPipeline) void { - wgpuRenderPassEncoderSetPipeline(render_pass_encoder, pipeline); - } - extern fn wgpuRenderPassEncoderSetPipeline( - render_pass_encoder: RenderPassEncoder, - pipeline: RenderPipeline, - ) void; - - pub inline fn setScissorRect( - render_pass_encoder: RenderPassEncoder, - x: u32, - y: u32, - width: u32, - height: u32, - ) void { - wgpuRenderPassEncoderSetScissorRect(render_pass_encoder, x, y, width, height); - } - extern fn wgpuRenderPassEncoderSetScissorRect( - render_pass_encoder: RenderPassEncoder, - x: u32, - y: u32, - width: u32, - height: u32, - ) void; - - pub inline fn setStencilReference(render_pass_encoder: RenderPassEncoder, ref: u32) void { - wgpuRenderPassEncoderSetStencilReference(render_pass_encoder, ref); - } - extern fn wgpuRenderPassEncoderSetStencilReference(render_pass_encoder: RenderPassEncoder, ref: u32) void; - - pub inline fn setVertexBuffer( - render_pass_encoder: RenderPassEncoder, - slot: u32, - buffer: Buffer, - offset: u64, - size: u64, - ) void { - wgpuRenderPassEncoderSetVertexBuffer(render_pass_encoder, slot, buffer, offset, size); - } - extern fn wgpuRenderPassEncoderSetVertexBuffer( - render_pass_encoder: RenderPassEncoder, - slot: u32, - buffer: Buffer, - offset: u64, - size: u64, - ) void; - - pub inline fn setViewport( - render_pass_encoder: RenderPassEncoder, - x: f32, - y: f32, - width: f32, - height: f32, - min_depth: f32, - max_depth: f32, - ) void { - wgpuRenderPassEncoderSetViewport(render_pass_encoder, x, y, width, height, min_depth, max_depth); - } - extern fn wgpuRenderPassEncoderSetViewport( - render_pass_encoder: RenderPassEncoder, - x: f32, - y: f32, - width: f32, - height: f32, - min_depth: f32, - max_depth: f32, - ) void; - - pub inline fn writeTimestamp( - render_pass_encoder: RenderPassEncoder, - query_set: QuerySet, - query_index: u32, - ) void { - wgpuRenderPassEncoderWriteTimestamp(render_pass_encoder, query_set, query_index); - } - extern fn wgpuRenderPassEncoderWriteTimestamp( - render_pass_encoder: RenderPassEncoder, - query_set: QuerySet, - query_index: u32, - ) void; - - pub inline fn reference(render_pass_encoder: RenderPassEncoder) void { - wgpuRenderPassEncoderReference(render_pass_encoder); - } - extern fn wgpuRenderPassEncoderReference(render_pass_encoder: RenderPassEncoder) void; - - pub inline fn release(render_pass_encoder: RenderPassEncoder) void { - wgpuRenderPassEncoderRelease(render_pass_encoder); - } - extern fn wgpuRenderPassEncoderRelease(render_pass_encoder: RenderPassEncoder) void; -}; - -pub const RenderPipeline = *opaque { - pub inline fn getBindGroupLayout(render_pipeline: RenderPipeline, group_index: u32) BindGroupLayout { - return wgpuRenderPipelineGetBindGroupLayout(render_pipeline, group_index); - } - extern fn wgpuRenderPipelineGetBindGroupLayout( - render_pipeline: RenderPipeline, - group_index: u32, - ) BindGroupLayout; - - pub inline fn setLabel(render_pipeline: RenderPipeline, label: ?[*:0]const u8) void { - wgpuRenderPipelineSetLabel(render_pipeline, label); - } - extern fn wgpuRenderPipelineSetLabel(render_pipeline: RenderPipeline, label: ?[*:0]const u8) void; - - pub inline fn reference(render_pipeline: RenderPipeline) void { - wgpuRenderPipelineReference(render_pipeline); - } - extern fn wgpuRenderPipelineReference(render_pipeline: RenderPipeline) void; - - pub inline fn release(render_pipeline: RenderPipeline) void { - wgpuRenderPipelineRelease(render_pipeline); - } - extern fn wgpuRenderPipelineRelease(render_pipeline: RenderPipeline) void; -}; - -pub const Sampler = *opaque { - pub inline fn setLabel(sampler: Sampler, label: ?[*:0]const u8) void { - wgpuSamplerSetLabel(sampler, label); - } - extern fn wgpuSamplerSetLabel(sampler: Sampler, label: ?[*:0]const u8) void; - - pub inline fn reference(sampler: Sampler) void { - wgpuSamplerReference(sampler); - } - extern fn wgpuSamplerReference(sampler: Sampler) void; - - pub inline fn release(sampler: Sampler) void { - wgpuSamplerRelease(sampler); - } - extern fn wgpuSamplerRelease(sampler: Sampler) void; -}; - -pub const ShaderModule = *opaque { - pub inline fn getCompilationInfo( - shader_module: ShaderModule, - callback: CompilationInfoCallback, - userdata: ?*anyopaque, - ) void { - wgpuShaderModuleGetCompilationInfo(shader_module, callback, userdata); - } - extern fn wgpuShaderModuleGetCompilationInfo( - shader_module: ShaderModule, - callback: CompilationInfoCallback, - userdata: ?*anyopaque, - ) void; - - pub inline fn setLabel(shader_module: ShaderModule, label: ?[*:0]const u8) void { - wgpuShaderModuleSetLabel(shader_module, label); - } - extern fn wgpuShaderModuleSetLabel(shader_module: ShaderModule, label: ?[*:0]const u8) void; - - pub inline fn reference(shader_module: ShaderModule) void { - wgpuShaderModuleReference(shader_module); - } - extern fn wgpuShaderModuleReference(shader_module: ShaderModule) void; - - pub inline fn release(shader_module: ShaderModule) void { - wgpuShaderModuleRelease(shader_module); - } - extern fn wgpuShaderModuleRelease(shader_module: ShaderModule) void; -}; - -pub const Surface = *opaque { - pub inline fn reference(surface: Surface) void { - wgpuSurfaceReference(surface); - } - extern fn wgpuSurfaceReference(surface: Surface) void; - - pub inline fn release(surface: Surface) void { - wgpuSurfaceRelease(surface); - } - extern fn wgpuSurfaceRelease(surface: Surface) void; -}; - -pub const SwapChain = *opaque { - pub inline fn configure( - swap_chain: SwapChain, - format: TextureFormat, - allowed_usage: TextureUsage, - width: u32, - height: u32, - ) void { - wgpuSwapChainConfigure(swap_chain, format, allowed_usage, width, height); - } - extern fn wgpuSwapChainConfigure( - swap_chain: SwapChain, - format: TextureFormat, - allowed_usage: TextureUsage, - width: u32, - height: u32, - ) void; - - pub inline fn getCurrentTextureView(swap_chain: SwapChain) TextureView { - return wgpuSwapChainGetCurrentTextureView(swap_chain); - } - extern fn wgpuSwapChainGetCurrentTextureView(swap_chain: SwapChain) TextureView; - - pub inline fn present(swap_chain: SwapChain) void { - wgpuSwapChainPresent(swap_chain); - } - extern fn wgpuSwapChainPresent(swap_chain: SwapChain) void; - - pub inline fn reference(swap_chain: SwapChain) void { - wgpuSwapChainReference(swap_chain); - } - extern fn wgpuSwapChainReference(swap_chain: SwapChain) void; - - pub inline fn release(swap_chain: SwapChain) void { - wgpuSwapChainRelease(swap_chain); - } - extern fn wgpuSwapChainRelease(swap_chain: SwapChain) void; -}; - -pub const Texture = *opaque { - pub inline fn createView(texture: Texture, descriptor: TextureViewDescriptor) TextureView { - return wgpuTextureCreateView(texture, &descriptor); - } - extern fn wgpuTextureCreateView(texture: Texture, descriptor: *const TextureViewDescriptor) TextureView; - - pub inline fn destroy(texture: Texture) void { - wgpuTextureDestroy(texture); - } - extern fn wgpuTextureDestroy(texture: Texture) void; - - pub inline fn setLabel(texture: Texture, label: ?[*:0]const u8) void { - wgpuTextureSetLabel(texture, label); - } - extern fn wgpuTextureSetLabel(texture: Texture, label: ?[*:0]const u8) void; - - pub inline fn reference(texture: Texture) void { - wgpuTextureReference(texture); - } - extern fn wgpuTextureReference(texture: Texture) void; - - pub inline fn release(texture: Texture) void { - wgpuTextureRelease(texture); - } - extern fn wgpuTextureRelease(texture: Texture) void; -}; - -pub const TextureView = *opaque { - pub inline fn setLabel(texture_view: TextureView, label: ?[*:0]const u8) void { - wgpuTextureViewSetLabel(texture_view, label); - } - extern fn wgpuTextureViewSetLabel(texture_view: TextureView, label: ?[*:0]const u8) void; - - pub inline fn reference(texture_view: TextureView) void { - wgpuTextureViewReference(texture_view); - } - extern fn wgpuTextureViewReference(texture_view: TextureView) void; - - pub inline fn release(texture_view: TextureView) void { - wgpuTextureViewRelease(texture_view); - } - extern fn wgpuTextureViewRelease(texture_view: TextureView) void; -}; diff --git a/libs/zgpu/src/zgpu.zig b/libs/zgpu/src/zgpu.zig deleted file mode 100644 index 75546c97a..000000000 --- a/libs/zgpu/src/zgpu.zig +++ /dev/null @@ -1,1744 +0,0 @@ -//-------------------------------------------------------------------------------------------------- -// zgpu v0.2 -// -// `zgpu` is a cross-platform (Windows/Linux/macOS) graphics layer built on top of wgpu API (Dawn). -//-------------------------------------------------------------------------------------------------- -const std = @import("std"); -const math = std.math; -const assert = std.debug.assert; -const glfw = @import("zglfw"); -const wgsl = @import("common_wgsl.zig"); -pub const wgpu = @import("wgpu.zig"); - -pub const GraphicsContext = struct { - pub const swapchain_format = wgpu.TextureFormat.bgra8_unorm; - - window: glfw.Window, - stats: FrameStats = .{}, - - instance: wgpu.Instance, - device: wgpu.Device, - queue: wgpu.Queue, - surface: wgpu.Surface, - swapchain: wgpu.SwapChain, - swapchain_descriptor: wgpu.SwapChainDescriptor, - - buffer_pool: BufferPool, - texture_pool: TexturePool, - texture_view_pool: TextureViewPool, - sampler_pool: SamplerPool, - render_pipeline_pool: RenderPipelinePool, - compute_pipeline_pool: ComputePipelinePool, - bind_group_pool: BindGroupPool, - bind_group_layout_pool: BindGroupLayoutPool, - pipeline_layout_pool: PipelineLayoutPool, - - mipgens: std.AutoHashMap(wgpu.TextureFormat, MipgenResources), - - uniforms: struct { - offset: u32 = 0, - buffer: BufferHandle = .{}, - stage: struct { - num: u32 = 0, - current: u32 = 0, - buffers: [uniforms_staging_pipeline_len]UniformsStagingBuffer = - [_]UniformsStagingBuffer{.{}} ** uniforms_staging_pipeline_len, - } = .{}, - } = .{}, - - // TODO: Adjust pool sizes. - const buffer_pool_size = 256; - const texture_pool_size = 256; - const texture_view_pool_size = 256; - const sampler_pool_size = 16; - const render_pipeline_pool_size = 128; - const compute_pipeline_pool_size = 128; - const bind_group_pool_size = 32; - const bind_group_layout_pool_size = 32; - const pipeline_layout_pool_size = 32; - - pub fn init(allocator: std.mem.Allocator, window: glfw.Window) !*GraphicsContext { - const instance = createWgpuInstance(); - errdefer instance.release(); - - const adapter = adapter: { - const Response = struct { - status: wgpu.RequestAdapterStatus = .unknown, - adapter: wgpu.Adapter = undefined, - }; - - const callback = (struct { - fn callback( - status: wgpu.RequestAdapterStatus, - adapter: wgpu.Adapter, - message: ?[*:0]const u8, - userdata: ?*anyopaque, - ) callconv(.C) void { - _ = message; - const response = @ptrCast(*Response, @alignCast(@sizeOf(usize), userdata)); - response.status = status; - response.adapter = adapter; - } - }).callback; - - var response = Response{}; - instance.requestAdapter( - .{ .power_preference = .high_performance }, - callback, - @ptrCast(*anyopaque, &response), - ); - - if (response.status != .success) { - std.debug.print("Failed to request GPU adapter (status: {any}).\n", .{response.status}); - return error.NoGraphicsAdapter; - } - break :adapter response.adapter; - }; - errdefer adapter.release(); - - var properties: wgpu.AdapterProperties = undefined; - adapter.getProperties(&properties); - std.debug.print("[zgpu] High-performance device has been selected:\n", .{}); - std.debug.print("[zgpu] Name: {s}\n", .{properties.name}); - std.debug.print("[zgpu] Driver: {s}\n", .{properties.driver_description}); - std.debug.print("[zgpu] Adapter type: {s}\n", .{@tagName(properties.adapter_type)}); - std.debug.print("[zgpu] Backend type: {s}\n", .{@tagName(properties.backend_type)}); - - const device = device: { - const Response = struct { - status: wgpu.RequestDeviceStatus = .unknown, - device: wgpu.Device = undefined, - }; - - const callback = (struct { - fn callback( - status: wgpu.RequestDeviceStatus, - device: wgpu.Device, - message: ?[*:0]const u8, - userdata: ?*anyopaque, - ) callconv(.C) void { - _ = message; - const response = @ptrCast(*Response, @alignCast(@sizeOf(usize), userdata)); - response.status = status; - response.device = device; - } - }).callback; - - var response = Response{}; - adapter.requestDevice( - wgpu.DeviceDescriptor{}, - callback, - @ptrCast(*anyopaque, &response), - ); - - if (response.status != .success) { - std.debug.print("Failed to request GPU device (status: {any}).\n", .{response.status}); - return error.NoGraphicsDevice; - } - break :device response.device; - }; - errdefer device.release(); - - device.setUncapturedErrorCallback(printUnhandledError, null); - - const surface = createSurfaceForWindow(instance, window); - errdefer surface.release(); - - const framebuffer_size = window.getFramebufferSize(); - - const swapchain_descriptor = wgpu.SwapChainDescriptor{ - .label = "main window swap chain", - .usage = .{ .render_attachment = true }, - .format = swapchain_format, - .width = @intCast(u32, framebuffer_size[0]), - .height = @intCast(u32, framebuffer_size[1]), - .present_mode = .fifo, - .implementation = 0, - }; - const swapchain = device.createSwapChain(surface, swapchain_descriptor); - errdefer swapchain.release(); - - const gctx = try allocator.create(GraphicsContext); - gctx.* = .{ - .instance = instance, - .device = device, - .queue = device.getQueue(), - .window = window, - .surface = surface, - .swapchain = swapchain, - .swapchain_descriptor = swapchain_descriptor, - .buffer_pool = BufferPool.init(allocator, buffer_pool_size), - .texture_pool = TexturePool.init(allocator, texture_pool_size), - .texture_view_pool = TextureViewPool.init(allocator, texture_view_pool_size), - .sampler_pool = SamplerPool.init(allocator, sampler_pool_size), - .render_pipeline_pool = RenderPipelinePool.init(allocator, render_pipeline_pool_size), - .compute_pipeline_pool = ComputePipelinePool.init(allocator, compute_pipeline_pool_size), - .bind_group_pool = BindGroupPool.init(allocator, bind_group_pool_size), - .bind_group_layout_pool = BindGroupLayoutPool.init(allocator, bind_group_layout_pool_size), - .pipeline_layout_pool = PipelineLayoutPool.init(allocator, pipeline_layout_pool_size), - .mipgens = std.AutoHashMap(wgpu.TextureFormat, MipgenResources).init(allocator), - }; - - uniformsInit(gctx); - return gctx; - } - - pub fn deinit(gctx: *GraphicsContext, allocator: std.mem.Allocator) void { - // TODO: How to release `native_instance`? - - // Wait for the GPU to finish all encoded commands. - while (gctx.stats.cpu_frame_number != gctx.stats.gpu_frame_number) { - gctx.device.tick(); - } - - // Wait for all outstanding mapAsync() calls to complete. - wait_loop: while (true) { - gctx.device.tick(); - var i: u32 = 0; - while (i < gctx.uniforms.stage.num) : (i += 1) { - if (gctx.uniforms.stage.buffers[i].slice == null) { - continue :wait_loop; - } - } - break; - } - - gctx.mipgens.deinit(); - gctx.pipeline_layout_pool.deinit(allocator); - gctx.bind_group_pool.deinit(allocator); - gctx.bind_group_layout_pool.deinit(allocator); - gctx.buffer_pool.deinit(allocator); - gctx.texture_view_pool.deinit(allocator); - gctx.texture_pool.deinit(allocator); - gctx.sampler_pool.deinit(allocator); - gctx.render_pipeline_pool.deinit(allocator); - gctx.compute_pipeline_pool.deinit(allocator); - gctx.surface.release(); - gctx.swapchain.release(); - gctx.queue.release(); - gctx.device.release(); - allocator.destroy(gctx); - } - - // - // Uniform buffer pool - // - pub fn uniformsAllocate( - gctx: *GraphicsContext, - comptime T: type, - num_elements: u32, - ) struct { slice: []T, offset: u32 } { - assert(num_elements > 0); - const size = num_elements * @sizeOf(T); - - const offset = gctx.uniforms.offset; - const aligned_size = (size + (uniforms_alloc_alignment - 1)) & ~(uniforms_alloc_alignment - 1); - if ((offset + aligned_size) >= uniforms_buffer_size) { - // TODO: Better error handling; pool is full; flush it? - return .{ .slice = @as([*]T, undefined)[0..0], .offset = 0 }; - } - - const current = gctx.uniforms.stage.current; - const slice = (gctx.uniforms.stage.buffers[current].slice.?.ptr + offset)[0..size]; - - gctx.uniforms.offset += aligned_size; - return .{ - .slice = std.mem.bytesAsSlice(T, @alignCast(@alignOf(T), slice)), - .offset = offset, - }; - } - - const UniformsStagingBuffer = struct { - slice: ?[]u8 = null, - buffer: wgpu.Buffer = undefined, - }; - const uniforms_buffer_size = 4 * 1024 * 1024; - const uniforms_staging_pipeline_len = 8; - const uniforms_alloc_alignment: u32 = 256; - - fn uniformsInit(gctx: *GraphicsContext) void { - gctx.uniforms.buffer = gctx.createBuffer(.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = uniforms_buffer_size, - }); - gctx.uniformsNextStagingBuffer(); - } - - fn uniformsMappedCallback(status: wgpu.BufferMapAsyncStatus, userdata: ?*anyopaque) callconv(.C) void { - const usb = @ptrCast(*UniformsStagingBuffer, @alignCast(@sizeOf(usize), userdata)); - assert(usb.slice == null); - if (status == .success) { - usb.slice = usb.buffer.getMappedRange(u8, 0, uniforms_buffer_size).?; - } else { - std.debug.print("[zgpu] Failed to map buffer (code: {d})\n", .{@enumToInt(status)}); - } - } - - fn uniformsNextStagingBuffer(gctx: *GraphicsContext) void { - if (gctx.stats.cpu_frame_number > 0) { - // Map staging buffer which was used this frame. - const current = gctx.uniforms.stage.current; - assert(gctx.uniforms.stage.buffers[current].slice == null); - gctx.uniforms.stage.buffers[current].buffer.mapAsync( - .{ .write = true }, - 0, - uniforms_buffer_size, - uniformsMappedCallback, - @ptrCast(*anyopaque, &gctx.uniforms.stage.buffers[current]), - ); - } - - gctx.uniforms.offset = 0; - - var i: u32 = 0; - while (i < gctx.uniforms.stage.num) : (i += 1) { - if (gctx.uniforms.stage.buffers[i].slice != null) { - gctx.uniforms.stage.current = i; - return; - } - } - - if (gctx.uniforms.stage.num >= uniforms_staging_pipeline_len) { - // Wait until one of the buffers is mapped and ready to use. - while (true) { - gctx.device.tick(); - - i = 0; - while (i < gctx.uniforms.stage.num) : (i += 1) { - if (gctx.uniforms.stage.buffers[i].slice != null) { - gctx.uniforms.stage.current = i; - return; - } - } - } - } - - assert(gctx.uniforms.stage.num < uniforms_staging_pipeline_len); - const current = gctx.uniforms.stage.num; - gctx.uniforms.stage.current = current; - gctx.uniforms.stage.num += 1; - - // Create new staging buffer. - const buffer_handle = gctx.createBuffer(.{ - .usage = .{ .copy_src = true, .map_write = true }, - .size = uniforms_buffer_size, - .mapped_at_creation = true, - }); - - // Add new (mapped) staging buffer to the buffer list. - gctx.uniforms.stage.buffers[current] = .{ - .slice = gctx.lookupResource(buffer_handle).?.getMappedRange(u8, 0, uniforms_buffer_size).?, - .buffer = gctx.lookupResource(buffer_handle).?, - }; - } - - // - // Submit/Present - // - pub fn submit(gctx: *GraphicsContext, commands: []const wgpu.CommandBuffer) void { - const stage_commands = stage_commands: { - const stage_encoder = gctx.device.createCommandEncoder(null); - defer stage_encoder.release(); - - const current = gctx.uniforms.stage.current; - assert(gctx.uniforms.stage.buffers[current].slice != null); - - gctx.uniforms.stage.buffers[current].slice = null; - gctx.uniforms.stage.buffers[current].buffer.unmap(); - - if (gctx.uniforms.offset > 0) { - stage_encoder.copyBufferToBuffer( - gctx.uniforms.stage.buffers[current].buffer, - 0, - gctx.lookupResource(gctx.uniforms.buffer).?, - 0, - gctx.uniforms.offset, - ); - } - - break :stage_commands stage_encoder.finish(null); - }; - defer stage_commands.release(); - - gctx.queue.onSubmittedWorkDone(0, gpuWorkDone, @ptrCast(*anyopaque, &gctx.stats.gpu_frame_number)); - - // TODO: We support up to 32 command buffers for now. Make it more robust. - var command_buffers = std.BoundedArray(wgpu.CommandBuffer, 32).init(0) catch unreachable; - command_buffers.append(stage_commands) catch unreachable; - command_buffers.appendSlice(commands) catch unreachable; - gctx.queue.submit(command_buffers.slice()); - - gctx.stats.tick(); - gctx.uniformsNextStagingBuffer(); - } - - fn gpuWorkDone(status: wgpu.QueueWorkDoneStatus, userdata: ?*anyopaque) callconv(.C) void { - const gpu_frame_number = @ptrCast(*u64, @alignCast(@sizeOf(usize), userdata)); - gpu_frame_number.* += 1; - if (status != .success) { - std.debug.print("[zgpu] Failed to complete GPU work (code: {d})\n", .{@enumToInt(status)}); - } - } - - pub fn present(gctx: *GraphicsContext) enum { - normal_execution, - swap_chain_resized, - } { - gctx.swapchain.present(); - - const fb_size = gctx.window.getFramebufferSize(); - if (gctx.swapchain_descriptor.width != fb_size[0] or - gctx.swapchain_descriptor.height != fb_size[1]) - { - gctx.swapchain_descriptor.width = @intCast(u32, fb_size[0]); - gctx.swapchain_descriptor.height = @intCast(u32, fb_size[1]); - gctx.swapchain.release(); - - gctx.swapchain = gctx.device.createSwapChain(gctx.surface, gctx.swapchain_descriptor); - - std.debug.print( - "[zgpu] Window has been resized to: {d}x{d}\n", - .{ gctx.swapchain_descriptor.width, gctx.swapchain_descriptor.height }, - ); - return .swap_chain_resized; - } - - return .normal_execution; - } - - // - // Resources - // - pub fn createBuffer(gctx: *GraphicsContext, descriptor: wgpu.BufferDescriptor) BufferHandle { - return gctx.buffer_pool.addResource(gctx.*, .{ - .gpuobj = gctx.device.createBuffer(descriptor), - .size = descriptor.size, - .usage = descriptor.usage, - }); - } - - pub fn createTexture(gctx: *GraphicsContext, descriptor: wgpu.TextureDescriptor) TextureHandle { - return gctx.texture_pool.addResource(gctx.*, .{ - .gpuobj = gctx.device.createTexture(descriptor), - .usage = descriptor.usage, - .dimension = descriptor.dimension, - .size = descriptor.size, - .format = descriptor.format, - .mip_level_count = descriptor.mip_level_count, - .sample_count = descriptor.sample_count, - }); - } - - pub fn createTextureView( - gctx: *GraphicsContext, - texture_handle: TextureHandle, - descriptor: wgpu.TextureViewDescriptor, - ) TextureViewHandle { - const texture = gctx.lookupResource(texture_handle).?; - const info = gctx.lookupResourceInfo(texture_handle).?; - var dim = descriptor.dimension; - if (dim == .undef) { - dim = switch (info.dimension) { - .tdim_1d => .tvdim_1d, - .tdim_2d => .tvdim_2d, - .tdim_3d => .tvdim_3d, - }; - } - return gctx.texture_view_pool.addResource(gctx.*, .{ - .gpuobj = texture.createView(descriptor), - .format = if (descriptor.format == .undef) info.format else descriptor.format, - .dimension = dim, - .base_mip_level = descriptor.base_mip_level, - .mip_level_count = if (descriptor.mip_level_count == 0xffff_ffff) - info.mip_level_count - else - descriptor.mip_level_count, - .base_array_layer = descriptor.base_array_layer, - .array_layer_count = if (descriptor.array_layer_count == 0xffff_ffff) - info.size.depth_or_array_layers - else - descriptor.array_layer_count, - .aspect = descriptor.aspect, - .parent_texture_handle = texture_handle, - }); - } - - pub fn createSampler(gctx: *GraphicsContext, descriptor: wgpu.SamplerDescriptor) SamplerHandle { - return gctx.sampler_pool.addResource(gctx.*, .{ - .gpuobj = gctx.device.createSampler(descriptor), - .address_mode_u = descriptor.address_mode_u, - .address_mode_v = descriptor.address_mode_v, - .address_mode_w = descriptor.address_mode_w, - .mag_filter = descriptor.mag_filter, - .min_filter = descriptor.min_filter, - .mipmap_filter = descriptor.mipmap_filter, - .lod_min_clamp = descriptor.lod_min_clamp, - .lod_max_clamp = descriptor.lod_max_clamp, - .compare = descriptor.compare, - .max_anisotropy = descriptor.max_anisotropy, - }); - } - - pub fn createRenderPipeline( - gctx: *GraphicsContext, - pipeline_layout: PipelineLayoutHandle, - descriptor: wgpu.RenderPipelineDescriptor, - ) RenderPipelineHandle { - var desc = descriptor; - desc.layout = gctx.lookupResource(pipeline_layout) orelse null; - return gctx.render_pipeline_pool.addResource(gctx.*, .{ - .gpuobj = gctx.device.createRenderPipeline(desc), - .pipeline_layout_handle = pipeline_layout, - }); - } - - const AsyncCreateOpRender = struct { - gctx: *GraphicsContext, - result: *RenderPipelineHandle, - pipeline_layout: PipelineLayoutHandle, - allocator: std.mem.Allocator, - - fn create( - status: wgpu.CreatePipelineAsyncStatus, - pipeline: wgpu.RenderPipeline, - message: ?[*:0]const u8, - userdata: ?*anyopaque, - ) callconv(.C) void { - const op = @ptrCast(*AsyncCreateOpRender, @alignCast(@sizeOf(usize), userdata)); - if (status == .success) { - op.result.* = op.gctx.render_pipeline_pool.addResource( - op.gctx.*, - .{ .gpuobj = pipeline, .pipeline_layout_handle = op.pipeline_layout }, - ); - } else { - std.debug.print( - "[zgpu] Failed to async create render pipeline (code: {s})\n{s}\n", - .{ @tagName(status), if (message) |msg| msg else "[zgpu] No error details from the driver" }, - ); - } - op.allocator.destroy(op); - } - }; - - pub fn createRenderPipelineAsync( - gctx: *GraphicsContext, - allocator: std.mem.Allocator, - pipeline_layout: PipelineLayoutHandle, - descriptor: wgpu.RenderPipelineDescriptor, - result: *RenderPipelineHandle, - ) void { - var desc = descriptor; - desc.layout = gctx.lookupResource(pipeline_layout) orelse null; - - const op = allocator.create(AsyncCreateOpRender) catch unreachable; - op.* = .{ - .gctx = gctx, - .result = result, - .pipeline_layout = pipeline_layout, - .allocator = allocator, - }; - gctx.device.createRenderPipelineAsync(desc, AsyncCreateOpRender.create, @ptrCast(*anyopaque, op)); - } - - pub fn createComputePipeline( - gctx: *GraphicsContext, - pipeline_layout: PipelineLayoutHandle, - descriptor: wgpu.ComputePipelineDescriptor, - ) ComputePipelineHandle { - var desc = descriptor; - desc.layout = gctx.lookupResource(pipeline_layout) orelse null; - return gctx.compute_pipeline_pool.addResource(gctx.*, .{ - .gpuobj = gctx.device.createComputePipeline(desc), - .pipeline_layout_handle = pipeline_layout, - }); - } - - const AsyncCreateOpCompute = struct { - gctx: *GraphicsContext, - result: *ComputePipelineHandle, - pipeline_layout: PipelineLayoutHandle, - allocator: std.mem.Allocator, - - fn create( - status: wgpu.CreatePipelineAsyncStatus, - pipeline: wgpu.ComputePipeline, - message: ?[*:0]const u8, - userdata: ?*anyopaque, - ) callconv(.C) void { - const op = @ptrCast(*AsyncCreateOpCompute, @alignCast(@sizeOf(usize), userdata)); - if (status == .success) { - op.result.* = op.gctx.compute_pipeline_pool.addResource( - op.gctx.*, - .{ .gpuobj = pipeline, .pipeline_layout_handle = op.pipeline_layout }, - ); - } else { - std.debug.print( - "[zgpu] Failed to async create compute pipeline (code: {s})\n{s}\n", - .{ @tagName(status), if (message) |msg| msg else "[zgpu] No error details from the driver" }, - ); - } - op.allocator.destroy(op); - } - }; - - pub fn createComputePipelineAsync( - gctx: *GraphicsContext, - allocator: std.mem.Allocator, - pipeline_layout: PipelineLayoutHandle, - descriptor: wgpu.ComputePipelineDescriptor, - result: *ComputePipelineHandle, - ) void { - var desc = descriptor; - desc.layout = gctx.lookupResource(pipeline_layout) orelse null; - - const op = allocator.create(AsyncCreateOpCompute) catch unreachable; - op.* = .{ - .gctx = gctx, - .result = result, - .pipeline_layout = pipeline_layout, - .allocator = allocator, - }; - gctx.device.createComputePipelineAsync(desc, AsyncCreateOpCompute.create, @ptrCast(*anyopaque, op)); - } - - pub fn createBindGroup( - gctx: *GraphicsContext, - layout: BindGroupLayoutHandle, - entries: []const BindGroupEntryInfo, - ) BindGroupHandle { - assert(entries.len > 0 and entries.len <= max_num_bindings_per_group); - - var bind_group_info = BindGroupInfo{ .num_entries = @intCast(u32, entries.len) }; - var gpu_bind_group_entries: [max_num_bindings_per_group]wgpu.BindGroupEntry = undefined; - - for (entries) |entry, i| { - bind_group_info.entries[i] = entry; - - if (entries[i].buffer_handle) |handle| { - gpu_bind_group_entries[i] = .{ - .binding = entries[i].binding, - .buffer = gctx.lookupResource(handle).?, - .offset = entries[i].offset, - .size = entries[i].size, - .sampler = null, - .texture_view = null, - }; - } else if (entries[i].sampler_handle) |handle| { - gpu_bind_group_entries[i] = .{ - .binding = entries[i].binding, - .buffer = null, - .offset = 0, - .size = 0, - .sampler = gctx.lookupResource(handle).?, - .texture_view = null, - }; - } else if (entries[i].texture_view_handle) |handle| { - gpu_bind_group_entries[i] = .{ - .binding = entries[i].binding, - .buffer = null, - .offset = 0, - .size = 0, - .sampler = null, - .texture_view = gctx.lookupResource(handle).?, - }; - } else unreachable; - } - bind_group_info.gpuobj = gctx.device.createBindGroup(.{ - .layout = gctx.lookupResource(layout).?, - .entry_count = @intCast(u32, entries.len), - .entries = &gpu_bind_group_entries, - }); - return gctx.bind_group_pool.addResource(gctx.*, bind_group_info); - } - - pub fn createBindGroupLayout( - gctx: *GraphicsContext, - entries: []const wgpu.BindGroupLayoutEntry, - ) BindGroupLayoutHandle { - assert(entries.len > 0 and entries.len <= max_num_bindings_per_group); - - var bind_group_layout_info = BindGroupLayoutInfo{ - .gpuobj = gctx.device.createBindGroupLayout(.{ - .entry_count = @intCast(u32, entries.len), - .entries = entries.ptr, - }), - .num_entries = @intCast(u32, entries.len), - }; - for (entries) |entry, i| { - bind_group_layout_info.entries[i] = entry; - bind_group_layout_info.entries[i].next_in_chain = null; - bind_group_layout_info.entries[i].buffer.next_in_chain = null; - bind_group_layout_info.entries[i].sampler.next_in_chain = null; - bind_group_layout_info.entries[i].texture.next_in_chain = null; - bind_group_layout_info.entries[i].storage_texture.next_in_chain = null; - } - return gctx.bind_group_layout_pool.addResource(gctx.*, bind_group_layout_info); - } - - pub fn createBindGroupLayoutAuto( - gctx: *GraphicsContext, - pipeline: anytype, - group_index: u32, - ) BindGroupLayoutHandle { - const bgl = gctx.lookupResource(pipeline).?.getBindGroupLayout(group_index); - return gctx.bind_group_layout_pool.addResource(gctx.*, BindGroupLayoutInfo{ .gpuobj = bgl }); - } - - pub fn createPipelineLayout( - gctx: *GraphicsContext, - bind_group_layouts: []const BindGroupLayoutHandle, - ) PipelineLayoutHandle { - assert(bind_group_layouts.len > 0); - - var info: PipelineLayoutInfo = .{ .num_bind_group_layouts = @intCast(u32, bind_group_layouts.len) }; - var gpu_bind_group_layouts: [max_num_bind_groups_per_pipeline]wgpu.BindGroupLayout = undefined; - - for (bind_group_layouts) |bgl, i| { - info.bind_group_layouts[i] = bgl; - gpu_bind_group_layouts[i] = gctx.lookupResource(bgl).?; - } - - info.gpuobj = gctx.device.createPipelineLayout(.{ - .bind_group_layout_count = info.num_bind_group_layouts, - .bind_group_layouts = &gpu_bind_group_layouts, - }); - - return gctx.pipeline_layout_pool.addResource(gctx.*, info); - } - - pub fn lookupResource(gctx: GraphicsContext, handle: anytype) ?handleToGpuResourceType(@TypeOf(handle)) { - if (gctx.isResourceValid(handle)) { - const T = @TypeOf(handle); - return switch (T) { - BufferHandle => gctx.buffer_pool.getGpuObj(handle).?, - TextureHandle => gctx.texture_pool.getGpuObj(handle).?, - TextureViewHandle => gctx.texture_view_pool.getGpuObj(handle).?, - SamplerHandle => gctx.sampler_pool.getGpuObj(handle).?, - RenderPipelineHandle => gctx.render_pipeline_pool.getGpuObj(handle).?, - ComputePipelineHandle => gctx.compute_pipeline_pool.getGpuObj(handle).?, - BindGroupHandle => gctx.bind_group_pool.getGpuObj(handle).?, - BindGroupLayoutHandle => gctx.bind_group_layout_pool.getGpuObj(handle).?, - PipelineLayoutHandle => gctx.pipeline_layout_pool.getGpuObj(handle).?, - else => @compileError( - "[zgpu] GraphicsContext.lookupResource() not implemented for " ++ @typeName(T), - ), - }; - } - return null; - } - - pub fn lookupResourceInfo(gctx: GraphicsContext, handle: anytype) ?handleToResourceInfoType(@TypeOf(handle)) { - if (gctx.isResourceValid(handle)) { - const T = @TypeOf(handle); - return switch (T) { - BufferHandle => gctx.buffer_pool.getInfo(handle), - TextureHandle => gctx.texture_pool.getInfo(handle), - TextureViewHandle => gctx.texture_view_pool.getInfo(handle), - SamplerHandle => gctx.sampler_pool.getInfo(handle), - RenderPipelineHandle => gctx.render_pipeline_pool.getInfo(handle), - ComputePipelineHandle => gctx.compute_pipeline_pool.getInfo(handle), - BindGroupHandle => gctx.bind_group_pool.getInfo(handle), - BindGroupLayoutHandle => gctx.bind_group_layout_pool.getInfo(handle), - PipelineLayoutHandle => gctx.pipeline_layout_pool.getInfo(handle), - else => @compileError( - "[zgpu] GraphicsContext.lookupResourceInfo() not implemented for " ++ @typeName(T), - ), - }; - } - return null; - } - - pub fn releaseResource(gctx: *GraphicsContext, handle: anytype) void { - const T = @TypeOf(handle); - switch (T) { - BufferHandle => gctx.buffer_pool.destroyResource(handle, false), - TextureHandle => gctx.texture_pool.destroyResource(handle, false), - TextureViewHandle => gctx.texture_view_pool.destroyResource(handle, false), - SamplerHandle => gctx.sampler_pool.destroyResource(handle, false), - RenderPipelineHandle => gctx.render_pipeline_pool.destroyResource(handle, false), - ComputePipelineHandle => gctx.compute_pipeline_pool.destroyResource(handle, false), - BindGroupHandle => gctx.bind_group_pool.destroyResource(handle, false), - BindGroupLayoutHandle => gctx.bind_group_layout_pool.destroyResource(handle, false), - PipelineLayoutHandle => gctx.pipeline_layout_pool.destroyResource(handle, false), - else => @compileError("[zgpu] GraphicsContext.releaseResource() not implemented for " ++ @typeName(T)), - } - } - - pub fn destroyResource(gctx: *GraphicsContext, handle: anytype) void { - const T = @TypeOf(handle); - switch (T) { - BufferHandle => gctx.buffer_pool.destroyResource(handle, true), - TextureHandle => gctx.texture_pool.destroyResource(handle, true), - else => @compileError("[zgpu] GraphicsContext.destroyResource() not implemented for " ++ @typeName(T)), - } - } - - pub fn isResourceValid(gctx: GraphicsContext, handle: anytype) bool { - const T = @TypeOf(handle); - switch (T) { - BufferHandle => return gctx.buffer_pool.isHandleValid(handle), - TextureHandle => return gctx.texture_pool.isHandleValid(handle), - TextureViewHandle => { - if (gctx.texture_view_pool.isHandleValid(handle)) { - const texture = gctx.texture_view_pool.getInfoPtr(handle).parent_texture_handle; - return gctx.isResourceValid(texture); - } - return false; - }, - SamplerHandle => return gctx.sampler_pool.isHandleValid(handle), - RenderPipelineHandle => return gctx.render_pipeline_pool.isHandleValid(handle), - ComputePipelineHandle => return gctx.compute_pipeline_pool.isHandleValid(handle), - BindGroupHandle => { - if (gctx.bind_group_pool.isHandleValid(handle)) { - const num_entries = gctx.bind_group_pool.getInfoPtr(handle).num_entries; - const entries = &gctx.bind_group_pool.getInfoPtr(handle).entries; - var i: u32 = 0; - while (i < num_entries) : (i += 1) { - if (entries[i].buffer_handle) |buffer| { - if (!gctx.isResourceValid(buffer)) - return false; - } else if (entries[i].sampler_handle) |sampler| { - if (!gctx.isResourceValid(sampler)) - return false; - } else if (entries[i].texture_view_handle) |texture_view| { - if (!gctx.isResourceValid(texture_view)) - return false; - } else unreachable; - } - return true; - } - return false; - }, - BindGroupLayoutHandle => return gctx.bind_group_layout_pool.isHandleValid(handle), - PipelineLayoutHandle => return gctx.pipeline_layout_pool.isHandleValid(handle), - else => @compileError("[zgpu] GraphicsContext.isResourceValid() not implemented for " ++ @typeName(T)), - } - } - - // - // Mipmaps - // - const MipgenResources = struct { - pipeline: ComputePipelineHandle = .{}, - scratch_texture: TextureHandle = .{}, - scratch_texture_views: [max_levels_per_dispatch]TextureViewHandle = - [_]TextureViewHandle{.{}} ** max_levels_per_dispatch, - bind_group_layout: BindGroupLayoutHandle = .{}, - - const max_levels_per_dispatch = 4; - }; - - pub fn generateMipmaps( - gctx: *GraphicsContext, - arena: std.mem.Allocator, - encoder: wgpu.CommandEncoder, - texture: TextureHandle, - ) void { - const texture_info = gctx.lookupResourceInfo(texture) orelse return; - if (texture_info.mip_level_count == 1) return; - - const max_size = 2048; - - assert(texture_info.usage.copy_dst == true); - assert(texture_info.dimension == .tdim_2d); - assert(texture_info.size.width <= max_size and texture_info.size.height <= max_size); - assert(texture_info.size.width == texture_info.size.height); - assert(math.isPowerOfTwo(texture_info.size.width)); - - const format = texture_info.format; - const entry = gctx.mipgens.getOrPut(format) catch unreachable; - const mipgen = entry.value_ptr; - - if (!entry.found_existing) { - mipgen.bind_group_layout = gctx.createBindGroupLayout(&.{ - bglBuffer(0, .{ .compute = true }, .uniform, true, 0), - bglTexture(1, .{ .compute = true }, .unfilterable_float, .tvdim_2d, false), - bglStorageTexture(2, .{ .compute = true }, .write_only, format, .tvdim_2d), - bglStorageTexture(3, .{ .compute = true }, .write_only, format, .tvdim_2d), - bglStorageTexture(4, .{ .compute = true }, .write_only, format, .tvdim_2d), - bglStorageTexture(5, .{ .compute = true }, .write_only, format, .tvdim_2d), - }); - - const pipeline_layout = gctx.createPipelineLayout(&.{ - mipgen.bind_group_layout, - }); - defer gctx.releaseResource(pipeline_layout); - - const wgsl_src = wgsl.csGenerateMipmaps(arena, formatToShaderFormat(format)); - const cs_module = util.createWgslShaderModule(gctx.device, wgsl_src, "zgpu_cs_generate_mipmaps"); - defer { - arena.free(wgsl_src); - cs_module.release(); - } - - mipgen.pipeline = gctx.createComputePipeline(pipeline_layout, .{ - .compute = .{ - .module = cs_module, - .entry_point = "main", - }, - }); - - mipgen.scratch_texture = gctx.createTexture(.{ - .usage = .{ .copy_src = true, .storage_binding = true }, - .dimension = .tdim_2d, - .size = .{ .width = max_size / 2, .height = max_size / 2, .depth_or_array_layers = 1 }, - .format = format, - .mip_level_count = MipgenResources.max_levels_per_dispatch, - .sample_count = 1, - }); - - for (mipgen.scratch_texture_views) |*view, i| { - view.* = gctx.createTextureView(mipgen.scratch_texture, .{ - .base_mip_level = @intCast(u32, i), - .mip_level_count = 1, - .base_array_layer = 0, - .array_layer_count = 1, - }); - } - } - - var array_layer: u32 = 0; - while (array_layer < texture_info.size.depth_or_array_layers) : (array_layer += 1) { - const texture_view = gctx.createTextureView(texture, .{ - .dimension = .tvdim_2d, - .base_array_layer = array_layer, - .array_layer_count = 1, - }); - defer gctx.releaseResource(texture_view); - - const bind_group = gctx.createBindGroup(mipgen.bind_group_layout, &[_]BindGroupEntryInfo{ - .{ .binding = 0, .buffer_handle = gctx.uniforms.buffer, .offset = 0, .size = 8 }, - .{ .binding = 1, .texture_view_handle = texture_view }, - .{ .binding = 2, .texture_view_handle = mipgen.scratch_texture_views[0] }, - .{ .binding = 3, .texture_view_handle = mipgen.scratch_texture_views[1] }, - .{ .binding = 4, .texture_view_handle = mipgen.scratch_texture_views[2] }, - .{ .binding = 5, .texture_view_handle = mipgen.scratch_texture_views[3] }, - }); - defer gctx.releaseResource(bind_group); - - const MipgenUniforms = extern struct { - src_mip_level: i32, - num_mip_levels: u32, - }; - - var total_num_mips: u32 = texture_info.mip_level_count - 1; - var current_src_mip_level: u32 = 0; - - while (true) { - const dispatch_num_mips = math.min(MipgenResources.max_levels_per_dispatch, total_num_mips); - { - const pass = encoder.beginComputePass(null); - defer { - pass.end(); - pass.release(); - } - - pass.setPipeline(gctx.lookupResource(mipgen.pipeline).?); - - const mem = gctx.uniformsAllocate(MipgenUniforms, 1); - mem.slice[0] = .{ - .src_mip_level = @intCast(i32, current_src_mip_level), - .num_mip_levels = dispatch_num_mips, - }; - pass.setBindGroup(0, gctx.lookupResource(bind_group).?, &.{mem.offset}); - - pass.dispatchWorkgroups( - math.max(texture_info.size.width >> @intCast(u5, 3 + current_src_mip_level), 1), - math.max(texture_info.size.height >> @intCast(u5, 3 + current_src_mip_level), 1), - 1, - ); - } - - var mip_index: u32 = 0; - while (mip_index < dispatch_num_mips) : (mip_index += 1) { - const src_origin = wgpu.Origin3D{ .x = 0, .y = 0, .z = 0 }; - const dst_origin = wgpu.Origin3D{ .x = 0, .y = 0, .z = array_layer }; - encoder.copyTextureToTexture( - .{ - .texture = gctx.lookupResource(mipgen.scratch_texture).?, - .mip_level = mip_index, - .origin = src_origin, - }, - .{ - .texture = gctx.lookupResource(texture).?, - .mip_level = mip_index + current_src_mip_level + 1, - .origin = dst_origin, - }, - .{ - .width = texture_info.size.width >> @intCast(u5, mip_index + current_src_mip_level + 1), - .height = texture_info.size.height >> @intCast(u5, mip_index + current_src_mip_level + 1), - }, - ); - } - - assert(total_num_mips >= dispatch_num_mips); - total_num_mips -= dispatch_num_mips; - if (total_num_mips == 0) { - break; - } - current_src_mip_level += dispatch_num_mips; - } - } - } -}; - -// Defined in dawn.cpp -pub const DawnNativeInstance = ?*opaque {}; -pub const DawnProcsTable = ?*opaque {}; -pub extern fn dawnNativeCreateInstance() DawnNativeInstance; -pub extern fn dawnNativeDestroyInstance(native_instance: DawnNativeInstance) void; -pub extern fn dawnNativeGetWgpuInstance(native_instance: DawnNativeInstance) ?wgpu.Instance; -pub extern fn dawnNativeDiscoverDefaultAdapters(native_instance: DawnNativeInstance) void; -pub extern fn dawnNativeGetProcs() DawnProcsTable; - -// Defined in Dawn codebase -pub extern fn dawnProcSetProcs(procs: DawnProcsTable) void; - -pub fn createWgpuInstance() wgpu.Instance { - dawnProcSetProcs(dawnNativeGetProcs()); - const native_instance = dawnNativeCreateInstance(); - dawnNativeDiscoverDefaultAdapters(native_instance); - return dawnNativeGetWgpuInstance(native_instance).?; -} - -pub const bglBuffer = wgpu.BindGroupLayoutEntry.buffer; -pub const bglTexture = wgpu.BindGroupLayoutEntry.texture; -pub const bglSampler = wgpu.BindGroupLayoutEntry.sampler; -pub const bglStorageTexture = wgpu.BindGroupLayoutEntry.storageTexture; - -pub const util = struct { - /// You may disable async shader compilation for debugging purposes. - const enable_async_shader_compilation = true; - - /// Helper function for creating render pipelines. - /// Supports: one vertex buffer, one non-blending render target, - /// one vertex shader module and one fragment shader module. - pub fn createRenderPipelineSimple( - allocator: std.mem.Allocator, - gctx: *GraphicsContext, - bgls: []const BindGroupLayoutHandle, - wgsl_vs: [:0]const u8, - wgsl_fs: [:0]const u8, - vertex_stride: ?u64, - vertex_attribs: ?[]const wgpu.VertexAttribute, - primitive_state: wgpu.PrimitiveState, - rt_format: wgpu.TextureFormat, - depth_state: ?wgpu.DepthStencilState, - out_pipe: *RenderPipelineHandle, - ) void { - const pl = gctx.createPipelineLayout(bgls); - defer gctx.releaseResource(pl); - - const vs_mod = createWgslShaderModule(gctx.device, wgsl_vs, null); - defer vs_mod.release(); - - const fs_mod = createWgslShaderModule(gctx.device, wgsl_fs, null); - defer fs_mod.release(); - - const color_targets = [_]wgpu.ColorTargetState{.{ .format = rt_format }}; - - const vertex_buffers = if (vertex_stride) |vs| [_]wgpu.VertexBufferLayout{.{ - .array_stride = vs, - .attribute_count = @intCast(u32, vertex_attribs.?.len), - .attributes = vertex_attribs.?.ptr, - }} else null; - - const pipe_desc = wgpu.RenderPipelineDescriptor{ - .vertex = wgpu.VertexState{ - .module = vs_mod, - .entry_point = "main", - .buffer_count = if (vertex_buffers) |vbs| vbs.len else 0, - .buffers = if (vertex_buffers) |vbs| &vbs else null, - }, - .fragment = &wgpu.FragmentState{ - .module = fs_mod, - .entry_point = "main", - .target_count = color_targets.len, - .targets = &color_targets, - }, - .depth_stencil = if (depth_state) |ds| &ds else null, - .primitive = primitive_state, - }; - - if (enable_async_shader_compilation) { - gctx.createRenderPipelineAsync(allocator, pl, pipe_desc, out_pipe); - } else { - out_pipe.* = gctx.createRenderPipeline(pl, pipe_desc); - } - } - - /// Helper function for creating render passes. - /// Supports: One color attachment and optional depth attachment. - pub fn beginRenderPassSimple( - encoder: wgpu.CommandEncoder, - load_op: wgpu.LoadOp, - color_texv: wgpu.TextureView, - clear_color: ?wgpu.Color, - depth_texv: ?wgpu.TextureView, - clear_depth: ?f32, - ) wgpu.RenderPassEncoder { - if (depth_texv == null) { - assert(clear_depth == null); - } - const color_attachments = [_]wgpu.RenderPassColorAttachment{.{ - .view = color_texv, - .load_op = load_op, - .store_op = .store, - .clear_value = if (clear_color) |cc| cc else .{ .r = 0, .g = 0, .b = 0, .a = 0 }, - }}; - if (depth_texv) |dtexv| { - const depth_attachment = wgpu.RenderPassDepthStencilAttachment{ - .view = dtexv, - .depth_load_op = load_op, - .depth_store_op = .store, - .depth_clear_value = if (clear_depth) |cd| cd else 0.0, - }; - return encoder.beginRenderPass(.{ - .color_attachment_count = color_attachments.len, - .color_attachments = &color_attachments, - .depth_stencil_attachment = &depth_attachment, - }); - } - return encoder.beginRenderPass(.{ - .color_attachment_count = color_attachments.len, - .color_attachments = &color_attachments, - }); - } - - pub fn endRelease(pass: anytype) void { - pass.end(); - pass.release(); - } - - pub fn createWgslShaderModule( - device: wgpu.Device, - source: [*:0]const u8, - label: ?[*:0]const u8, - ) wgpu.ShaderModule { - const wgsl_desc = wgpu.ShaderModuleWgslDescriptor{ - .chain = .{ - .next = null, - .struct_type = .shader_module_wgsl_descriptor, - }, - .source = source, - }; - const desc = wgpu.ShaderModuleDescriptor{ - .next_in_chain = @ptrCast(*const wgpu.ChainedStruct, &wgsl_desc), - .label = if (label) |l| l else null, - }; - return device.createShaderModule(desc); - } -}; - -pub const BufferInfo = struct { - gpuobj: ?wgpu.Buffer = null, - size: usize = 0, - usage: wgpu.BufferUsage = .{}, -}; - -pub const TextureInfo = struct { - gpuobj: ?wgpu.Texture = null, - usage: wgpu.TextureUsage = .{}, - dimension: wgpu.TextureDimension = .tdim_1d, - size: wgpu.Extent3D = .{ .width = 0 }, - format: wgpu.TextureFormat = .undef, - mip_level_count: u32 = 0, - sample_count: u32 = 0, -}; - -pub const TextureViewInfo = struct { - gpuobj: ?wgpu.TextureView = null, - format: wgpu.TextureFormat = .undef, - dimension: wgpu.TextureViewDimension = .undef, - base_mip_level: u32 = 0, - mip_level_count: u32 = 0, - base_array_layer: u32 = 0, - array_layer_count: u32 = 0, - aspect: wgpu.TextureAspect = .all, - parent_texture_handle: TextureHandle = .{}, -}; - -pub const SamplerInfo = struct { - gpuobj: ?wgpu.Sampler = null, - address_mode_u: wgpu.AddressMode = .repeat, - address_mode_v: wgpu.AddressMode = .repeat, - address_mode_w: wgpu.AddressMode = .repeat, - mag_filter: wgpu.FilterMode = .nearest, - min_filter: wgpu.FilterMode = .nearest, - mipmap_filter: wgpu.FilterMode = .nearest, - lod_min_clamp: f32 = 0.0, - lod_max_clamp: f32 = 0.0, - compare: wgpu.CompareFunction = .undef, - max_anisotropy: u16 = 0, -}; - -pub const RenderPipelineInfo = struct { - gpuobj: ?wgpu.RenderPipeline = null, - pipeline_layout_handle: PipelineLayoutHandle = .{}, -}; - -pub const ComputePipelineInfo = struct { - gpuobj: ?wgpu.ComputePipeline = null, - pipeline_layout_handle: PipelineLayoutHandle = .{}, -}; - -pub const BindGroupEntryInfo = struct { - binding: u32 = 0, - buffer_handle: ?BufferHandle = null, - offset: u64 = 0, - size: u64 = 0, - sampler_handle: ?SamplerHandle = null, - texture_view_handle: ?TextureViewHandle = null, -}; - -const max_num_bindings_per_group = 10; - -pub const BindGroupInfo = struct { - gpuobj: ?wgpu.BindGroup = null, - num_entries: u32 = 0, - entries: [max_num_bindings_per_group]BindGroupEntryInfo = - [_]BindGroupEntryInfo{.{}} ** max_num_bindings_per_group, -}; - -pub const BindGroupLayoutInfo = struct { - gpuobj: ?wgpu.BindGroupLayout = null, - num_entries: u32 = 0, - entries: [max_num_bindings_per_group]wgpu.BindGroupLayoutEntry = - [_]wgpu.BindGroupLayoutEntry{.{ .binding = 0, .visibility = .{} }} ** max_num_bindings_per_group, -}; - -const max_num_bind_groups_per_pipeline = 4; - -pub const PipelineLayoutInfo = struct { - gpuobj: ?wgpu.PipelineLayout = null, - num_bind_group_layouts: u32 = 0, - bind_group_layouts: [max_num_bind_groups_per_pipeline]BindGroupLayoutHandle = - [_]BindGroupLayoutHandle{.{}} ** max_num_bind_groups_per_pipeline, -}; - -pub const BufferHandle = BufferPool.Handle; -pub const TextureHandle = TexturePool.Handle; -pub const TextureViewHandle = TextureViewPool.Handle; -pub const SamplerHandle = SamplerPool.Handle; -pub const RenderPipelineHandle = RenderPipelinePool.Handle; -pub const ComputePipelineHandle = ComputePipelinePool.Handle; -pub const BindGroupHandle = BindGroupPool.Handle; -pub const BindGroupLayoutHandle = BindGroupLayoutPool.Handle; -pub const PipelineLayoutHandle = PipelineLayoutPool.Handle; - -const BufferPool = ResourcePool(BufferInfo, wgpu.Buffer); -const TexturePool = ResourcePool(TextureInfo, wgpu.Texture); -const TextureViewPool = ResourcePool(TextureViewInfo, wgpu.TextureView); -const SamplerPool = ResourcePool(SamplerInfo, wgpu.Sampler); -const RenderPipelinePool = ResourcePool(RenderPipelineInfo, wgpu.RenderPipeline); -const ComputePipelinePool = ResourcePool(ComputePipelineInfo, wgpu.ComputePipeline); -const BindGroupPool = ResourcePool(BindGroupInfo, wgpu.BindGroup); -const BindGroupLayoutPool = ResourcePool(BindGroupLayoutInfo, wgpu.BindGroupLayout); -const PipelineLayoutPool = ResourcePool(PipelineLayoutInfo, wgpu.PipelineLayout); - -fn ResourcePool(comptime Info: type, comptime Resource: type) type { - const zpool = @import("zpool"); - const Pool = zpool.Pool(16, 16, Resource, struct { info: Info }); - - return struct { - const Self = @This(); - - pub const Handle = Pool.Handle; - - pool: Pool, - - fn init(allocator: std.mem.Allocator, capacity: u32) Self { - const pool = Pool.initCapacity(allocator, capacity) catch unreachable; - return .{ .pool = pool }; - } - - fn deinit(self: *Self, allocator: std.mem.Allocator) void { - _ = allocator; - self.pool.deinit(); - } - - fn addResource(self: *Self, gctx: GraphicsContext, info: Info) Handle { - assert(info.gpuobj != null); - - if (self.pool.addIfNotFull(.{ .info = info })) |handle| { - return handle; - } - - // If pool is free, attempt to remove a resource that is now invalid - // because of dependent resources which have become invalid. - // For example, texture view becomes invalid when parent texture - // is destroyed. - // - // TODO: We could instead store a linked list in Info to track - // dependencies. The parent resource could "point" to the first - // dependent resource, and each dependent resource could "point" to - // the parent and the prev/next dependent resources of the same - // type (perhaps using handles instead of pointers). - // When a parent resource is destroyed, we could traverse that list - // to destroy dependent resources, and when a dependent resource - // is destroyed, we can remove it from the doubly-linked list. - // - // pub const TextureInfo = struct { - // ... - // // note generic name: - // first_dependent_handle: TextureViewHandle = .{} - // }; - // - // pub const TextureViewInfo = struct { - // ... - // // note generic names: - // parent_handle: TextureHandle = .{}, - // prev_dependent_handle: TextureViewHandle, - // next_dependent_handle: TextureViewHandle, - // }; - if (self.removeResourceIfInvalid(gctx)) { - if (self.pool.addIfNotFull(.{ .info = info })) |handle| { - return handle; - } - } - - // TODO: For now we just assert if pool is full - make it more roboust. - assert(false); - return Handle.nil; - } - - fn removeResourceIfInvalid(self: *Self, gctx: GraphicsContext) bool { - var live_handles = self.pool.liveHandles(); - while (live_handles.next()) |live_handle| { - if (!gctx.isResourceValid(live_handle)) { - self.destroyResource(live_handle, true); - return true; - } - } - return false; - } - - fn destroyResource(self: *Self, handle: Handle, comptime call_destroy: bool) void { - if (!self.isHandleValid(handle)) - return; - - const resource_info = self.pool.getColumnPtrAssumeLive(handle, .info); - const gpuobj = resource_info.gpuobj.?; - - if (call_destroy and (Handle == BufferHandle or Handle == TextureHandle)) { - gpuobj.destroy(); - } - gpuobj.release(); - resource_info.* = .{}; - - self.pool.removeAssumeLive(handle); - } - - fn isHandleValid(self: Self, handle: Handle) bool { - return self.pool.isLiveHandle(handle); - } - - fn getInfoPtr(self: Self, handle: Handle) *Info { - return self.pool.getColumnPtrAssumeLive(handle, .info); - } - - fn getInfo(self: Self, handle: Handle) Info { - return self.pool.getColumnAssumeLive(handle, .info); - } - - fn getGpuObj(self: Self, handle: Handle) ?Resource { - if (self.pool.getColumnPtrIfLive(handle, .info)) |info| { - return info.gpuobj; - } - return null; - } - }; -} - -pub fn checkSystem(comptime content_dir: []const u8) !void { - const doSystemCheck = (struct { - fn impl() error{ GraphicsApiUnavailable, InvalidDataFiles }!void { - // TODO: On Windows we should check if DirectX 12 is supported (Windows 10+). - // On Linux we require Vulkan support. - if (@import("builtin").target.os.tag == .linux) { - if (!glfw.vulkanSupported()) { - return error.GraphicsApiUnavailable; - } - _ = glfw.getRequiredInstanceExtensions() catch return error.GraphicsApiUnavailable; - } - // Change directory to where an executable is located. - { - var exe_path_buffer: [1024]u8 = undefined; - const exe_path = std.fs.selfExeDirPath(exe_path_buffer[0..]) catch "./"; - std.os.chdir(exe_path) catch {}; - } - // Make sure font file is a valid data file and not just a Git LFS pointer. - { - const file = std.fs.cwd().openFile( - content_dir ++ "Roboto-Medium.ttf", - .{}, - ) catch return error.InvalidDataFiles; - defer file.close(); - - const size = @intCast(usize, file.getEndPos() catch return error.InvalidDataFiles); - if (size <= 1024) { - return error.InvalidDataFiles; - } - } - } - }).impl; - - doSystemCheck() catch |err| switch (err) { - error.GraphicsApiUnavailable => { - std.debug.print( - \\ - \\GRAPHICS ERROR - \\ - \\This program requires: - \\ - \\ * DirectX 12 graphics driver on Windows - \\ * Vulkan graphics driver on Linux (OpenGL is NOT supported) - \\ * Metal graphics driver on macOS - \\ - \\Please install latest supported driver and try again. - \\ - \\ - , .{}); - return err; - }, - error.InvalidDataFiles => { - std.debug.print( - \\ - \\DATA ERROR - \\ - \\Invalid data files or missing content folder. - \\Please install Git LFS (Large File Support) and run (in the repo): - \\ - \\git lfs install - \\git lfs pull - \\ - \\For more info please see: https://git-lfs.github.com/ - \\ - \\ - , .{}); - return err; - }, - else => unreachable, - }; -} - -const FrameStats = struct { - time: f64 = 0.0, - delta_time: f32 = 0.0, - fps_counter: u32 = 0, - fps: f64 = 0.0, - average_cpu_time: f64 = 0.0, - previous_time: f64 = 0.0, - fps_refresh_time: f64 = 0.0, - cpu_frame_number: u64 = 0, - gpu_frame_number: u64 = 0, - - fn tick(stats: *FrameStats) void { - stats.time = glfw.getTime(); - stats.delta_time = @floatCast(f32, stats.time - stats.previous_time); - stats.previous_time = stats.time; - - if ((stats.time - stats.fps_refresh_time) >= 1.0) { - const t = stats.time - stats.fps_refresh_time; - const fps = @intToFloat(f64, stats.fps_counter) / t; - const ms = (1.0 / fps) * 1000.0; - - stats.fps = fps; - stats.average_cpu_time = ms; - stats.fps_refresh_time = stats.time; - stats.fps_counter = 0; - } - stats.fps_counter += 1; - stats.cpu_frame_number += 1; - } -}; - -const SurfaceDescriptorTag = enum { - metal_layer, - windows_hwnd, - xlib, -}; - -const SurfaceDescriptor = union(SurfaceDescriptorTag) { - metal_layer: struct { - label: ?[*:0]const u8 = null, - layer: *anyopaque, - }, - windows_hwnd: struct { - label: ?[*:0]const u8 = null, - hinstance: *anyopaque, - hwnd: *anyopaque, - }, - xlib: struct { - label: ?[*:0]const u8 = null, - display: *anyopaque, - window: u32, - }, -}; - -pub fn createSurfaceForWindow(instance: wgpu.Instance, window: glfw.Window) wgpu.Surface { - const os_tag = @import("builtin").target.os.tag; - - const descriptor = if (os_tag == .windows) SurfaceDescriptor{ - .windows_hwnd = .{ - .label = "basic surface", - .hinstance = std.os.windows.kernel32.GetModuleHandleW(null).?, - .hwnd = glfw.getWin32Window(window) catch unreachable, - }, - } else if (os_tag == .linux) SurfaceDescriptor{ - .xlib = .{ - .label = "basic surface", - .display = glfw.getX11Display() catch unreachable, - .window = glfw.getX11Window(window) catch unreachable, - }, - } else if (os_tag == .macos) blk: { - const ns_window = glfw.getCocoaWindow(window) catch unreachable; - const ns_view = msgSend(ns_window, "contentView", .{}, *anyopaque); // [nsWindow contentView] - - // Create a CAMetalLayer that covers the whole window that will be passed to CreateSurface. - msgSend(ns_view, "setWantsLayer:", .{true}, void); // [view setWantsLayer:YES] - const layer = msgSend(objc.objc_getClass("CAMetalLayer"), "layer", .{}, ?*anyopaque); // [CAMetalLayer layer] - if (layer == null) @panic("failed to create Metal layer"); - msgSend(ns_view, "setLayer:", .{layer.?}, void); // [view setLayer:layer] - - // Use retina if the window was created with retina support. - const scale_factor = msgSend(ns_window, "backingScaleFactor", .{}, f64); // [ns_window backingScaleFactor] - msgSend(layer.?, "setContentsScale:", .{scale_factor}, void); // [layer setContentsScale:scale_factor] - - break :blk SurfaceDescriptor{ - .metal_layer = .{ - .label = "basic surface", - .layer = layer.?, - }, - }; - } else unreachable; - - return switch (descriptor) { - .metal_layer => |src| blk: { - var desc: wgpu.SurfaceDescriptorFromMetalLayer = undefined; - desc.chain.next = null; - desc.chain.struct_type = .surface_descriptor_from_metal_layer; - desc.layer = src.layer; - break :blk instance.createSurface(.{ - .next_in_chain = @ptrCast(*const wgpu.ChainedStruct, &desc), - .label = if (src.label) |l| l else null, - }); - }, - .windows_hwnd => |src| blk: { - var desc: wgpu.SurfaceDescriptorFromWindowsHWND = undefined; - desc.chain.next = null; - desc.chain.struct_type = .surface_descriptor_from_windows_hwnd; - desc.hinstance = src.hinstance; - desc.hwnd = src.hwnd; - break :blk instance.createSurface(.{ - .next_in_chain = @ptrCast(*const wgpu.ChainedStruct, &desc), - .label = if (src.label) |l| l else null, - }); - }, - .xlib => |src| blk: { - var desc: wgpu.SurfaceDescriptorFromXlibWindow = undefined; - desc.chain.next = null; - desc.chain.struct_type = .surface_descriptor_from_xlib_window; - desc.display = src.display; - desc.window = src.window; - break :blk instance.createSurface(.{ - .next_in_chain = @ptrCast(*const wgpu.ChainedStruct, &desc), - .label = if (src.label) |l| l else null, - }); - }, - }; -} - -const objc = struct { - pub const SEL = ?*opaque {}; - pub const Class = ?*opaque {}; - - pub extern fn sel_getUid(str: [*:0]const u8) SEL; - pub extern fn objc_getClass(name: [*:0]const u8) Class; - pub extern fn objc_msgSend() void; -}; - -fn msgSend(obj: anytype, sel_name: [:0]const u8, args: anytype, comptime ReturnType: type) ReturnType { - const args_meta = @typeInfo(@TypeOf(args)).Struct.fields; - - const FnType = switch (args_meta.len) { - 0 => *const fn (@TypeOf(obj), objc.SEL) callconv(.C) ReturnType, - 1 => *const fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type) callconv(.C) ReturnType, - 2 => *const fn ( - @TypeOf(obj), - objc.SEL, - args_meta[0].field_type, - args_meta[1].field_type, - ) callconv(.C) ReturnType, - 3 => *const fn ( - @TypeOf(obj), - objc.SEL, - args_meta[0].field_type, - args_meta[1].field_type, - args_meta[2].field_type, - ) callconv(.C) ReturnType, - 4 => *const fn ( - @TypeOf(obj), - objc.SEL, - args_meta[0].field_type, - args_meta[1].field_type, - args_meta[2].field_type, - args_meta[3].field_type, - ) callconv(.C) ReturnType, - else => @compileError("[zgpu] Unsupported number of args"), - }; - - const func = @ptrCast(FnType, &objc.objc_msgSend); - const sel = objc.sel_getUid(sel_name.ptr); - - return @call(.{}, func, .{ obj, sel } ++ args); -} - -fn printUnhandledError( - err_type: wgpu.ErrorType, - message: [*:0]const u8, - userdata: ?*anyopaque, -) callconv(.C) void { - _ = userdata; - switch (err_type) { - .validation => std.debug.print("[zgpu] Validation error: {s}\n", .{message}), - .out_of_memory => std.debug.print("[zgpu] Out of memory: {s}\n", .{message}), - .device_lost => std.debug.print("[zgpu] Device lost: {s}\n", .{message}), - .unknown => std.debug.print("[zgpu] Unknown error: {s}\n", .{message}), - else => unreachable, - } - // TODO: Do something better. - std.process.exit(1); -} - -fn handleToGpuResourceType(comptime T: type) type { - return switch (T) { - BufferHandle => wgpu.Buffer, - TextureHandle => wgpu.Texture, - TextureViewHandle => wgpu.TextureView, - SamplerHandle => wgpu.Sampler, - RenderPipelineHandle => wgpu.RenderPipeline, - ComputePipelineHandle => wgpu.ComputePipeline, - BindGroupHandle => wgpu.BindGroup, - BindGroupLayoutHandle => wgpu.BindGroupLayout, - PipelineLayoutHandle => wgpu.PipelineLayout, - else => @compileError("[zgpu] handleToGpuResourceType() not implemented for " ++ @typeName(T)), - }; -} - -fn handleToResourceInfoType(comptime T: type) type { - return switch (T) { - BufferHandle => BufferInfo, - TextureHandle => TextureInfo, - TextureViewHandle => TextureViewInfo, - SamplerHandle => SamplerInfo, - RenderPipelineHandle => RenderPipelineInfo, - ComputePipelineHandle => ComputePipelineInfo, - BindGroupHandle => BindGroupInfo, - BindGroupLayoutHandle => BindGroupLayoutInfo, - PipelineLayoutHandle => PipelineLayoutInfo, - else => @compileError("[zgpu] handleToResourceInfoType() not implemented for " ++ @typeName(T)), - }; -} - -fn formatToShaderFormat(format: wgpu.TextureFormat) []const u8 { - // TODO: Add missing formats. - return switch (format) { - .rgba8_unorm => "rgba8unorm", - .rgba8_snorm => "rgba8snorm", - .rgba16_float => "rgba16float", - .rgba32_float => "rgba32float", - else => unreachable, - }; -} - -const expect = std.testing.expect; - -test "zgpu.wgpu.init" { - const instance = createWgpuInstance(); - instance.reference(); - instance.release(); - - const adapter = adapter: { - const Response = struct { - status: wgpu.RequestAdapterStatus = .unknown, - adapter: wgpu.Adapter = undefined, - }; - - const callback = (struct { - fn callback( - status: wgpu.RequestAdapterStatus, - adapter: wgpu.Adapter, - message: ?[*:0]const u8, - userdata: ?*anyopaque, - ) callconv(.C) void { - _ = message; - - var response = @ptrCast(*Response, @alignCast(@sizeOf(usize), userdata)); - response.status = status; - response.adapter = adapter; - - if (status != .success) { - std.debug.print("Failed to request GPU adapter (status: {any}).\n", .{status}); - } - } - }).callback; - - var response = Response{}; - instance.requestAdapter( - .{ .power_preference = .high_performance }, - callback, - @ptrCast(*anyopaque, &response), - ); - try expect(response.status == .success); - - const adapter = response.adapter; - - var features: [32]wgpu.FeatureName = undefined; - const num_adapter_features = std.math.min(adapter.enumerateFeatures(null), features.len); - _ = adapter.enumerateFeatures(&features); - _ = num_adapter_features; - - var properties: wgpu.AdapterProperties = undefined; - adapter.getProperties(&properties); - - break :adapter adapter; - }; - defer adapter.release(); - - const device = device: { - const Response = struct { - status: wgpu.RequestDeviceStatus = .unknown, - device: wgpu.Device = undefined, - }; - - const callback = (struct { - fn callback( - status: wgpu.RequestDeviceStatus, - device: wgpu.Device, - message: ?[*:0]const u8, - userdata: ?*anyopaque, - ) callconv(.C) void { - _ = message; - - var response = @ptrCast(*Response, @alignCast(@sizeOf(usize), userdata)); - response.status = status; - response.device = device; - - if (status != .success) { - std.debug.print("Failed to request GPU device (status: {any}).\n", .{status}); - } - } - }).callback; - - var response = Response{}; - adapter.requestDevice( - wgpu.DeviceDescriptor{}, - callback, - @ptrCast(*anyopaque, &response), - ); - try expect(response.status == .success); - break :device response.device; - }; - defer device.release(); -} diff --git a/libs/zgui/README.md b/libs/zgui/README.md deleted file mode 100644 index 6deee0f5a..000000000 --- a/libs/zgui/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# zgui - dear imgui bindings - -Easy to use, hand-crafted API with default arguments, named parameters and Zig style text formatting. For a test application please see [here](https://github.com/michal-z/zig-gamedev/tree/main/samples/gui_test_wgpu). - -## Getting started - -Copy `zgui` and `zglfw` folders to a `libs` subdirectory of the root of your project. - -Then in your `build.zig` add: -```zig -const zgui = @import("libs/zgui/build.zig"); -const zglfw = @import("libs/zglfw/build.zig"); - -pub fn build(b: *std.build.Builder) void { - ... - const zgui_pkg = zgui.getPkg(&.{zglfw.pkg}); - - exe.addPackage(zgui_pkg); - exe.addPackage(zglfw.pkg); - - zgui.link(exe); - zglfw.link(exe); -} -``` -Now in your code you may import and use `zgui`: -```zig -const zgui = @import("zgui"); - -zgui.init(); -defer zgui.deinit(); - -_ = zgui.io.addFontFromFile(content_dir ++ "Roboto-Medium.ttf", 16.0); - -zgui.backend.init( - window, - demo.gctx.device, - @enumToInt(swapchain_format), -); -defer zgui.backend.deinit(); - -var value0: f32 = 0.0; -var value1: f32 = 0.0; - -// Main loop -while (...) { - zgui.backend.newFrame(framebuffer_width, framebuffer_height); - - zgui.bulletText( - "Average : {d:.3} ms/frame ({d:.1} fps)", - .{ demo.gctx.stats.average_cpu_time, demo.gctx.stats.fps }, - ); - zgui.bulletText("W, A, S, D : move camera", .{}); - zgui.spacing(); - - if (zgui.button("Setup Scene", .{})) { - // Button pressed. - } - - if (zgui.dragFloat("Drag 1", .{ .v = &value0 })) { - // value0 has changed - } - - if (zgui.dragFloat("Drag 2", .{ .v = &value0, .min = -1.0, .max = 1.0 })) { - // value1 has changed - } - - // Setup wgpu render pass here - - zgui.backend.draw(pass); -} -``` diff --git a/libs/zgui/build.zig b/libs/zgui/build.zig deleted file mode 100644 index d87f51e93..000000000 --- a/libs/zgui/build.zig +++ /dev/null @@ -1,29 +0,0 @@ -const std = @import("std"); - -pub fn getPkg(dependencies: []const std.build.Pkg) std.build.Pkg { - return .{ - .name = "zgui", - .source = .{ .path = thisDir() ++ "/src/main.zig" }, - .dependencies = dependencies, - }; -} - -pub fn link(exe: *std.build.LibExeObjStep) void { - exe.addCSourceFile(thisDir() ++ "/src/zgui.cpp", &.{""}); - exe.addIncludePath(thisDir() ++ "/libs"); - - exe.addCSourceFile(thisDir() ++ "/libs/imgui/imgui.cpp", &.{""}); - exe.addCSourceFile(thisDir() ++ "/libs/imgui/imgui_widgets.cpp", &.{""}); - exe.addCSourceFile(thisDir() ++ "/libs/imgui/imgui_tables.cpp", &.{""}); - exe.addCSourceFile(thisDir() ++ "/libs/imgui/imgui_draw.cpp", &.{""}); - exe.addCSourceFile(thisDir() ++ "/libs/imgui/imgui_demo.cpp", &.{""}); - exe.addCSourceFile(thisDir() ++ "/libs/imgui/imgui_impl_glfw.cpp", &.{""}); - exe.addCSourceFile(thisDir() ++ "/libs/imgui/imgui_impl_wgpu.cpp", &.{""}); - exe.addCSourceFile(thisDir() ++ "/libs/imgui/implot_demo.cpp", &.{""}); - exe.addCSourceFile(thisDir() ++ "/libs/imgui/implot.cpp", &.{""}); - exe.addCSourceFile(thisDir() ++ "/libs/imgui/implot_items.cpp", &.{""}); -} - -inline fn thisDir() []const u8 { - return comptime std.fs.path.dirname(@src().file) orelse "."; -} diff --git a/libs/zgui/libs/imgui/LICENSE.txt b/libs/zgui/libs/imgui/LICENSE.txt deleted file mode 100644 index 4023e0caa..000000000 --- a/libs/zgui/libs/imgui/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014-2022 Omar Cornut - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/libs/zgui/libs/imgui/imconfig.h b/libs/zgui/libs/imgui/imconfig.h deleted file mode 100644 index 72dc7c694..000000000 --- a/libs/zgui/libs/imgui/imconfig.h +++ /dev/null @@ -1,126 +0,0 @@ -//----------------------------------------------------------------------------- -// COMPILE-TIME OPTIONS FOR DEAR IMGUI -// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. -// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. -//----------------------------------------------------------------------------- -// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) -// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. -//----------------------------------------------------------------------------- -// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp -// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. -// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. -// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. -//----------------------------------------------------------------------------- - -#pragma once - -//---- Define assertion handler. Defaults to calling assert(). -// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. -//#define IM_ASSERT(_EXPR) MyAssert(_EXPR) -//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts - -//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows -// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. -// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() -// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. -//#define IMGUI_API __declspec( dllexport ) -//#define IMGUI_API __declspec( dllimport ) - -//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. -// mziulek: We disable obsolete stuff. -#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS -#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions. - -//---- Disable all of Dear ImGui or don't implement standard windows/tools. -// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp. -//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. -//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. -//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88). - -//---- Don't implement some functions to reduce linkage requirements. -//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) -//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) -//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) -//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). -//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). -//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) -//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. -//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) -//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. -//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). -//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available - -//---- Include imgui_user.h at the end of imgui.h as a convenience -//#define IMGUI_INCLUDE_IMGUI_USER_H - -//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) -//#define IMGUI_USE_BGRA_PACKED_COLOR - -//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) -//#define IMGUI_USE_WCHAR32 - -//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version -// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. -//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" -//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" -//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled -//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION -//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION - -//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) -// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h. -//#define IMGUI_USE_STB_SPRINTF - -//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) -// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). -// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. -//#define IMGUI_ENABLE_FREETYPE - -//---- Use stb_truetype to build and rasterize the font atlas (default) -// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. -//#define IMGUI_ENABLE_STB_TRUETYPE - -//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. -// This will be inlined as part of ImVec2 and ImVec4 class declarations. -/* -#define IM_VEC2_CLASS_EXTRA \ - constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \ - operator MyVec2() const { return MyVec2(x,y); } - -#define IM_VEC4_CLASS_EXTRA \ - constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \ - operator MyVec4() const { return MyVec4(x,y,z,w); } -*/ - -//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. -// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). -// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. -// Read about ImGuiBackendFlags_RendererHasVtxOffset for details. -//#define ImDrawIdx unsigned int - -//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) -//struct ImDrawList; -//struct ImDrawCmd; -//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); -//#define ImDrawCallback MyImDrawCallback - -//---- Debug Tools: Macro to break in Debugger -// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) -//#define IM_DEBUG_BREAK IM_ASSERT(0) -//#define IM_DEBUG_BREAK __debugbreak() - -//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(), -// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.) -// This adds a small runtime cost which is why it is not enabled by default. -//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX - -//---- Debug Tools: Enable slower asserts -//#define IMGUI_DEBUG_PARANOID - -//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. -/* -namespace ImGui -{ - void MyFunction(const char* name, const MyMatrix44& v); -} -*/ diff --git a/libs/zgui/libs/imgui/imgui.cpp b/libs/zgui/libs/imgui/imgui.cpp deleted file mode 100644 index 01982c3da..000000000 --- a/libs/zgui/libs/imgui/imgui.cpp +++ /dev/null @@ -1,13444 +0,0 @@ -// dear imgui, v1.88 -// (main code and documentation) - -// Help: -// - Read FAQ at http://dearimgui.org/faq -// - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase. -// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. -// Read imgui.cpp for details, links and comments. - -// Resources: -// - FAQ http://dearimgui.org/faq -// - Homepage & latest https://github.com/ocornut/imgui -// - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/5243 (please post your screenshots/video there!) -// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) -// - Glossary https://github.com/ocornut/imgui/wiki/Glossary -// - Issues & support https://github.com/ocornut/imgui/issues - -// Getting Started? -// - For first-time users having issues compiling/linking/running or issues loading fonts: -// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. - -// Developed by Omar Cornut and every direct or indirect contributors to the GitHub. -// See LICENSE.txt for copyright and licensing details (standard MIT License). -// This library is free but needs your support to sustain development and maintenance. -// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com". -// Individuals: you can support continued development via donations. See docs/README or web page. - -// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. -// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without -// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't -// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you -// to a better solution or official support for them. - -/* - -Index of this file: - -DOCUMENTATION - -- MISSION STATEMENT -- END-USER GUIDE -- PROGRAMMER GUIDE - - READ FIRST - - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI - - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE - - HOW A SIMPLE APPLICATION MAY LOOK LIKE - - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE - - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS -- API BREAKING CHANGES (read me when you update!) -- FREQUENTLY ASKED QUESTIONS (FAQ) - - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer) - -CODE -(search for "[SECTION]" in the code to find them) - -// [SECTION] INCLUDES -// [SECTION] FORWARD DECLARATIONS -// [SECTION] CONTEXT AND MEMORY ALLOCATORS -// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) -// [SECTION] MISC HELPERS/UTILITIES (Geometry functions) -// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) -// [SECTION] MISC HELPERS/UTILITIES (File functions) -// [SECTION] MISC HELPERS/UTILITIES (ImText* functions) -// [SECTION] MISC HELPERS/UTILITIES (Color functions) -// [SECTION] ImGuiStorage -// [SECTION] ImGuiTextFilter -// [SECTION] ImGuiTextBuffer -// [SECTION] ImGuiListClipper -// [SECTION] STYLING -// [SECTION] RENDER HELPERS -// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) -// [SECTION] INPUTS -// [SECTION] ERROR CHECKING -// [SECTION] LAYOUT -// [SECTION] SCROLLING -// [SECTION] TOOLTIPS -// [SECTION] POPUPS -// [SECTION] KEYBOARD/GAMEPAD NAVIGATION -// [SECTION] DRAG AND DROP -// [SECTION] LOGGING/CAPTURING -// [SECTION] SETTINGS -// [SECTION] VIEWPORTS -// [SECTION] PLATFORM DEPENDENT HELPERS -// [SECTION] METRICS/DEBUGGER WINDOW -// [SECTION] DEBUG LOG WINDOW -// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) - -*/ - -//----------------------------------------------------------------------------- -// DOCUMENTATION -//----------------------------------------------------------------------------- - -/* - - MISSION STATEMENT - ================= - - - Easy to use to create code-driven and data-driven tools. - - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools. - - Easy to hack and improve. - - Minimize setup and maintenance. - - Minimize state storage on user side. - - Minimize state synchronization. - - Portable, minimize dependencies, run on target (consoles, phones, etc.). - - Efficient runtime and memory consumption. - - Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes: - - - Doesn't look fancy, doesn't animate. - - Limited layout features, intricate layouts are typically crafted in code. - - - END-USER GUIDE - ============== - - - Double-click on title bar to collapse window. - - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin(). - - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents). - - Click and drag on any empty space to move window. - - TAB/SHIFT+TAB to cycle through keyboard editable fields. - - CTRL+Click on a slider or drag box to input value as text. - - Use mouse wheel to scroll. - - Text editor: - - Hold SHIFT or use mouse to select text. - - CTRL+Left/Right to word jump. - - CTRL+Shift+Left/Right to select words. - - CTRL+A our Double-Click to select all. - - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/ - - CTRL+Z,CTRL+Y to undo/redo. - - ESCAPE to revert text to its original value. - - Controls are automatically adjusted for OSX to match standard OSX text editing operations. - - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. - - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets - - - PROGRAMMER GUIDE - ================ - - READ FIRST - ---------- - - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki) - - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction or - destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs. - - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. - - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build. - - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori). - You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki. - - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances. - For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI, - where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches. - - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. - - This codebase is also optimized to yield decent performances with typical "Debug" builds settings. - - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected). - If you get an assert, read the messages and comments around the assert. - - C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace. - - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types. - See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that. - However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase. - - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!). - - - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI - ---------------------------------------------- - - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h) - - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master". - - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file. - - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. - If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed - from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will - likely be a comment about it. Please report any issue to the GitHub page! - - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file. - - Try to keep your copy of Dear ImGui reasonably up to date. - - - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE - --------------------------------------------------------------- - - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. - - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder. - - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system. - It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL). - - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types. - - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them. - - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide. - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" - phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render(). - - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code. - - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder. - - - HOW A SIMPLE APPLICATION MAY LOOK LIKE - -------------------------------------- - EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder). - The sub-folders in examples/ contain examples applications following this structure. - - // Application init: create a dear imgui context, setup some options, load fonts - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. - // TODO: Fill optional fields of the io structure later. - // TODO: Load TTF/OTF fonts if you don't want to use the default font. - - // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp) - ImGui_ImplWin32_Init(hwnd); - ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); - - // Application main loop - while (true) - { - // Feed inputs to dear imgui, start new frame - ImGui_ImplDX11_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); - - // Any application code here - ImGui::Text("Hello, world!"); - - // Render dear imgui into screen - ImGui::Render(); - ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - g_pSwapChain->Present(1, 0); - } - - // Shutdown - ImGui_ImplDX11_Shutdown(); - ImGui_ImplWin32_Shutdown(); - ImGui::DestroyContext(); - - EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE - - // Application init: create a dear imgui context, setup some options, load fonts - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. - // TODO: Fill optional fields of the io structure later. - // TODO: Load TTF/OTF fonts if you don't want to use the default font. - - // Build and load the texture atlas into a texture - // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer) - int width, height; - unsigned char* pixels = NULL; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - - // At this point you've got the texture data and you need to upload that to your graphic system: - // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'. - // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID. - MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32) - io.Fonts->SetTexID((void*)texture); - - // Application main loop - while (true) - { - // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc. - // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends) - io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds) - io.DisplaySize.x = 1920.0f; // set the current display width - io.DisplaySize.y = 1280.0f; // set the current display height here - io.AddMousePosEvent(mouse_x, mouse_y); // update mouse position - io.AddMouseButtonEvent(0, mouse_b[0]); // update mouse button states - io.AddMouseButtonEvent(1, mouse_b[1]); // update mouse button states - - // Call NewFrame(), after this point you can use ImGui::* functions anytime - // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere) - ImGui::NewFrame(); - - // Most of your application code here - ImGui::Text("Hello, world!"); - MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); - MyGameRender(); // may use any Dear ImGui functions as well! - - // Render dear imgui, swap buffers - // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code) - ImGui::EndFrame(); - ImGui::Render(); - ImDrawData* draw_data = ImGui::GetDrawData(); - MyImGuiRenderFunction(draw_data); - SwapBuffers(); - } - - // Shutdown - ImGui::DestroyContext(); - - To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application, - you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! - Please read the FAQ and example applications for details about this! - - - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE - --------------------------------------------- - The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function. - - void void MyImGuiRenderFunction(ImDrawData* draw_data) - { - // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled - // TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering. - // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize - // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize - // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. - ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); - ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); - if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) - continue; - - // We are using scissoring to clip some objects. All low-level graphics API should support it. - // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches - // (some elements visible outside their bounds) but you can fix that once everything else works! - // - Clipping coordinates are provided in imgui coordinates space: - // - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size - // - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values. - // - In the interest of supporting multi-viewport applications (see 'docking' branch on github), - // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. - // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) - MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y); - - // The texture for the draw call is specified by pcmd->GetTexID(). - // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. - MyEngineBindTexture((MyTexture*)pcmd->GetTexID()); - - // Render 'pcmd->ElemCount/3' indexed triangles. - // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices. - MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset); - } - } - } - } - - - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS - ------------------------------------------ - - The gamepad/keyboard navigation is fairly functional and keeps being improved. - - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse! - - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787 - - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - - Keyboard: - - Application: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. - - Internally: NewFrame() will automatically fill io.NavInputs[] based on backend's io.AddKeyEvent() calls. - - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), - the io.WantCaptureKeyboard flag will be set. For more advanced uses, you may want to read from: - - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions. - Please reach out if you think the game vs navigation input sharing could be improved. - - Gamepad: - - Application: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. - - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys. - For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly. - Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). - - Internally: NewFrame() will automatically fill io.NavInputs[] based on backend's io.AddKeyEvent() + io.AddKeyAnalogEvent() calls. - - BEFORE 1.87, BACKENDS USED TO WRITE DIRECTLY TO io.NavInputs[]. This is going to be obsoleted in the future. Please call io functions instead! - - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets - - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing, - with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. - - Mouse: - - PS4/PS5 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard. - - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag. - Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements. - When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved. - When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that. - (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse moving back and forth!) - (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want - to set a boolean to ignore your other external mouse positions until the external source is moved again.) - - - API BREAKING CHANGES - ==================== - - Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. - Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code. - When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. - You can read releases logs https://github.com/ocornut/imgui/releases for more details. - - - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete). - - 2022/05/03 (1.88) - backends: osx: removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. All ImGui_ImplOSX_HandleEvent() calls should be removed as they are now unnecessary. - - 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO. - - 2022/01/20 (1.87) - inputs: reworded gamepad IO. - - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values. - - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used). - - 2022/01/17 (1.87) - inputs: reworked mouse IO. - - Backend writing to io.MousePos -> backend should call io.AddMousePosEvent() - - Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent() - - Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent() - - Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only] - note: for all calls to IO new functions, the Dear ImGui context should be bound/current. - read https://github.com/ocornut/imgui/issues/4921 for details. - - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details. - - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX) - - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) - - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to stil function with legacy key codes). - - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiKey_ModXXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiKey_ModXXX values.* - - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") because those values are now larger than the legacy KeyDown[] array. Will assert. - - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. - - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. - - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) - - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() - - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x - - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing()); - - ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect - - ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex - - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings - - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. - - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. - - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): - - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList() - - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder - - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID(). - - if you are using official backends from the source tree: you have nothing to do. - - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID(). - - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags. - - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft - - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight - - ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc. - flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API. - breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners": - - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use) - - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use) - - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use) - - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f. - this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok. - the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts. - legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise). - - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018): - - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY() - - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing. - - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future. - - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'. - - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed. - - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete). - - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete). - - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete). - - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags. - - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags. - - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. - - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018): - - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit(). - - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg - - ImGuiInputTextCallback -> use ImGuiTextEditCallback - - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData - - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete). - - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added! - - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API. - - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures - - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/. - - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018): - - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend - - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow) - - ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow) - - ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT - - ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT - - removed redirecting functions names that were marked obsolete in 1.61 (May 2018): - - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision. - - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter. - - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed). - - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently). - - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. - - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory. - - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result. - - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now! - - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar(). - replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags). - worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions: - - if you omitted the 'power' parameter (likely!), you are not affected. - - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct. - - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f. - see https://github.com/ocornut/imgui/issues/3361 for all details. - kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used. - for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. - - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime. - - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems. - - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79] - - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017. - - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular(). - - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more. - - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead. - - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value. - - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017): - - ShowTestWindow() -> use ShowDemoWindow() - - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow) - - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) - - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f) - - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing() - - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg - - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding - - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap - - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS - - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API. - - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it). - - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert. - - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency. - - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency. - - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017): - - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed - - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) - - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding() - - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f) - - ImFont::Glyph -> use ImFontGlyph - - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse "typematic" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function. - if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix. - The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay). - If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you. - - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete). - - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete). - - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71. - - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have - overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering. - This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows. - Please reach out if you are affected. - - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete). - - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c). - - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now. - - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete). - - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete). - - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete). - - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value! - - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). - - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead! - - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete). - - 2018/12/20 (1.67) - made it illegal to call Begin("") with an empty string. This somehow half-worked before but had various undesirable side-effects. - - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags. - - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files. - - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete). - - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h. - If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths. - - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427) - - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp. - NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED. - Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions. - - 2018/08/22 (1.63) - renamed IsItemDeactivatedAfterChange() to IsItemDeactivatedAfterEdit() for consistency with new IsItemEdited() API. Kept redirection function (will obsolete soonish as IsItemDeactivatedAfterChange() is very recent). - - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete). - - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). - - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges [update 1.67 renamed to ConfigWindowsResizeFromEdges] to enable the feature. - - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency. - - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. - - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete). - - 2018/06/08 (1.62) - examples: the imgui_impl_XXX files have been split to separate platform (Win32, GLFW, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan, etc.). - old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports. - when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call. - in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function. - - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set. - - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details. - - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more. - If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format. - To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code. - If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. "DragInt.*%f" to help you find them. - - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional "int decimal_precision" in favor of an equivalent and more flexible "const char* format", - consistent with other functions. Kept redirection functions (will obsolete). - - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value. - - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some backend ahead of merging the Nav branch). - - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. - - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically. - - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. - - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment. - - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display. - - 2018/02/07 (1.60) - reorganized context handling to be more explicit, - - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. - - removed Shutdown() function, as DestroyContext() serve this purpose. - - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance. - - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts. - - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts. - - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths. - - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete). - - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete). - - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData. - - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side. - - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete). - - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags - - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame. - - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set. - - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete). - - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete). - - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete). - - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete). - - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete). - - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed. - - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up. - Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions. - - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency. - - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg. - - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding. - - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); - - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency. - - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it. - - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details. - removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting. - IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly) - IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow) - IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior] - - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead! - - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete). - - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete). - - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete). - - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your backend if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)". - - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)! - - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). - - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete). - - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency. - - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix. - - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type. - - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely. - - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete). - - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete). - - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). - - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu. - - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options. - - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0))' - - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse - - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. - - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. - - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetID() and use it instead of passing string to BeginChild(). - - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. - - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc. - - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully, breakage should be minimal. - - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. - If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. - This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color: - ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); } - If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color. - - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). - - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. - - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen). - - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. - - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref GitHub issue #337). - - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337) - - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). - - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert. - - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you. - - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis. - - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete. - - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position. - GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side. - GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out! - - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize - - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project. - - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason - - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure. - you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text. - - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost. - this necessary change will break your rendering function! the fix should be very easy. sorry for that :( - - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest. - - the signature of the io.RenderDrawListsFn handler has changed! - old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) - new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). - parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount' - ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new. - ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'. - - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer. - - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering! - - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade! - - 2015/07/10 (1.43) - changed SameLine() parameters from int to float. - - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). - - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount. - - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence - - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry! - - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). - - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). - - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons. - - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened. - - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same). - - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50. - - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API - - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. - - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead. - - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50. - - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing - - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50. - - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing) - - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50. - - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. - - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. - - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior - - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() - - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) - - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. - - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. - - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. - - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..]; - - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier); - you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation. - - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID() - - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) - - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets - - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) - - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph) - - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility - - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered() - - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly) - - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) - - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale() - - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn - - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically) - - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite - - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes - - - FREQUENTLY ASKED QUESTIONS (FAQ) - ================================ - - Read all answers online: - https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url) - Read all answers locally (with a text editor or ideally a Markdown viewer): - docs/FAQ.md - Some answers are copied down here to facilitate searching in code. - - Q&A: Basics - =========== - - Q: Where is the documentation? - A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++. - - Run the examples/ and explore them. - - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. - - The demo covers most features of Dear ImGui, so you can read the code and see its output. - - See documentation and comments at the top of imgui.cpp + effectively imgui.h. - - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the - examples/ folder to explain how to integrate Dear ImGui with your own engine/application. - - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links. - - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful. - - Your programming IDE is your friend, find the type or function declaration to find comments - associated with it. - - Q: What is this library called? - Q: Which version should I get? - >> This library is called "Dear ImGui", please don't call it "ImGui" :) - >> See https://www.dearimgui.org/faq for details. - - Q&A: Integration - ================ - - Q: How to get started? - A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt. - - Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application? - A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! - >> See https://www.dearimgui.org/faq for a fully detailed answer. You really want to read this. - - Q. How can I enable keyboard controls? - Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) - Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... - Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around... - Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries... - >> See https://www.dearimgui.org/faq - - Q&A: Usage - ---------- - - Q: About the ID Stack system.. - - Why is my widget not reacting when I click on it? - - How can I have widgets with an empty label? - - How can I have multiple widgets with the same label? - - How can I have multiple windows with the same label? - Q: How can I display an image? What is ImTextureID, how does it works? - Q: How can I use my own math types instead of ImVec2/ImVec4? - Q: How can I interact with standard C++ types (such as std::string and std::vector)? - Q: How can I display custom shapes? (using low-level ImDrawList API) - >> See https://www.dearimgui.org/faq - - Q&A: Fonts, Text - ================ - - Q: How should I handle DPI in my application? - Q: How can I load a different font than the default? - Q: How can I easily use icons in my application? - Q: How can I load multiple fonts? - Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? - >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md - - Q&A: Concerns - ============= - - Q: Who uses Dear ImGui? - Q: Can you create elaborate/serious tools with Dear ImGui? - Q: Can you reskin the look of Dear ImGui? - Q: Why using C++ (as opposed to C)? - >> See https://www.dearimgui.org/faq - - Q&A: Community - ============== - - Q: How can I help? - A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui! - We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. - This is among the most useful thing you can do for Dear ImGui. With increased funding, we can hire more people working on this project. - - Individuals: you can support continued development via PayPal donations. See README. - - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt - and see how you want to help and can help! - - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. - You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers. - But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions. - - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on GitHub or privately). - -*/ - -//------------------------------------------------------------------------- -// [SECTION] INCLUDES -//------------------------------------------------------------------------- - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "imgui.h" -#ifndef IMGUI_DISABLE - -#ifndef IMGUI_DEFINE_MATH_OPERATORS -#define IMGUI_DEFINE_MATH_OPERATORS -#endif -#include "imgui_internal.h" - -// System includes -#include // toupper -#include // vsnprintf, sscanf, printf -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else -#include // intptr_t -#endif - -// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled -#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) -#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS -#endif - -// [Windows] OS specific includes (optional) -#if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) -#define IMGUI_DISABLE_WIN32_FUNCTIONS -#endif -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#ifndef NOMINMAX -#define NOMINMAX -#endif -#ifndef __MINGW32__ -#include // _wfopen, OpenClipboard -#else -#include -#endif -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions -#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS -#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS -#endif -#endif - -// [Apple] OS specific includes -#if defined(__APPLE__) -#include -#endif - -// Visual Studio warnings -#ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later -#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types -#endif -#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). -#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). -#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). -#endif - -// Clang/GCC warnings with -Weverything -#if defined(__clang__) -#if __has_warning("-Wunknown-warning-option") -#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! -#endif -#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' -#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. -#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. -#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. -#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning: declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. -#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is. -#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness -#pragma clang diagnostic ignored "-Wformat-pedantic" // warning: format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic. -#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int' -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 -#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. -#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision -#elif defined(__GNUC__) -// We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association. -#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind -#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used -#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size -#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*' -#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function -#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value -#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked -#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false -#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead -#endif - -// Debug options -#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL -#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window -#define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower) - -// When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. -static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in -static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear - -// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend) -static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow(). -static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. -static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. - -//------------------------------------------------------------------------- -// [SECTION] FORWARD DECLARATIONS -//------------------------------------------------------------------------- - -static void SetCurrentWindow(ImGuiWindow* window); -static void FindHoveredWindow(); -static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags); -static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); - -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); -static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); - -// Settings -static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); -static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); -static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); -static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); -static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf); - -// Platform Dependents default implementation for IO functions -static const char* GetClipboardTextFn_DefaultImpl(void* user_data); -static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); -static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data); - -namespace ImGui -{ -// Navigation -static void NavUpdate(); -static void NavUpdateWindowing(); -static void NavUpdateWindowingOverlay(); -static void NavUpdateCancelRequest(); -static void NavUpdateCreateMoveRequest(); -static void NavUpdateCreateTabbingRequest(); -static float NavUpdatePageUpPageDown(); -static inline void NavUpdateAnyRequestFlag(); -static void NavUpdateCreateWrappingRequest(); -static void NavEndFrame(); -static bool NavScoreItem(ImGuiNavItemData* result); -static void NavApplyItemToResult(ImGuiNavItemData* result); -static void NavProcessItem(); -static void NavProcessItemForTabbingRequest(ImGuiID id); -static ImVec2 NavCalcPreferredRefPos(); -static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); -static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); -static void NavRestoreLayer(ImGuiNavLayer layer); -static void NavRestoreHighlightAfterMove(); -static int FindWindowFocusIndex(ImGuiWindow* window); - -// Error Checking and Debug Tools -static void ErrorCheckNewFrameSanityChecks(); -static void ErrorCheckEndFrameSanityChecks(); -static void UpdateDebugToolItemPicker(); -static void UpdateDebugToolStackQueries(); - -// Misc -static void UpdateSettings(); -static void UpdateKeyboardInputs(); -static void UpdateMouseInputs(); -static void UpdateMouseWheel(); -static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); -static void RenderWindowOuterBorders(ImGuiWindow* window); -static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); -static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); -static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); -static void RenderDimmedBackgrounds(); -static ImGuiWindow* FindBlockingModal(ImGuiWindow* window); - -// Viewports -static void UpdateViewportsNewFrame(); - -} - -//----------------------------------------------------------------------------- -// [SECTION] CONTEXT AND MEMORY ALLOCATORS -//----------------------------------------------------------------------------- - -// DLL users: -// - Heaps and globals are not shared across DLL boundaries! -// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from. -// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL). -// - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. -// - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in). - -// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL. -// - ImGui::CreateContext() will automatically set this pointer if it is NULL. -// Change to a different context by calling ImGui::SetCurrentContext(). -// - Important: Dear ImGui functions are not thread-safe because of this pointer. -// If you want thread-safety to allow N threads to access N different contexts: -// - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h: -// struct ImGuiContext; -// extern thread_local ImGuiContext* MyImGuiTLS; -// #define GImGui MyImGuiTLS -// And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword. -// - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 -// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace. -// - DLL users: read comments above. -#ifndef GImGui -ImGuiContext* GImGui = NULL; -#endif - -// Memory Allocator functions. Use SetAllocatorFunctions() to change them. -// - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction. -// - DLL users: read comments above. -#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS -static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); } -static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); } -#else -static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; } -static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); } -#endif -static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper; -static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper; -static void* GImAllocatorUserData = NULL; - -//----------------------------------------------------------------------------- -// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) -//----------------------------------------------------------------------------- - -ImGuiStyle::ImGuiStyle() -{ - Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui. - DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. - WindowPadding = ImVec2(8,8); // Padding within a window - WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. - WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. - WindowMinSize = ImVec2(32,32); // Minimum window size - WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text - WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. - ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows - ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. - PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows - PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested. - FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) - FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). - FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. - ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines - ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) - CellPadding = ImVec2(4,2); // Padding within a table cell - TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! - IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). - ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). - ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar - ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar - GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar - GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. - LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. - TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. - TabBorderSize = 0.0f; // Thickness of border around tabs. - TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. - ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. - ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. - SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. - DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. - DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. - MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. - AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. - AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). - AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). - CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. - CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. - - // Default theme - ImGui::StyleColorsDark(this); -} - -// To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you. -// Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times. -void ImGuiStyle::ScaleAllSizes(float scale_factor) -{ - WindowPadding = ImFloor(WindowPadding * scale_factor); - WindowRounding = ImFloor(WindowRounding * scale_factor); - WindowMinSize = ImFloor(WindowMinSize * scale_factor); - ChildRounding = ImFloor(ChildRounding * scale_factor); - PopupRounding = ImFloor(PopupRounding * scale_factor); - FramePadding = ImFloor(FramePadding * scale_factor); - FrameRounding = ImFloor(FrameRounding * scale_factor); - ItemSpacing = ImFloor(ItemSpacing * scale_factor); - ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor); - CellPadding = ImFloor(CellPadding * scale_factor); - TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor); - IndentSpacing = ImFloor(IndentSpacing * scale_factor); - ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor); - ScrollbarSize = ImFloor(ScrollbarSize * scale_factor); - ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor); - GrabMinSize = ImFloor(GrabMinSize * scale_factor); - GrabRounding = ImFloor(GrabRounding * scale_factor); - LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor); - TabRounding = ImFloor(TabRounding * scale_factor); - TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; - DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); - DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); - MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); -} - -ImGuiIO::ImGuiIO() -{ - // Most fields are initialized with zero - memset(this, 0, sizeof(*this)); - IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); - - // Settings - ConfigFlags = ImGuiConfigFlags_None; - BackendFlags = ImGuiBackendFlags_None; - DisplaySize = ImVec2(-1.0f, -1.0f); - DeltaTime = 1.0f / 60.0f; - IniSavingRate = 5.0f; - IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). - LogFilename = "imgui_log.txt"; - MouseDoubleClickTime = 0.30f; - MouseDoubleClickMaxDist = 6.0f; -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - for (int i = 0; i < ImGuiKey_COUNT; i++) - KeyMap[i] = -1; -#endif - KeyRepeatDelay = 0.275f; - KeyRepeatRate = 0.050f; - UserData = NULL; - - Fonts = NULL; - FontGlobalScale = 1.0f; - FontDefault = NULL; - FontAllowUserScaling = false; - DisplayFramebufferScale = ImVec2(1.0f, 1.0f); - - // Miscellaneous options - MouseDrawCursor = false; -#ifdef __APPLE__ - ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag -#else - ConfigMacOSXBehaviors = false; -#endif - ConfigInputTrickleEventQueue = true; - ConfigInputTextCursorBlink = true; - ConfigWindowsResizeFromEdges = true; - ConfigWindowsMoveFromTitleBarOnly = false; - ConfigMemoryCompactTimer = 60.0f; - - // Platform Functions - BackendPlatformName = BackendRendererName = NULL; - BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; - GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations - SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; - ClipboardUserData = NULL; - SetPlatformImeDataFn = SetPlatformImeDataFn_DefaultImpl; - - // Input (NB: we already have memset zero the entire structure!) - MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); - MouseDragThreshold = 6.0f; - for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; - for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } - for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; - AppAcceptingEvents = true; - BackendUsingLegacyKeyArrays = (ImS8)-1; - BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong -} - -// Pass in translated ASCII characters for text input. -// - with glfw you can get those from the callback set in glfwSetCharCallback() -// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message -// FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API -void ImGuiIO::AddInputCharacter(unsigned int c) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(&g.IO == this && "Can only add events to current context."); - if (c == 0 || !AppAcceptingEvents) - return; - - ImGuiInputEvent e; - e.Type = ImGuiInputEventType_Text; - e.Source = ImGuiInputSource_Keyboard; - e.Text.Char = c; - g.InputEventsQueue.push_back(e); -} - -// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so -// we should save the high surrogate. -void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) -{ - if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents) - return; - - if ((c & 0xFC00) == 0xD800) // High surrogate, must save - { - if (InputQueueSurrogate != 0) - AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID); - InputQueueSurrogate = c; - return; - } - - ImWchar cp = c; - if (InputQueueSurrogate != 0) - { - if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate - { - AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID); - } - else - { -#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF - cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar -#else - cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000); -#endif - } - - InputQueueSurrogate = 0; - } - AddInputCharacter((unsigned)cp); -} - -void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) -{ - if (!AppAcceptingEvents) - return; - while (*utf8_chars != 0) - { - unsigned int c = 0; - utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL); - if (c != 0) - AddInputCharacter(c); - } -} - -void ImGuiIO::ClearInputCharacters() -{ - InputQueueCharacters.resize(0); -} - -void ImGuiIO::ClearInputKeys() -{ -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - memset(KeysDown, 0, sizeof(KeysDown)); -#endif - for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++) - { - KeysData[n].Down = false; - KeysData[n].DownDuration = -1.0f; - KeysData[n].DownDurationPrev = -1.0f; - } - KeyCtrl = KeyShift = KeyAlt = KeySuper = false; - KeyMods = ImGuiModFlags_None; - for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++) - NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; -} - -// Queue a new key down/up event. -// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) -// - bool down: Is the key down? use false to signify a key release. -// - float analog_value: 0.0f..1.0f -void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) -{ - //if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } - if (key == ImGuiKey_None || !AppAcceptingEvents) - return; - ImGuiContext& g = *GImGui; - IM_ASSERT(&g.IO == this && "Can only add events to current context."); - IM_ASSERT(ImGui::IsNamedKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API. - - // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data. -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT((BackendUsingLegacyKeyArrays == -1 || BackendUsingLegacyKeyArrays == 0) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); - if (BackendUsingLegacyKeyArrays == -1) - for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) - IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); - BackendUsingLegacyKeyArrays = 0; -#endif - if (ImGui::IsGamepadKey(key)) - BackendUsingLegacyNavInputArray = false; - - // Partial filter of duplicates (not strictly needed, but makes data neater in particular for key mods and gamepad values which are most commonly spmamed) - ImGuiKeyData* key_data = ImGui::GetKeyData(key); - if (key_data->Down == down && key_data->AnalogValue == analog_value) - { - bool found = false; - for (int n = g.InputEventsQueue.Size - 1; n >= 0 && !found; n--) - if (g.InputEventsQueue[n].Type == ImGuiInputEventType_Key && g.InputEventsQueue[n].Key.Key == key) - found = true; - if (!found) - return; - } - - // Add event - ImGuiInputEvent e; - e.Type = ImGuiInputEventType_Key; - e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard; - e.Key.Key = key; - e.Key.Down = down; - e.Key.AnalogValue = analog_value; - g.InputEventsQueue.push_back(e); -} - -void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) -{ - if (!AppAcceptingEvents) - return; - AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f); -} - -// [Optional] Call after AddKeyEvent(). -// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices. -// If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this. -void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index) -{ - if (key == ImGuiKey_None) - return; - IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512 - IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey(native_legacy_index)); // >= 0 && <= 511 - IM_UNUSED(native_keycode); // Yet unused - IM_UNUSED(native_scancode); // Yet unused - - // Build native->imgui map so old user code can still call key functions with native 0..511 values. -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode; - if (!ImGui::IsLegacyKey(legacy_key)) - return; - KeyMap[legacy_key] = key; - KeyMap[key] = legacy_key; -#else - IM_UNUSED(key); - IM_UNUSED(native_legacy_index); -#endif -} - -// Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen. -void ImGuiIO::SetAppAcceptingEvents(bool accepting_events) -{ - AppAcceptingEvents = accepting_events; -} - -// Queue a mouse move event -void ImGuiIO::AddMousePosEvent(float x, float y) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(&g.IO == this && "Can only add events to current context."); - if (!AppAcceptingEvents) - return; - - ImGuiInputEvent e; - e.Type = ImGuiInputEventType_MousePos; - e.Source = ImGuiInputSource_Mouse; - e.MousePos.PosX = x; - e.MousePos.PosY = y; - g.InputEventsQueue.push_back(e); -} - -void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(&g.IO == this && "Can only add events to current context."); - IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); - if (!AppAcceptingEvents) - return; - - ImGuiInputEvent e; - e.Type = ImGuiInputEventType_MouseButton; - e.Source = ImGuiInputSource_Mouse; - e.MouseButton.Button = mouse_button; - e.MouseButton.Down = down; - g.InputEventsQueue.push_back(e); -} - -// Queue a mouse wheel event (most mouse/API will only have a Y component) -void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(&g.IO == this && "Can only add events to current context."); - if ((wheel_x == 0.0f && wheel_y == 0.0f) || !AppAcceptingEvents) - return; - - ImGuiInputEvent e; - e.Type = ImGuiInputEventType_MouseWheel; - e.Source = ImGuiInputSource_Mouse; - e.MouseWheel.WheelX = wheel_x; - e.MouseWheel.WheelY = wheel_y; - g.InputEventsQueue.push_back(e); -} - -void ImGuiIO::AddFocusEvent(bool focused) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(&g.IO == this && "Can only add events to current context."); - - ImGuiInputEvent e; - e.Type = ImGuiInputEventType_Focus; - e.AppFocused.Focused = focused; - g.InputEventsQueue.push_back(e); -} - -//----------------------------------------------------------------------------- -// [SECTION] MISC HELPERS/UTILITIES (Geometry functions) -//----------------------------------------------------------------------------- - -ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments) -{ - IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau() - ImVec2 p_last = p1; - ImVec2 p_closest; - float p_closest_dist2 = FLT_MAX; - float t_step = 1.0f / (float)num_segments; - for (int i_step = 1; i_step <= num_segments; i_step++) - { - ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step); - ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); - float dist2 = ImLengthSqr(p - p_line); - if (dist2 < p_closest_dist2) - { - p_closest = p_line; - p_closest_dist2 = dist2; - } - p_last = p_current; - } - return p_closest; -} - -// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp -static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) -{ - float dx = x4 - x1; - float dy = y4 - y1; - float d2 = ((x2 - x4) * dy - (y2 - y4) * dx); - float d3 = ((x3 - x4) * dy - (y3 - y4) * dx); - d2 = (d2 >= 0) ? d2 : -d2; - d3 = (d3 >= 0) ? d3 : -d3; - if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy)) - { - ImVec2 p_current(x4, y4); - ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); - float dist2 = ImLengthSqr(p - p_line); - if (dist2 < p_closest_dist2) - { - p_closest = p_line; - p_closest_dist2 = dist2; - } - p_last = p_current; - } - else if (level < 10) - { - float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f; - float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f; - float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f; - float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f; - float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f; - float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f; - ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); - ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); - } -} - -// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol -// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically. -ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol) -{ - IM_ASSERT(tess_tol > 0.0f); - ImVec2 p_last = p1; - ImVec2 p_closest; - float p_closest_dist2 = FLT_MAX; - ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0); - return p_closest; -} - -ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) -{ - ImVec2 ap = p - a; - ImVec2 ab_dir = b - a; - float dot = ap.x * ab_dir.x + ap.y * ab_dir.y; - if (dot < 0.0f) - return a; - float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y; - if (dot > ab_len_sqr) - return b; - return a + ab_dir * dot / ab_len_sqr; -} - -bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) -{ - bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; - bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; - bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f; - return ((b1 == b2) && (b2 == b3)); -} - -void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w) -{ - ImVec2 v0 = b - a; - ImVec2 v1 = c - a; - ImVec2 v2 = p - a; - const float denom = v0.x * v1.y - v1.x * v0.y; - out_v = (v2.x * v1.y - v1.x * v2.y) / denom; - out_w = (v0.x * v2.y - v2.x * v0.y) / denom; - out_u = 1.0f - out_v - out_w; -} - -ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) -{ - ImVec2 proj_ab = ImLineClosestPoint(a, b, p); - ImVec2 proj_bc = ImLineClosestPoint(b, c, p); - ImVec2 proj_ca = ImLineClosestPoint(c, a, p); - float dist2_ab = ImLengthSqr(p - proj_ab); - float dist2_bc = ImLengthSqr(p - proj_bc); - float dist2_ca = ImLengthSqr(p - proj_ca); - float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca)); - if (m == dist2_ab) - return proj_ab; - if (m == dist2_bc) - return proj_bc; - return proj_ca; -} - -//----------------------------------------------------------------------------- -// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) -//----------------------------------------------------------------------------- - -// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more. -int ImStricmp(const char* str1, const char* str2) -{ - int d; - while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } - return d; -} - -int ImStrnicmp(const char* str1, const char* str2, size_t count) -{ - int d = 0; - while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; } - return d; -} - -void ImStrncpy(char* dst, const char* src, size_t count) -{ - if (count < 1) - return; - if (count > 1) - strncpy(dst, src, count - 1); - dst[count - 1] = 0; -} - -char* ImStrdup(const char* str) -{ - size_t len = strlen(str); - void* buf = IM_ALLOC(len + 1); - return (char*)memcpy(buf, (const void*)str, len + 1); -} - -char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src) -{ - size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1; - size_t src_size = strlen(src) + 1; - if (dst_buf_size < src_size) - { - IM_FREE(dst); - dst = (char*)IM_ALLOC(src_size); - if (p_dst_size) - *p_dst_size = src_size; - } - return (char*)memcpy(dst, (const void*)src, src_size); -} - -const char* ImStrchrRange(const char* str, const char* str_end, char c) -{ - const char* p = (const char*)memchr(str, (int)c, str_end - str); - return p; -} - -int ImStrlenW(const ImWchar* str) -{ - //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit - int n = 0; - while (*str++) n++; - return n; -} - -// Find end-of-line. Return pointer will point to either first \n, either str_end. -const char* ImStreolRange(const char* str, const char* str_end) -{ - const char* p = (const char*)memchr(str, '\n', str_end - str); - return p ? p : str_end; -} - -const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line -{ - while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n') - buf_mid_line--; - return buf_mid_line; -} - -const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end) -{ - if (!needle_end) - needle_end = needle + strlen(needle); - - const char un0 = (char)toupper(*needle); - while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end)) - { - if (toupper(*haystack) == un0) - { - const char* b = needle + 1; - for (const char* a = haystack + 1; b < needle_end; a++, b++) - if (toupper(*a) != toupper(*b)) - break; - if (b == needle_end) - return haystack; - } - haystack++; - } - return NULL; -} - -// Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible. -void ImStrTrimBlanks(char* buf) -{ - char* p = buf; - while (p[0] == ' ' || p[0] == '\t') // Leading blanks - p++; - char* p_start = p; - while (*p != 0) // Find end of string - p++; - while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks - p--; - if (p_start != buf) // Copy memory if we had leading blanks - memmove(buf, p_start, p - p_start); - buf[p - p_start] = 0; // Zero terminate -} - -const char* ImStrSkipBlank(const char* str) -{ - while (str[0] == ' ' || str[0] == '\t') - str++; - return str; -} - -// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). -// Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm. -// B) When buf==NULL vsnprintf() will return the output size. -#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS - -// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h) -// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS -// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are -// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.) -#ifdef IMGUI_USE_STB_SPRINTF -#define STB_SPRINTF_IMPLEMENTATION -#ifdef IMGUI_STB_SPRINTF_FILENAME -#include IMGUI_STB_SPRINTF_FILENAME -#else -#include "stb_sprintf.h" -#endif -#endif - -#if defined(_MSC_VER) && !defined(vsnprintf) -#define vsnprintf _vsnprintf -#endif - -int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); -#ifdef IMGUI_USE_STB_SPRINTF - int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); -#else - int w = vsnprintf(buf, buf_size, fmt, args); -#endif - va_end(args); - if (buf == NULL) - return w; - if (w == -1 || w >= (int)buf_size) - w = (int)buf_size - 1; - buf[w] = 0; - return w; -} - -int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) -{ -#ifdef IMGUI_USE_STB_SPRINTF - int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); -#else - int w = vsnprintf(buf, buf_size, fmt, args); -#endif - if (buf == NULL) - return w; - if (w == -1 || w >= (int)buf_size) - w = (int)buf_size - 1; - buf[w] = 0; - return w; -} -#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS - -void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) -{ - ImGuiContext& g = *GImGui; - va_list args; - va_start(args, fmt); - int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args); - *out_buf = g.TempBuffer.Data; - if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; } - va_end(args); -} - -void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) -{ - ImGuiContext& g = *GImGui; - int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args); - *out_buf = g.TempBuffer.Data; - if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; } -} - -// CRC32 needs a 1KB lookup table (not cache friendly) -// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily: -// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe. -static const ImU32 GCrc32LookupTable[256] = -{ - 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, - 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, - 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, - 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, - 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, - 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, - 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, - 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, - 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, - 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, - 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, - 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, - 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, - 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, - 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, - 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D, -}; - -// Known size hash -// It is ok to call ImHashData on a string with known length but the ### operator won't be supported. -// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. -ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed) -{ - ImU32 crc = ~seed; - const unsigned char* data = (const unsigned char*)data_p; - const ImU32* crc32_lut = GCrc32LookupTable; - while (data_size-- != 0) - crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++]; - return ~crc; -} - -// Zero-terminated string hash, with support for ### to reset back to seed value -// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. -// Because this syntax is rarely used we are optimizing for the common case. -// - If we reach ### in the string we discard the hash so far and reset to the seed. -// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build) -// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. -ImGuiID ImHashStr(const char* data_p, size_t data_size, ImU32 seed) -{ - seed = ~seed; - ImU32 crc = seed; - const unsigned char* data = (const unsigned char*)data_p; - const ImU32* crc32_lut = GCrc32LookupTable; - if (data_size != 0) - { - while (data_size-- != 0) - { - unsigned char c = *data++; - if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') - crc = seed; - crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; - } - } - else - { - while (unsigned char c = *data++) - { - if (c == '#' && data[0] == '#' && data[1] == '#') - crc = seed; - crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; - } - } - return ~crc; -} - -//----------------------------------------------------------------------------- -// [SECTION] MISC HELPERS/UTILITIES (File functions) -//----------------------------------------------------------------------------- - -// Default file functions -#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS - -ImFileHandle ImFileOpen(const char* filename, const char* mode) -{ -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__) - // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. - // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32! - const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); - const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0); - ImVector buf; - buf.resize(filename_wsize + mode_wsize); - ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize); - ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize); - return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]); -#else - return fopen(filename, mode); -#endif -} - -// We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way. -bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; } -ImU64 ImFileGetSize(ImFileHandle f) { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; } -ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); } -ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); } -#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS - -// Helper: Load file content into memory -// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree() -// This can't really be used with "rt" because fseek size won't match read size. -void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes) -{ - IM_ASSERT(filename && mode); - if (out_file_size) - *out_file_size = 0; - - ImFileHandle f; - if ((f = ImFileOpen(filename, mode)) == NULL) - return NULL; - - size_t file_size = (size_t)ImFileGetSize(f); - if (file_size == (size_t)-1) - { - ImFileClose(f); - return NULL; - } - - void* file_data = IM_ALLOC(file_size + padding_bytes); - if (file_data == NULL) - { - ImFileClose(f); - return NULL; - } - if (ImFileRead(file_data, 1, file_size, f) != file_size) - { - ImFileClose(f); - IM_FREE(file_data); - return NULL; - } - if (padding_bytes > 0) - memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); - - ImFileClose(f); - if (out_file_size) - *out_file_size = file_size; - - return file_data; -} - -//----------------------------------------------------------------------------- -// [SECTION] MISC HELPERS/UTILITIES (ImText* functions) -//----------------------------------------------------------------------------- - -// Convert UTF-8 to 32-bit character, process single character input. -// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8). -// We handle UTF-8 decoding error by skipping forward. -int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) -{ - static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 }; - static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 }; - static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 }; - static const int shiftc[] = { 0, 18, 12, 6, 0 }; - static const int shifte[] = { 0, 6, 4, 2, 0 }; - int len = lengths[*(const unsigned char*)in_text >> 3]; - int wanted = len + !len; - - if (in_text_end == NULL) - in_text_end = in_text + wanted; // Max length, nulls will be taken into account. - - // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here, - // so it is fast even with excessive branching. - unsigned char s[4]; - s[0] = in_text + 0 < in_text_end ? in_text[0] : 0; - s[1] = in_text + 1 < in_text_end ? in_text[1] : 0; - s[2] = in_text + 2 < in_text_end ? in_text[2] : 0; - s[3] = in_text + 3 < in_text_end ? in_text[3] : 0; - - // Assume a four-byte character and load four bytes. Unused bits are shifted out. - *out_char = (uint32_t)(s[0] & masks[len]) << 18; - *out_char |= (uint32_t)(s[1] & 0x3f) << 12; - *out_char |= (uint32_t)(s[2] & 0x3f) << 6; - *out_char |= (uint32_t)(s[3] & 0x3f) << 0; - *out_char >>= shiftc[len]; - - // Accumulate the various error conditions. - int e = 0; - e = (*out_char < mins[len]) << 6; // non-canonical encoding - e |= ((*out_char >> 11) == 0x1b) << 7; // surrogate half? - e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8; // out of range? - e |= (s[1] & 0xc0) >> 2; - e |= (s[2] & 0xc0) >> 4; - e |= (s[3] ) >> 6; - e ^= 0x2a; // top two bits of each tail byte correct? - e >>= shifte[len]; - - if (e) - { - // No bytes are consumed when *in_text == 0 || in_text == in_text_end. - // One byte is consumed in case of invalid first byte of in_text. - // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes. - // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s. - wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]); - *out_char = IM_UNICODE_CODEPOINT_INVALID; - } - - return wanted; -} - -int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) -{ - ImWchar* buf_out = buf; - ImWchar* buf_end = buf + buf_size; - while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) - { - unsigned int c; - in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); - if (c == 0) - break; - *buf_out++ = (ImWchar)c; - } - *buf_out = 0; - if (in_text_remaining) - *in_text_remaining = in_text; - return (int)(buf_out - buf); -} - -int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) -{ - int char_count = 0; - while ((!in_text_end || in_text < in_text_end) && *in_text) - { - unsigned int c; - in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); - if (c == 0) - break; - char_count++; - } - return char_count; -} - -// Based on stb_to_utf8() from github.com/nothings/stb/ -static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c) -{ - if (c < 0x80) - { - buf[0] = (char)c; - return 1; - } - if (c < 0x800) - { - if (buf_size < 2) return 0; - buf[0] = (char)(0xc0 + (c >> 6)); - buf[1] = (char)(0x80 + (c & 0x3f)); - return 2; - } - if (c < 0x10000) - { - if (buf_size < 3) return 0; - buf[0] = (char)(0xe0 + (c >> 12)); - buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); - buf[2] = (char)(0x80 + ((c ) & 0x3f)); - return 3; - } - if (c <= 0x10FFFF) - { - if (buf_size < 4) return 0; - buf[0] = (char)(0xf0 + (c >> 18)); - buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); - buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); - buf[3] = (char)(0x80 + ((c ) & 0x3f)); - return 4; - } - // Invalid code point, the max unicode is 0x10FFFF - return 0; -} - -const char* ImTextCharToUtf8(char out_buf[5], unsigned int c) -{ - int count = ImTextCharToUtf8_inline(out_buf, 5, c); - out_buf[count] = 0; - return out_buf; -} - -// Not optimal but we very rarely use this function. -int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end) -{ - unsigned int unused = 0; - return ImTextCharFromUtf8(&unused, in_text, in_text_end); -} - -static inline int ImTextCountUtf8BytesFromChar(unsigned int c) -{ - if (c < 0x80) return 1; - if (c < 0x800) return 2; - if (c < 0x10000) return 3; - if (c <= 0x10FFFF) return 4; - return 3; -} - -int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end) -{ - char* buf_p = out_buf; - const char* buf_end = out_buf + out_buf_size; - while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) - { - unsigned int c = (unsigned int)(*in_text++); - if (c < 0x80) - *buf_p++ = (char)c; - else - buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c); - } - *buf_p = 0; - return (int)(buf_p - out_buf); -} - -int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) -{ - int bytes_count = 0; - while ((!in_text_end || in_text < in_text_end) && *in_text) - { - unsigned int c = (unsigned int)(*in_text++); - if (c < 0x80) - bytes_count++; - else - bytes_count += ImTextCountUtf8BytesFromChar(c); - } - return bytes_count; -} - -//----------------------------------------------------------------------------- -// [SECTION] MISC HELPERS/UTILITIES (Color functions) -// Note: The Convert functions are early design which are not consistent with other API. -//----------------------------------------------------------------------------- - -IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b) -{ - float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; - int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); - int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); - int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); - return IM_COL32(r, g, b, 0xFF); -} - -ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) -{ - float s = 1.0f / 255.0f; - return ImVec4( - ((in >> IM_COL32_R_SHIFT) & 0xFF) * s, - ((in >> IM_COL32_G_SHIFT) & 0xFF) * s, - ((in >> IM_COL32_B_SHIFT) & 0xFF) * s, - ((in >> IM_COL32_A_SHIFT) & 0xFF) * s); -} - -ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) -{ - ImU32 out; - out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT; - out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT; - out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT; - out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT; - return out; -} - -// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 -// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv -void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) -{ - float K = 0.f; - if (g < b) - { - ImSwap(g, b); - K = -1.f; - } - if (r < g) - { - ImSwap(r, g); - K = -2.f / 6.f - K; - } - - const float chroma = r - (g < b ? g : b); - out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f)); - out_s = chroma / (r + 1e-20f); - out_v = r; -} - -// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593 -// also http://en.wikipedia.org/wiki/HSL_and_HSV -void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b) -{ - if (s == 0.0f) - { - // gray - out_r = out_g = out_b = v; - return; - } - - h = ImFmod(h, 1.0f) / (60.0f / 360.0f); - int i = (int)h; - float f = h - (float)i; - float p = v * (1.0f - s); - float q = v * (1.0f - s * f); - float t = v * (1.0f - s * (1.0f - f)); - - switch (i) - { - case 0: out_r = v; out_g = t; out_b = p; break; - case 1: out_r = q; out_g = v; out_b = p; break; - case 2: out_r = p; out_g = v; out_b = t; break; - case 3: out_r = p; out_g = q; out_b = v; break; - case 4: out_r = t; out_g = p; out_b = v; break; - case 5: default: out_r = v; out_g = p; out_b = q; break; - } -} - -//----------------------------------------------------------------------------- -// [SECTION] ImGuiStorage -// Helper: Key->value storage -//----------------------------------------------------------------------------- - -// std::lower_bound but without the bullshit -static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector& data, ImGuiID key) -{ - ImGuiStorage::ImGuiStoragePair* first = data.Data; - ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size; - size_t count = (size_t)(last - first); - while (count > 0) - { - size_t count2 = count >> 1; - ImGuiStorage::ImGuiStoragePair* mid = first + count2; - if (mid->key < key) - { - first = ++mid; - count -= count2 + 1; - } - else - { - count = count2; - } - } - return first; -} - -// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. -void ImGuiStorage::BuildSortByKey() -{ - struct StaticFunc - { - static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs) - { - // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. - if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1; - if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1; - return 0; - } - }; - ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairComparerByID); -} - -int ImGuiStorage::GetInt(ImGuiID key, int default_val) const -{ - ImGuiStoragePair* it = LowerBound(const_cast&>(Data), key); - if (it == Data.end() || it->key != key) - return default_val; - return it->val_i; -} - -bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const -{ - return GetInt(key, default_val ? 1 : 0) != 0; -} - -float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const -{ - ImGuiStoragePair* it = LowerBound(const_cast&>(Data), key); - if (it == Data.end() || it->key != key) - return default_val; - return it->val_f; -} - -void* ImGuiStorage::GetVoidPtr(ImGuiID key) const -{ - ImGuiStoragePair* it = LowerBound(const_cast&>(Data), key); - if (it == Data.end() || it->key != key) - return NULL; - return it->val_p; -} - -// References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. -int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) -{ - ImGuiStoragePair* it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - it = Data.insert(it, ImGuiStoragePair(key, default_val)); - return &it->val_i; -} - -bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) -{ - return (bool*)GetIntRef(key, default_val ? 1 : 0); -} - -float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) -{ - ImGuiStoragePair* it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - it = Data.insert(it, ImGuiStoragePair(key, default_val)); - return &it->val_f; -} - -void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) -{ - ImGuiStoragePair* it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - it = Data.insert(it, ImGuiStoragePair(key, default_val)); - return &it->val_p; -} - -// FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame) -void ImGuiStorage::SetInt(ImGuiID key, int val) -{ - ImGuiStoragePair* it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - { - Data.insert(it, ImGuiStoragePair(key, val)); - return; - } - it->val_i = val; -} - -void ImGuiStorage::SetBool(ImGuiID key, bool val) -{ - SetInt(key, val ? 1 : 0); -} - -void ImGuiStorage::SetFloat(ImGuiID key, float val) -{ - ImGuiStoragePair* it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - { - Data.insert(it, ImGuiStoragePair(key, val)); - return; - } - it->val_f = val; -} - -void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) -{ - ImGuiStoragePair* it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - { - Data.insert(it, ImGuiStoragePair(key, val)); - return; - } - it->val_p = val; -} - -void ImGuiStorage::SetAllInt(int v) -{ - for (int i = 0; i < Data.Size; i++) - Data[i].val_i = v; -} - -//----------------------------------------------------------------------------- -// [SECTION] ImGuiTextFilter -//----------------------------------------------------------------------------- - -// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" -ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 -{ - InputBuf[0] = 0; - CountGrep = 0; - if (default_filter) - { - ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf)); - Build(); - } -} - -bool ImGuiTextFilter::Draw(const char* label, float width) -{ - if (width != 0.0f) - ImGui::SetNextItemWidth(width); - bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); - if (value_changed) - Build(); - return value_changed; -} - -void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector* out) const -{ - out->resize(0); - const char* wb = b; - const char* we = wb; - while (we < e) - { - if (*we == separator) - { - out->push_back(ImGuiTextRange(wb, we)); - wb = we + 1; - } - we++; - } - if (wb != we) - out->push_back(ImGuiTextRange(wb, we)); -} - -void ImGuiTextFilter::Build() -{ - Filters.resize(0); - ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf)); - input_range.split(',', &Filters); - - CountGrep = 0; - for (int i = 0; i != Filters.Size; i++) - { - ImGuiTextRange& f = Filters[i]; - while (f.b < f.e && ImCharIsBlankA(f.b[0])) - f.b++; - while (f.e > f.b && ImCharIsBlankA(f.e[-1])) - f.e--; - if (f.empty()) - continue; - if (Filters[i].b[0] != '-') - CountGrep += 1; - } -} - -bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const -{ - if (Filters.empty()) - return true; - - if (text == NULL) - text = ""; - - for (int i = 0; i != Filters.Size; i++) - { - const ImGuiTextRange& f = Filters[i]; - if (f.empty()) - continue; - if (f.b[0] == '-') - { - // Subtract - if (ImStristr(text, text_end, f.b + 1, f.e) != NULL) - return false; - } - else - { - // Grep - if (ImStristr(text, text_end, f.b, f.e) != NULL) - return true; - } - } - - // Implicit * grep - if (CountGrep == 0) - return true; - - return false; -} - -//----------------------------------------------------------------------------- -// [SECTION] ImGuiTextBuffer -//----------------------------------------------------------------------------- - -// On some platform vsnprintf() takes va_list by reference and modifies it. -// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. -#ifndef va_copy -#if defined(__GNUC__) || defined(__clang__) -#define va_copy(dest, src) __builtin_va_copy(dest, src) -#else -#define va_copy(dest, src) (dest = src) -#endif -#endif - -char ImGuiTextBuffer::EmptyString[1] = { 0 }; - -void ImGuiTextBuffer::append(const char* str, const char* str_end) -{ - int len = str_end ? (int)(str_end - str) : (int)strlen(str); - - // Add zero-terminator the first time - const int write_off = (Buf.Size != 0) ? Buf.Size : 1; - const int needed_sz = write_off + len; - if (write_off + len >= Buf.Capacity) - { - int new_capacity = Buf.Capacity * 2; - Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); - } - - Buf.resize(needed_sz); - memcpy(&Buf[write_off - 1], str, (size_t)len); - Buf[write_off - 1 + len] = 0; -} - -void ImGuiTextBuffer::appendf(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - appendfv(fmt, args); - va_end(args); -} - -// Helper: Text buffer for logging/accumulating text -void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) -{ - va_list args_copy; - va_copy(args_copy, args); - - int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. - if (len <= 0) - { - va_end(args_copy); - return; - } - - // Add zero-terminator the first time - const int write_off = (Buf.Size != 0) ? Buf.Size : 1; - const int needed_sz = write_off + len; - if (write_off + len >= Buf.Capacity) - { - int new_capacity = Buf.Capacity * 2; - Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); - } - - Buf.resize(needed_sz); - ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy); - va_end(args_copy); -} - -//----------------------------------------------------------------------------- -// [SECTION] ImGuiListClipper -// This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed -// the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO) -//----------------------------------------------------------------------------- - -// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell. -// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous. -static bool GetSkipItemForListClipping() -{ - ImGuiContext& g = *GImGui; - return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems); -} - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -// Legacy helper to calculate coarse clipping of large list of evenly sized items. -// This legacy API is not ideal because it assume we will return a single contiguous rectangle. -// Prefer using ImGuiListClipper which can returns non-contiguous ranges. -void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.LogEnabled) - { - // If logging is active, do not perform any clipping - *out_items_display_start = 0; - *out_items_display_end = items_count; - return; - } - if (GetSkipItemForListClipping()) - { - *out_items_display_start = *out_items_display_end = 0; - return; - } - - // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect - // We don't include g.NavId's rectangle in there (unless g.NavJustMovedToId is set) because the rectangle enlargement can get costly. - ImRect rect = window->ClipRect; - if (g.NavMoveScoringItems) - rect.Add(g.NavScoringNoClipRect); - if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) - rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel - - const ImVec2 pos = window->DC.CursorPos; - int start = (int)((rect.Min.y - pos.y) / items_height); - int end = (int)((rect.Max.y - pos.y) / items_height); - - // When performing a navigation request, ensure we have one item extra in the direction we are moving to - // FIXME: Verify this works with tabbing - const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); - if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) - start--; - if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) - end++; - - start = ImClamp(start, 0, items_count); - end = ImClamp(end + 1, start, items_count); - *out_items_display_start = start; - *out_items_display_end = end; -} -#endif - -static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0) -{ - if (ranges.Size - offset <= 1) - return; - - // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries) - for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end) - for (int i = offset; i < sort_end + offset; ++i) - if (ranges[i].Min > ranges[i + 1].Min) - ImSwap(ranges[i], ranges[i + 1]); - - // Now fuse ranges together as much as possible. - for (int i = 1 + offset; i < ranges.Size; i++) - { - IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert); - if (ranges[i - 1].Max < ranges[i].Min) - continue; - ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min); - ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max); - ranges.erase(ranges.Data + i); - i--; - } -} - -static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height) -{ - // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. - // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. - // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek? - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - float off_y = pos_y - window->DC.CursorPos.y; - window->DC.CursorPos.y = pos_y; - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y); - window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage. - window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. - if (ImGuiOldColumns* columns = window->DC.CurrentColumns) - columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly - if (ImGuiTable* table = g.CurrentTable) - { - if (table->IsInsideRow) - ImGui::TableEndRow(table); - table->RowPosY2 = window->DC.CursorPos.y; - const int row_increase = (int)((off_y / line_height) + 0.5f); - //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow() - table->RowBgColorCounter += row_increase; - } -} - -static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n) -{ - // StartPosY starts from ItemsFrozen hence the subtraction - // Perform the add and multiply with double to allow seeking through larger ranges - ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; - float pos_y = (float)((double)clipper->StartPosY + data->LossynessOffset + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight); - ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight); -} - -ImGuiListClipper::ImGuiListClipper() -{ - memset(this, 0, sizeof(*this)); - ItemsCount = -1; -} - -ImGuiListClipper::~ImGuiListClipper() -{ - End(); -} - -// Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1 -// Use case B: Begin() called from constructor with items_height>0 -// FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style. -void ImGuiListClipper::Begin(int items_count, float items_height) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (ImGuiTable* table = g.CurrentTable) - if (table->IsInsideRow) - ImGui::TableEndRow(table); - - StartPosY = window->DC.CursorPos.y; - ItemsHeight = items_height; - ItemsCount = items_count; - DisplayStart = -1; - DisplayEnd = 0; - - // Acquire temporary buffer - if (++g.ClipperTempDataStacked > g.ClipperTempData.Size) - g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData()); - ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; - data->Reset(this); - data->LossynessOffset = window->DC.CursorStartPosLossyness.y; - TempData = data; -} - -void ImGuiListClipper::End() -{ - ImGuiContext& g = *GImGui; - if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData) - { - // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user. - if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) - ImGuiListClipper_SeekCursorForItem(this, ItemsCount); - - // Restore temporary buffer and fix back pointers which may be invalidated when nesting - IM_ASSERT(data->ListClipper == this); - data->StepNo = data->Ranges.Size; - if (--g.ClipperTempDataStacked > 0) - { - data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; - data->ListClipper->TempData = data; - } - TempData = NULL; - } - ItemsCount = -1; -} - -void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max) -{ - ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; - IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. - IM_ASSERT(item_min <= item_max); - if (item_min < item_max) - data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_min, item_max)); -} - -bool ImGuiListClipper::Step() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; - IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?"); - - ImGuiTable* table = g.CurrentTable; - if (table && table->IsInsideRow) - ImGui::TableEndRow(table); - - // No items - if (ItemsCount == 0 || GetSkipItemForListClipping()) - return (void)End(), false; - - // While we are in frozen row state, keep displaying items one by one, unclipped - // FIXME: Could be stored as a table-agnostic state. - if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows) - { - DisplayStart = data->ItemsFrozen; - DisplayEnd = data->ItemsFrozen + 1; - if (DisplayStart >= ItemsCount) - return (void)End(), false; - data->ItemsFrozen++; - return true; - } - - // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) - bool calc_clipping = false; - if (data->StepNo == 0) - { - StartPosY = window->DC.CursorPos.y; - if (ItemsHeight <= 0.0f) - { - // Submit the first item (or range) so we can measure its height (generally the first range is 0..1) - data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1)); - DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen); - DisplayEnd = ImMin(data->Ranges[0].Max, ItemsCount); - if (DisplayStart == DisplayEnd) - return (void)End(), false; - data->StepNo = 1; - return true; - } - calc_clipping = true; // If on the first step with known item height, calculate clipping. - } - - // Step 1: Let the clipper infer height from first range - if (ItemsHeight <= 0.0f) - { - IM_ASSERT(data->StepNo == 1); - if (table) - IM_ASSERT(table->RowPosY1 == StartPosY && table->RowPosY2 == window->DC.CursorPos.y); - - ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); - bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); - if (affected_by_floating_point_precision) - ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. - - IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); - calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. - } - - // Step 0 or 1: Calculate the actual ranges of visible elements. - const int already_submitted = DisplayEnd; - if (calc_clipping) - { - if (g.LogEnabled) - { - // If logging is active, do not perform any clipping - data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, ItemsCount)); - } - else - { - // Add range selected to be included for navigation - const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); - if (is_nav_request) - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); - if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && g.NavTabbingDir == -1) - data->Ranges.push_back(ImGuiListClipperRange::FromIndices(ItemsCount - 1, ItemsCount)); - - // Add focused/active item - ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]); - if (g.NavId != 0 && window->NavLastIds[0] == g.NavId) - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0)); - - // Add visible range - const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; - const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(window->ClipRect.Min.y, window->ClipRect.Max.y, off_min, off_max)); - } - - // Convert position ranges to item index ranges - // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping. - // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list, - // which with the flooring/ceiling tend to lead to 2 items instead of one being submitted. - for (int i = 0; i < data->Ranges.Size; i++) - if (data->Ranges[i].PosToIndexConvert) - { - int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight); - int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight) + 0.999999f); - data->Ranges[i].Min = ImClamp(already_submitted + m1 + data->Ranges[i].PosToIndexOffsetMin, already_submitted, ItemsCount - 1); - data->Ranges[i].Max = ImClamp(already_submitted + m2 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, ItemsCount); - data->Ranges[i].PosToIndexConvert = false; - } - ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo); - } - - // Step 0+ (if item height is given in advance) or 1+: Display the next range in line. - if (data->StepNo < data->Ranges.Size) - { - DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); - DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, ItemsCount); - if (DisplayStart > already_submitted) //-V1051 - ImGuiListClipper_SeekCursorForItem(this, DisplayStart); - data->StepNo++; - return true; - } - - // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), - // Advance the cursor to the end of the list and then returns 'false' to end the loop. - if (ItemsCount < INT_MAX) - ImGuiListClipper_SeekCursorForItem(this, ItemsCount); - - End(); - return false; -} - -//----------------------------------------------------------------------------- -// [SECTION] STYLING -//----------------------------------------------------------------------------- - -ImGuiStyle& ImGui::GetStyle() -{ - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); - return GImGui->Style; -} - -ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) -{ - ImGuiStyle& style = GImGui->Style; - ImVec4 c = style.Colors[idx]; - c.w *= style.Alpha * alpha_mul; - return ColorConvertFloat4ToU32(c); -} - -ImU32 ImGui::GetColorU32(const ImVec4& col) -{ - ImGuiStyle& style = GImGui->Style; - ImVec4 c = col; - c.w *= style.Alpha; - return ColorConvertFloat4ToU32(c); -} - -const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) -{ - ImGuiStyle& style = GImGui->Style; - return style.Colors[idx]; -} - -ImU32 ImGui::GetColorU32(ImU32 col) -{ - ImGuiStyle& style = GImGui->Style; - if (style.Alpha >= 1.0f) - return col; - ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; - a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. - return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); -} - -// FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32 -void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) -{ - ImGuiContext& g = *GImGui; - ImGuiColorMod backup; - backup.Col = idx; - backup.BackupValue = g.Style.Colors[idx]; - g.ColorStack.push_back(backup); - g.Style.Colors[idx] = ColorConvertU32ToFloat4(col); -} - -void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) -{ - ImGuiContext& g = *GImGui; - ImGuiColorMod backup; - backup.Col = idx; - backup.BackupValue = g.Style.Colors[idx]; - g.ColorStack.push_back(backup); - g.Style.Colors[idx] = col; -} - -void ImGui::PopStyleColor(int count) -{ - ImGuiContext& g = *GImGui; - while (count > 0) - { - ImGuiColorMod& backup = g.ColorStack.back(); - g.Style.Colors[backup.Col] = backup.BackupValue; - g.ColorStack.pop_back(); - count--; - } -} - -struct ImGuiStyleVarInfo -{ - ImGuiDataType Type; - ImU32 Count; - ImU32 Offset; - void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); } -}; - -static const ImGuiStyleVarInfo GStyleVarInfo[] = -{ - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign -}; - -static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) -{ - IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT); - IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT); - return &GStyleVarInfo[idx]; -} - -void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) -{ - const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) - { - ImGuiContext& g = *GImGui; - float* pvar = (float*)var_info->GetVarPtr(&g.Style); - g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); - *pvar = val; - return; - } - IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); -} - -void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) -{ - const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) - { - ImGuiContext& g = *GImGui; - ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); - g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); - *pvar = val; - return; - } - IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); -} - -void ImGui::PopStyleVar(int count) -{ - ImGuiContext& g = *GImGui; - while (count > 0) - { - // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it. - ImGuiStyleMod& backup = g.StyleVarStack.back(); - const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx); - void* data = info->GetVarPtr(&g.Style); - if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } - else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } - g.StyleVarStack.pop_back(); - count--; - } -} - -const char* ImGui::GetStyleColorName(ImGuiCol idx) -{ - // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1"; - switch (idx) - { - case ImGuiCol_Text: return "Text"; - case ImGuiCol_TextDisabled: return "TextDisabled"; - case ImGuiCol_WindowBg: return "WindowBg"; - case ImGuiCol_ChildBg: return "ChildBg"; - case ImGuiCol_PopupBg: return "PopupBg"; - case ImGuiCol_Border: return "Border"; - case ImGuiCol_BorderShadow: return "BorderShadow"; - case ImGuiCol_FrameBg: return "FrameBg"; - case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; - case ImGuiCol_FrameBgActive: return "FrameBgActive"; - case ImGuiCol_TitleBg: return "TitleBg"; - case ImGuiCol_TitleBgActive: return "TitleBgActive"; - case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; - case ImGuiCol_MenuBarBg: return "MenuBarBg"; - case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; - case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab"; - case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; - case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; - case ImGuiCol_CheckMark: return "CheckMark"; - case ImGuiCol_SliderGrab: return "SliderGrab"; - case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; - case ImGuiCol_Button: return "Button"; - case ImGuiCol_ButtonHovered: return "ButtonHovered"; - case ImGuiCol_ButtonActive: return "ButtonActive"; - case ImGuiCol_Header: return "Header"; - case ImGuiCol_HeaderHovered: return "HeaderHovered"; - case ImGuiCol_HeaderActive: return "HeaderActive"; - case ImGuiCol_Separator: return "Separator"; - case ImGuiCol_SeparatorHovered: return "SeparatorHovered"; - case ImGuiCol_SeparatorActive: return "SeparatorActive"; - case ImGuiCol_ResizeGrip: return "ResizeGrip"; - case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; - case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; - case ImGuiCol_Tab: return "Tab"; - case ImGuiCol_TabHovered: return "TabHovered"; - case ImGuiCol_TabActive: return "TabActive"; - case ImGuiCol_TabUnfocused: return "TabUnfocused"; - case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive"; - case ImGuiCol_PlotLines: return "PlotLines"; - case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; - case ImGuiCol_PlotHistogram: return "PlotHistogram"; - case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; - case ImGuiCol_TableHeaderBg: return "TableHeaderBg"; - case ImGuiCol_TableBorderStrong: return "TableBorderStrong"; - case ImGuiCol_TableBorderLight: return "TableBorderLight"; - case ImGuiCol_TableRowBg: return "TableRowBg"; - case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt"; - case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; - case ImGuiCol_DragDropTarget: return "DragDropTarget"; - case ImGuiCol_NavHighlight: return "NavHighlight"; - case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; - case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg"; - case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg"; - } - IM_ASSERT(0); - return "Unknown"; -} - - -//----------------------------------------------------------------------------- -// [SECTION] RENDER HELPERS -// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change, -// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context. -// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context. -//----------------------------------------------------------------------------- - -const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) -{ - const char* text_display_end = text; - if (!text_end) - text_end = (const char*)-1; - - while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) - text_display_end++; - return text_display_end; -} - -// Internal ImGui functions to render text -// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() -void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Hide anything after a '##' string - const char* text_display_end; - if (hide_text_after_hash) - { - text_display_end = FindRenderedTextEnd(text, text_end); - } - else - { - if (!text_end) - text_end = text + strlen(text); // FIXME-OPT - text_display_end = text_end; - } - - if (text != text_display_end) - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); - if (g.LogEnabled) - LogRenderedText(&pos, text, text_display_end); - } -} - -void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (!text_end) - text_end = text + strlen(text); // FIXME-OPT - - if (text != text_end) - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); - if (g.LogEnabled) - LogRenderedText(&pos, text, text_end); - } -} - -// Default clip_rect uses (pos_min,pos_max) -// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) -void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) -{ - // Perform CPU side clipping for single clipped element to avoid using scissor state - ImVec2 pos = pos_min; - const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); - - const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; - const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; - bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); - if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min - need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); - - // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. - if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); - if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); - - // Render - if (need_clipping) - { - ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); - draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); - } - else - { - draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); - } -} - -void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) -{ - // Hide anything after a '##' string - const char* text_display_end = FindRenderedTextEnd(text, text_end); - const int text_len = (int)(text_display_end - text); - if (text_len == 0) - return; - - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect); - if (g.LogEnabled) - LogRenderedText(&pos_min, text, text_display_end); -} - - -// Another overly complex function until we reorganize everything into a nice all-in-one helper. -// This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display. -// This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move. -void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known) -{ - ImGuiContext& g = *GImGui; - if (text_end_full == NULL) - text_end_full = FindRenderedTextEnd(text); - const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f); - - //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255)); - //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255)); - //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255)); - // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels. - if (text_size.x > pos_max.x - pos_min.x) - { - // Hello wo... - // | | | - // min max ellipsis_max - // <-> this is generally some padding value - - const ImFont* font = draw_list->_Data->Font; - const float font_size = draw_list->_Data->FontSize; - const char* text_end_ellipsis = NULL; - - ImWchar ellipsis_char = font->EllipsisChar; - int ellipsis_char_count = 1; - if (ellipsis_char == (ImWchar)-1) - { - ellipsis_char = font->DotChar; - ellipsis_char_count = 3; - } - const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char); - - float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side - float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis - - if (ellipsis_char_count > 1) - { - // Full ellipsis size without free spacing after it. - const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize); - ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots; - ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots; - } - - // We can now claim the space between pos_max.x and ellipsis_max.x - const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f); - float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x; - if (text == text_end_ellipsis && text_end_ellipsis < text_end_full) - { - // Always display at least 1 character if there's no room for character + ellipsis - text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full); - text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x; - } - while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1])) - { - // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text) - text_end_ellipsis--; - text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte - } - - // Render text, render ellipsis - RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f)); - float ellipsis_x = pos_min.x + text_size_clipped_x; - if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x) - for (int i = 0; i < ellipsis_char_count; i++) - { - font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char); - ellipsis_x += ellipsis_glyph_width; - } - } - else - { - RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f)); - } - - if (g.LogEnabled) - LogRenderedText(&pos_min, text, text_end_full); -} - -// Render a rectangle shaped with optional rounding and borders -void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); - const float border_size = g.Style.FrameBorderSize; - if (border && border_size > 0.0f) - { - window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); - } -} - -void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const float border_size = g.Style.FrameBorderSize; - if (border_size > 0.0f) - { - window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); - } -} - -void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) -{ - ImGuiContext& g = *GImGui; - if (id != g.NavId) - return; - if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) - return; - ImGuiWindow* window = g.CurrentWindow; - if (window->DC.NavHideHighlightOneFrame) - return; - - float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; - ImRect display_rect = bb; - display_rect.ClipWith(window->ClipRect); - if (flags & ImGuiNavHighlightFlags_TypeDefault) - { - const float THICKNESS = 2.0f; - const float DISTANCE = 3.0f + THICKNESS * 0.5f; - display_rect.Expand(ImVec2(DISTANCE, DISTANCE)); - bool fully_visible = window->ClipRect.Contains(display_rect); - if (!fully_visible) - window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); - window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, 0, THICKNESS); - if (!fully_visible) - window->DrawList->PopClipRect(); - } - if (flags & ImGuiNavHighlightFlags_TypeThin) - { - window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, 1.0f); - } -} - -void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT); - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - ImDrawList* draw_list = GetForegroundDrawList(viewport); - ImFontAtlas* font_atlas = draw_list->_Data->Font->ContainerAtlas; - ImVec2 offset, size, uv[4]; - if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) - { - const ImVec2 pos = base_pos - offset; - const float scale = base_scale; - ImTextureID tex_id = font_atlas->TexID; - draw_list->PushTextureID(tex_id); - draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); - draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); - draw_list->AddImage(tex_id, pos, pos + size * scale, uv[2], uv[3], col_border); - draw_list->AddImage(tex_id, pos, pos + size * scale, uv[0], uv[1], col_fill); - draw_list->PopTextureID(); - } - } -} - - -//----------------------------------------------------------------------------- -// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) -//----------------------------------------------------------------------------- - -// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods -ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL) -{ - memset(this, 0, sizeof(*this)); - Name = ImStrdup(name); - NameBufLen = (int)strlen(name) + 1; - ID = ImHashStr(name); - IDStack.push_back(ID); - MoveId = GetID("#MOVE"); - ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); - ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); - AutoFitFramesX = AutoFitFramesY = -1; - AutoPosLastDirection = ImGuiDir_None; - SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; - SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); - LastFrameActive = -1; - LastTimeActive = -1.0f; - FontWindowScale = 1.0f; - SettingsOffset = -1; - DrawList = &DrawListInst; - DrawList->_Data = &context->DrawListSharedData; - DrawList->_OwnerName = Name; -} - -ImGuiWindow::~ImGuiWindow() -{ - IM_ASSERT(DrawList == &DrawListInst); - IM_DELETE(Name); - ColumnsStorage.clear_destruct(); -} - -ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) -{ - ImGuiID seed = IDStack.back(); - ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); - ImGuiContext& g = *GImGui; - if (g.DebugHookIdInfo == id) - ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); - return id; -} - -ImGuiID ImGuiWindow::GetID(const void* ptr) -{ - ImGuiID seed = IDStack.back(); - ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); - ImGuiContext& g = *GImGui; - if (g.DebugHookIdInfo == id) - ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); - return id; -} - -ImGuiID ImGuiWindow::GetID(int n) -{ - ImGuiID seed = IDStack.back(); - ImGuiID id = ImHashData(&n, sizeof(n), seed); - ImGuiContext& g = *GImGui; - if (g.DebugHookIdInfo == id) - ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); - return id; -} - -// This is only used in rare/specific situations to manufacture an ID out of nowhere. -ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) -{ - ImGuiID seed = IDStack.back(); - ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs); - ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); - return id; -} - -static void SetCurrentWindow(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - g.CurrentWindow = window; - g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL; - if (window) - g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); -} - -void ImGui::GcCompactTransientMiscBuffers() -{ - ImGuiContext& g = *GImGui; - g.ItemFlagsStack.clear(); - g.GroupStack.clear(); - TableGcCompactSettings(); -} - -// Free up/compact internal window buffers, we can use this when a window becomes unused. -// Not freed: -// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data) -// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost. -void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window) -{ - window->MemoryCompacted = true; - window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity; - window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity; - window->IDStack.clear(); - window->DrawList->_ClearFreeMemory(); - window->DC.ChildWindows.clear(); - window->DC.ItemWidthStack.clear(); - window->DC.TextWrapPosStack.clear(); -} - -void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window) -{ - // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening. - // The other buffers tends to amortize much faster. - window->MemoryCompacted = false; - window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity); - window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity); - window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0; -} - -void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - - // While most behaved code would make an effort to not steal active id during window move/drag operations, - // we at least need to be resilient to it. Cancelling the move is rather aggressive and users of 'master' branch - // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that. - if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId) - { - IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n"); - g.MovingWindow = NULL; - } - - // Set active id - g.ActiveIdIsJustActivated = (g.ActiveId != id); - if (g.ActiveIdIsJustActivated) - { - IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : ""); - g.ActiveIdTimer = 0.0f; - g.ActiveIdHasBeenPressedBefore = false; - g.ActiveIdHasBeenEditedBefore = false; - g.ActiveIdMouseButton = -1; - if (id != 0) - { - g.LastActiveId = id; - g.LastActiveIdTimer = 0.0f; - } - } - g.ActiveId = id; - g.ActiveIdAllowOverlap = false; - g.ActiveIdNoClearOnFocusLoss = false; - g.ActiveIdWindow = window; - g.ActiveIdHasBeenEditedThisFrame = false; - if (id) - { - g.ActiveIdIsAlive = id; - g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustMovedToId == id) ? (ImGuiInputSource)ImGuiInputSource_Nav : ImGuiInputSource_Mouse; - } - - // Clear declaration of inputs claimed by the widget - // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet) - g.ActiveIdUsingMouseWheel = false; - g.ActiveIdUsingNavDirMask = 0x00; - g.ActiveIdUsingNavInputMask = 0x00; - g.ActiveIdUsingKeyInputMask.ClearAllBits(); -} - -void ImGui::ClearActiveID() -{ - SetActiveID(0, NULL); // g.ActiveId = 0; -} - -void ImGui::SetHoveredID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - g.HoveredId = id; - g.HoveredIdAllowOverlap = false; - g.HoveredIdUsingMouseWheel = false; - if (id != 0 && g.HoveredIdPreviousFrame != id) - g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f; -} - -ImGuiID ImGui::GetHoveredID() -{ - ImGuiContext& g = *GImGui; - return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame; -} - -// This is called by ItemAdd(). -// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID(). -void ImGui::KeepAliveID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId == id) - g.ActiveIdIsAlive = id; - if (g.ActiveIdPreviousFrame == id) - g.ActiveIdPreviousFrameIsAlive = true; -} - -void ImGui::MarkItemEdited(ImGuiID id) -{ - // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit(). - // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data. - ImGuiContext& g = *GImGui; - IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive); - IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out. - //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); - g.ActiveIdHasBeenEditedThisFrame = true; - g.ActiveIdHasBeenEditedBefore = true; - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; -} - -static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) -{ - // An active popup disable hovering on other windows (apart from its own children) - // FIXME-OPT: This could be cached/stored within the window. - ImGuiContext& g = *GImGui; - if (g.NavWindow) - if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) - if (focused_root_window->WasActive && focused_root_window != window->RootWindow) - { - // For the purpose of those flags we differentiate "standard popup" from "modal popup" - // NB: The order of those two tests is important because Modal windows are also Popups. - if (focused_root_window->Flags & ImGuiWindowFlags_Modal) - return false; - if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - return false; - } - return true; -} - -// This is roughly matching the behavior of internal-facing ItemHoverable() -// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered() -// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId -bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride)) - { - if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) - return false; - if (!IsItemFocused()) - return false; - } - else - { - // Test for bounding box overlap, as updated as ItemAdd() - ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; - if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) - return false; - IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function - - // Test if we are hovering the right window (our window could be behind another window) - // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) - // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable - // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was - // the test that has been running for a long while. - if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) - if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) - return false; - - // Test if another item is active (e.g. being dragged) - if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) - return false; - - // Test if interactions on this window are blocked by an active popup or modal. - // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. - if (!IsWindowContentHoverable(window, flags)) - return false; - - // Test if the item is disabled - if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) - return false; - - // Special handling for calling after Begin() which represent the title bar or tab. - // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. - if (g.LastItemData.ID == window->MoveId && window->WriteAccessed) - return false; - } - - return true; -} - -// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered(). -bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) -{ - ImGuiContext& g = *GImGui; - if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) - return false; - - ImGuiWindow* window = g.CurrentWindow; - if (g.HoveredWindow != window) - return false; - if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) - return false; - if (!IsMouseHoveringRect(bb.Min, bb.Max)) - return false; - if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) - { - g.HoveredIdDisabled = true; - return false; - } - - // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level - // hover test in widgets code. We could also decide to split this function is two. - if (id != 0) - SetHoveredID(id); - - // When disabled we'll return false but still set HoveredId - ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); - if (item_flags & ImGuiItemFlags_Disabled) - { - // Release active id if turning disabled - if (g.ActiveId == id) - ClearActiveID(); - g.HoveredIdDisabled = true; - return false; - } - - if (id != 0) - { - // [DEBUG] Item Picker tool! - // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making - // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered - // items if we perform the test in ItemAdd(), but that would incur a small runtime cost. - // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd(). - if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id) - GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255)); - if (g.DebugItemPickerBreakId == id) - IM_DEBUG_BREAK(); - } - - if (g.NavDisableMouseHover) - return false; - - return true; -} - -bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (!bb.Overlaps(window->ClipRect)) - if (id == 0 || (id != g.ActiveId && id != g.NavId)) - if (!g.LogEnabled) - return true; - return false; -} - -// This is also inlined in ItemAdd() -// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect! -void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) -{ - ImGuiContext& g = *GImGui; - g.LastItemData.ID = item_id; - g.LastItemData.InFlags = in_flags; - g.LastItemData.StatusFlags = item_flags; - g.LastItemData.Rect = item_rect; -} - -float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) -{ - if (wrap_pos_x < 0.0f) - return 0.0f; - - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (wrap_pos_x == 0.0f) - { - // We could decide to setup a default wrapping max point for auto-resizing windows, - // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function? - //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) - // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x); - //else - wrap_pos_x = window->WorkRect.Max.x; - } - else if (wrap_pos_x > 0.0f) - { - wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space - } - - return ImMax(wrap_pos_x - pos.x, 1.0f); -} - -// IM_ALLOC() == ImGui::MemAlloc() -void* ImGui::MemAlloc(size_t size) -{ - if (ImGuiContext* ctx = GImGui) - ctx->IO.MetricsActiveAllocations++; - return (*GImAllocatorAllocFunc)(size, GImAllocatorUserData); -} - -// IM_FREE() == ImGui::MemFree() -void ImGui::MemFree(void* ptr) -{ - if (ptr) - if (ImGuiContext* ctx = GImGui) - ctx->IO.MetricsActiveAllocations--; - return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData); -} - -const char* ImGui::GetClipboardText() -{ - ImGuiContext& g = *GImGui; - return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : ""; -} - -void ImGui::SetClipboardText(const char* text) -{ - ImGuiContext& g = *GImGui; - if (g.IO.SetClipboardTextFn) - g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text); -} - -const char* ImGui::GetVersion() -{ - return IMGUI_VERSION; -} - -// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself -// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module -ImGuiContext* ImGui::GetCurrentContext() -{ - return GImGui; -} - -void ImGui::SetCurrentContext(ImGuiContext* ctx) -{ -#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC - IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. -#else - GImGui = ctx; -#endif -} - -void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data) -{ - GImAllocatorAllocFunc = alloc_func; - GImAllocatorFreeFunc = free_func; - GImAllocatorUserData = user_data; -} - -// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space) -void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data) -{ - *p_alloc_func = GImAllocatorAllocFunc; - *p_free_func = GImAllocatorFreeFunc; - *p_user_data = GImAllocatorUserData; -} - -ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) -{ - ImGuiContext* prev_ctx = GetCurrentContext(); - ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); - SetCurrentContext(ctx); - Initialize(); - if (prev_ctx != NULL) - SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one. - return ctx; -} - -void ImGui::DestroyContext(ImGuiContext* ctx) -{ - ImGuiContext* prev_ctx = GetCurrentContext(); - if (ctx == NULL) //-V1051 - ctx = prev_ctx; - SetCurrentContext(ctx); - Shutdown(); - SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL); - IM_DELETE(ctx); -} - -// No specific ordering/dependency support, will see as needed -ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) -{ - ImGuiContext& g = *ctx; - IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_); - g.Hooks.push_back(*hook); - g.Hooks.back().HookId = ++g.HookIdNext; - return g.HookIdNext; -} - -// Deferred removal, avoiding issue with changing vector while iterating it -void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) -{ - ImGuiContext& g = *ctx; - IM_ASSERT(hook_id != 0); - for (int n = 0; n < g.Hooks.Size; n++) - if (g.Hooks[n].HookId == hook_id) - g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_; -} - -// Call context hooks (used by e.g. test engine) -// We assume a small number of hooks so all stored in same array -void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type) -{ - ImGuiContext& g = *ctx; - for (int n = 0; n < g.Hooks.Size; n++) - if (g.Hooks[n].Type == hook_type) - g.Hooks[n].Callback(&g, &g.Hooks[n]); -} - -ImGuiIO& ImGui::GetIO() -{ - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); - return GImGui->IO; -} - -// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame() -ImDrawData* ImGui::GetDrawData() -{ - ImGuiContext& g = *GImGui; - ImGuiViewportP* viewport = g.Viewports[0]; - return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL; -} - -double ImGui::GetTime() -{ - return GImGui->Time; -} - -int ImGui::GetFrameCount() -{ - return GImGui->FrameCount; -} - -static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name) -{ - // Create the draw list on demand, because they are not frequently used for all viewports - ImGuiContext& g = *GImGui; - IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists)); - ImDrawList* draw_list = viewport->DrawLists[drawlist_no]; - if (draw_list == NULL) - { - draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData); - draw_list->_OwnerName = drawlist_name; - viewport->DrawLists[drawlist_no] = draw_list; - } - - // Our ImDrawList system requires that there is always a command - if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount) - { - draw_list->_ResetForNewFrame(); - draw_list->PushTextureID(g.IO.Fonts->TexID); - draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); - viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount; - } - return draw_list; -} - -ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport) -{ - return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background"); -} - -ImDrawList* ImGui::GetBackgroundDrawList() -{ - ImGuiContext& g = *GImGui; - return GetBackgroundDrawList(g.Viewports[0]); -} - -ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport) -{ - return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground"); -} - -ImDrawList* ImGui::GetForegroundDrawList() -{ - ImGuiContext& g = *GImGui; - return GetForegroundDrawList(g.Viewports[0]); -} - -ImDrawListSharedData* ImGui::GetDrawListSharedData() -{ - return &GImGui->DrawListSharedData; -} - -void ImGui::StartMouseMovingWindow(ImGuiWindow* window) -{ - // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows. - // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward. - // This is because we want ActiveId to be set even when the window is not permitted to move. - ImGuiContext& g = *GImGui; - FocusWindow(window); - SetActiveID(window->MoveId, window); - g.NavDisableHighlight = true; - g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; - g.ActiveIdNoClearOnFocusLoss = true; - SetActiveIdUsingNavAndKeys(); - - bool can_move_window = true; - if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) - can_move_window = false; - if (can_move_window) - g.MovingWindow = window; -} - -// Handle mouse moving window -// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() -// FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId. -// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs, -// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other. -void ImGui::UpdateMouseMovingWindowNewFrame() -{ - ImGuiContext& g = *GImGui; - if (g.MovingWindow != NULL) - { - // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). - // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. - KeepAliveID(g.ActiveId); - IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); - ImGuiWindow* moving_window = g.MovingWindow->RootWindow; - if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) - { - ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; - SetWindowPos(moving_window, pos, ImGuiCond_Always); - FocusWindow(g.MovingWindow); - } - else - { - g.MovingWindow = NULL; - ClearActiveID(); - } - } - else - { - // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others. - if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId) - { - KeepAliveID(g.ActiveId); - if (!g.IO.MouseDown[0]) - ClearActiveID(); - } - } -} - -// Initiate moving window when clicking on empty space or title bar. -// Handle left-click and right-click focus. -void ImGui::UpdateMouseMovingWindowEndFrame() -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId != 0 || g.HoveredId != 0) - return; - - // Unless we just made a window/popup appear - if (g.NavWindow && g.NavWindow->Appearing) - return; - - // Click on empty space to focus window and start moving - // (after we're done with all our widgets) - if (g.IO.MouseClicked[0]) - { - // Handle the edge case of a popup being closed while clicking in its empty space. - // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more. - ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; - const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel); - - if (root_window != NULL && !is_closed_popup) - { - StartMouseMovingWindow(g.HoveredWindow); //-V595 - - // Cancel moving if clicked outside of title bar - if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar)) - if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) - g.MovingWindow = NULL; - - // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already) - if (g.HoveredIdDisabled) - g.MovingWindow = NULL; - } - else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL) - { - // Clicking on void disable focus - FocusWindow(NULL); - } - } - - // With right mouse button we close popups without changing focus based on where the mouse is aimed - // Instead, focus will be restored to the window under the bottom-most closed popup. - // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger) - if (g.IO.MouseClicked[1]) - { - // Find the top-most window between HoveredWindow and the top-most Modal Window. - // This is where we can trim the popup stack. - ImGuiWindow* modal = GetTopMostPopupModal(); - bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal)); - ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); - } -} - -static bool IsWindowActiveAndVisible(ImGuiWindow* window) -{ - return (window->Active) && (!window->Hidden); -} - -static void ImGui::UpdateKeyboardInputs() -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - - // Import legacy keys or verify they are not used -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - if (io.BackendUsingLegacyKeyArrays == 0) - { - // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally. - for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++) - IM_ASSERT((io.KeysDown[n] == false || IsKeyDown(n)) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); - } - else - { - if (g.FrameCount == 0) - for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) - IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!"); - - // Build reverse KeyMap (Named -> Legacy) - for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) - if (io.KeyMap[n] != -1) - { - IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n])); - io.KeyMap[io.KeyMap[n]] = n; - } - - // Import legacy keys into new ones - for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) - if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1) - { - const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n); - IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key)); - io.KeysData[key].Down = io.KeysDown[n]; - if (key != n) - io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends - io.BackendUsingLegacyKeyArrays = 1; - } - if (io.BackendUsingLegacyKeyArrays == 1) - { - io.KeysData[ImGuiKey_ModCtrl].Down = io.KeyCtrl; - io.KeysData[ImGuiKey_ModShift].Down = io.KeyShift; - io.KeysData[ImGuiKey_ModAlt].Down = io.KeyAlt; - io.KeysData[ImGuiKey_ModSuper].Down = io.KeySuper; - } - } -#endif - - // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools - io.KeyMods = GetMergedModFlags(); - - // Clear gamepad data if disabled - if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0) - for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++) - { - io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false; - io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f; - } - - // Update keys - for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) - { - ImGuiKeyData* key_data = &io.KeysData[i]; - key_data->DownDurationPrev = key_data->DownDuration; - key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f; - } -} - -static void ImGui::UpdateMouseInputs() -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - - // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) - if (IsMousePosValid(&io.MousePos)) - io.MousePos = g.MouseLastValidPos = ImFloorSigned(io.MousePos); - - // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta - if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev)) - io.MouseDelta = io.MousePos - io.MousePosPrev; - else - io.MouseDelta = ImVec2(0.0f, 0.0f); - - // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. - if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) - g.NavDisableMouseHover = false; - - io.MousePosPrev = io.MousePos; - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; - io.MouseClickedCount[i] = 0; // Will be filled below - io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f; - io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]; - io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f; - if (io.MouseClicked[i]) - { - bool is_repeated_click = false; - if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime) - { - ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist) - is_repeated_click = true; - } - if (is_repeated_click) - io.MouseClickedLastCount[i]++; - else - io.MouseClickedLastCount[i] = 1; - io.MouseClickedTime[i] = g.Time; - io.MouseClickedPos[i] = io.MousePos; - io.MouseClickedCount[i] = io.MouseClickedLastCount[i]; - io.MouseDragMaxDistanceSqr[i] = 0.0f; - } - else if (io.MouseDown[i]) - { - // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold - float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f; - io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos); - } - - // We provide io.MouseDoubleClicked[] as a legacy service - io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2); - - // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation - if (io.MouseClicked[i]) - g.NavDisableMouseHover = false; - } -} - -static void StartLockWheelingWindow(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (g.WheelingWindow == window) - return; - g.WheelingWindow = window; - g.WheelingWindowRefMousePos = g.IO.MousePos; - g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER; -} - -void ImGui::UpdateMouseWheel() -{ - ImGuiContext& g = *GImGui; - - // Reset the locked window if we move the mouse or after the timer elapses - if (g.WheelingWindow != NULL) - { - g.WheelingWindowTimer -= g.IO.DeltaTime; - if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) - g.WheelingWindowTimer = 0.0f; - if (g.WheelingWindowTimer <= 0.0f) - { - g.WheelingWindow = NULL; - g.WheelingWindowTimer = 0.0f; - } - } - - float wheel_x = g.IO.MouseWheelH; - float wheel_y = g.IO.MouseWheel; - if (wheel_x == 0.0f && wheel_y == 0.0f) - return; - - if ((g.ActiveId != 0 && g.ActiveIdUsingMouseWheel) || (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel)) - return; - - ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; - if (!window || window->Collapsed) - return; - - // Zoom / Scale window - // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. - if (wheel_y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) - { - StartLockWheelingWindow(window); - const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); - const float scale = new_font_scale / window->FontWindowScale; - window->FontWindowScale = new_font_scale; - if (window == window->RootWindow) - { - const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; - SetWindowPos(window, window->Pos + offset, 0); - window->Size = ImFloor(window->Size * scale); - window->SizeFull = ImFloor(window->SizeFull * scale); - } - return; - } - - // Mouse wheel scrolling - // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent - if (g.IO.KeyCtrl) - return; - - // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead - // (we avoid doing it on OSX as it the OS input layer handles this already) - const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors; - if (swap_axis) - { - wheel_x = wheel_y; - wheel_y = 0.0f; - } - - // Vertical Mouse Wheel scrolling - if (wheel_y != 0.0f) - { - StartLockWheelingWindow(window); - while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) - window = window->ParentWindow; - if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) - { - float max_step = window->InnerRect.GetHeight() * 0.67f; - float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step)); - SetScrollY(window, window->Scroll.y - wheel_y * scroll_step); - } - } - - // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held - if (wheel_x != 0.0f) - { - StartLockWheelingWindow(window); - while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) - window = window->ParentWindow; - if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) - { - float max_step = window->InnerRect.GetWidth() * 0.67f; - float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step)); - SetScrollX(window, window->Scroll.x - wheel_x * scroll_step); - } - } -} - -// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) -void ImGui::UpdateHoveredWindowAndCaptureFlags() -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING)); - - // Find the window hovered by mouse: - // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. - // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame. - // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. - bool clear_hovered_windows = false; - FindHoveredWindow(); - - // Modal windows prevents mouse from hovering behind them. - ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) - clear_hovered_windows = true; - - // Disabled mouse? - if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) - clear_hovered_windows = true; - - // We track click ownership. When clicked outside of a window the click is owned by the application and - // won't report hovering nor request capture even while dragging over our windows afterward. - const bool has_open_popup = (g.OpenPopupStack.Size > 0); - const bool has_open_modal = (modal_window != NULL); - int mouse_earliest_down = -1; - bool mouse_any_down = false; - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - if (io.MouseClicked[i]) - { - io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup; - io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal; - } - mouse_any_down |= io.MouseDown[i]; - if (io.MouseDown[i]) - if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down]) - mouse_earliest_down = i; - } - const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down]; - const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down]; - - // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. - // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) - const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; - if (!mouse_avail && !mouse_dragging_extern_payload) - clear_hovered_windows = true; - - if (clear_hovered_windows) - g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; - - // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app) - // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag - if (g.WantCaptureMouseNextFrame != -1) - { - io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0); - } - else - { - io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup; - io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal; - } - - // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app) - if (g.WantCaptureKeyboardNextFrame != -1) - io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); - else - io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); - if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) - io.WantCaptureKeyboard = true; - - // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible - io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; -} - -// [Internal] Do not use directly (can read io.KeyMods instead) -ImGuiModFlags ImGui::GetMergedModFlags() -{ - ImGuiContext& g = *GImGui; - ImGuiModFlags key_mods = ImGuiModFlags_None; - if (g.IO.KeyCtrl) { key_mods |= ImGuiModFlags_Ctrl; } - if (g.IO.KeyShift) { key_mods |= ImGuiModFlags_Shift; } - if (g.IO.KeyAlt) { key_mods |= ImGuiModFlags_Alt; } - if (g.IO.KeySuper) { key_mods |= ImGuiModFlags_Super; } - return key_mods; -} - -void ImGui::NewFrame() -{ - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); - ImGuiContext& g = *GImGui; - - // Remove pending delete hooks before frame start. - // This deferred removal avoid issues of removal while iterating the hook vector - for (int n = g.Hooks.Size - 1; n >= 0; n--) - if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_) - g.Hooks.erase(&g.Hooks[n]); - - CallContextHooks(&g, ImGuiContextHookType_NewFramePre); - - // Check and assert for various common IO and Configuration mistakes - ErrorCheckNewFrameSanityChecks(); - - // Load settings on first frame, save settings when modified (after a delay) - UpdateSettings(); - - g.Time += g.IO.DeltaTime; - g.WithinFrameScope = true; - g.FrameCount += 1; - g.TooltipOverrideCount = 0; - g.WindowsActiveCount = 0; - g.MenusIdSubmittedThisFrame.resize(0); - - // Calculate frame-rate for the user, as a purely luxurious feature - g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; - g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); - g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame)); - g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX; - - UpdateViewportsNewFrame(); - - // Setup current font and draw list shared data - g.IO.Fonts->Locked = true; - SetCurrentFont(GetDefaultFont()); - IM_ASSERT(g.Font->IsLoaded()); - ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); - for (int n = 0; n < g.Viewports.Size; n++) - virtual_space.Add(g.Viewports[n]->GetMainRect()); - g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); - g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; - g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError); - g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; - if (g.Style.AntiAliasedLines) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; - if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines)) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex; - if (g.Style.AntiAliasedFill) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; - if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; - - // Mark rendering data as invalid to prevent user who may have a handle on it to use it. - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataP.Clear(); - } - - // Drag and drop keep the source ID alive so even if the source disappear our state is consistent - if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId) - KeepAliveID(g.DragDropPayload.SourceId); - - // Update HoveredId data - if (!g.HoveredIdPreviousFrame) - g.HoveredIdTimer = 0.0f; - if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId)) - g.HoveredIdNotActiveTimer = 0.0f; - if (g.HoveredId) - g.HoveredIdTimer += g.IO.DeltaTime; - if (g.HoveredId && g.ActiveId != g.HoveredId) - g.HoveredIdNotActiveTimer += g.IO.DeltaTime; - g.HoveredIdPreviousFrame = g.HoveredId; - g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel; - g.HoveredId = 0; - g.HoveredIdAllowOverlap = false; - g.HoveredIdUsingMouseWheel = false; - g.HoveredIdDisabled = false; - - // Clear ActiveID if the item is not alive anymore. - // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd(). - // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves. - if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId) - { - IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n"); - ClearActiveID(); - } - - // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore) - if (g.ActiveId) - g.ActiveIdTimer += g.IO.DeltaTime; - g.LastActiveIdTimer += g.IO.DeltaTime; - g.ActiveIdPreviousFrame = g.ActiveId; - g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow; - g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore; - g.ActiveIdIsAlive = 0; - g.ActiveIdHasBeenEditedThisFrame = false; - g.ActiveIdPreviousFrameIsAlive = false; - g.ActiveIdIsJustActivated = false; - if (g.TempInputId != 0 && g.ActiveId != g.TempInputId) - g.TempInputId = 0; - if (g.ActiveId == 0) - { - g.ActiveIdUsingNavDirMask = 0x00; - g.ActiveIdUsingNavInputMask = 0x00; - g.ActiveIdUsingKeyInputMask.ClearAllBits(); - } - - // Drag and drop - g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; - g.DragDropAcceptIdCurr = 0; - g.DragDropAcceptIdCurrRectSurface = FLT_MAX; - g.DragDropWithinSource = false; - g.DragDropWithinTarget = false; - g.DragDropHoldJustPressedId = 0; - - // Close popups on focus lost (currently wip/opt-in) - //if (g.IO.AppFocusLost) - // ClosePopupsExceptModals(); - - // Process input queue (trickle as many events as possible) - g.InputEventsTrail.resize(0); - UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue); - - // Update keyboard input state - UpdateKeyboardInputs(); - - //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl)); - //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift)); - //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt)); - //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper)); - - // Update gamepad/keyboard navigation - NavUpdate(); - - // Update mouse input state - UpdateMouseInputs(); - - // Find hovered window - // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) - UpdateHoveredWindowAndCaptureFlags(); - - // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) - UpdateMouseMovingWindowNewFrame(); - - // Background darkening/whitening - if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f)) - g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f); - else - g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f); - - g.MouseCursor = ImGuiMouseCursor_Arrow; - g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; - - // Platform IME data: reset for the frame - g.PlatformImeDataPrev = g.PlatformImeData; - g.PlatformImeData.WantVisible = false; - - // Mouse wheel scrolling, scale - UpdateMouseWheel(); - - // Mark all windows as not visible and compact unused memory. - IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); - const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* window = g.Windows[i]; - window->WasActive = window->Active; - window->BeginCount = 0; - window->Active = false; - window->WriteAccessed = false; - - // Garbage collect transient buffers of recently unused windows - if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) - GcCompactTransientWindowBuffers(window); - } - - // Garbage collect transient buffers of recently unused tables - for (int i = 0; i < g.TablesLastTimeActive.Size; i++) - if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) - TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); - for (int i = 0; i < g.TablesTempData.Size; i++) - if (g.TablesTempData[i].LastTimeActive >= 0.0f && g.TablesTempData[i].LastTimeActive < memory_compact_start_time) - TableGcCompactTransientBuffers(&g.TablesTempData[i]); - if (g.GcCompactAll) - GcCompactTransientMiscBuffers(); - g.GcCompactAll = false; - - // Closing the focused window restore focus to the first active root window in descending z-order - if (g.NavWindow && !g.NavWindow->WasActive) - FocusTopMostWindowUnderOne(NULL, NULL); - - // No window should be open at the beginning of the frame. - // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. - g.CurrentWindowStack.resize(0); - g.BeginPopupStack.resize(0); - g.ItemFlagsStack.resize(0); - g.ItemFlagsStack.push_back(ImGuiItemFlags_None); - g.GroupStack.resize(0); - - // [DEBUG] Update debug features - UpdateDebugToolItemPicker(); - UpdateDebugToolStackQueries(); - - // Create implicit/fallback window - which we will only render it if the user has added something to it. - // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. - // This fallback is particularly important as it avoid ImGui:: calls from crashing. - g.WithinFrameScopeWithImplicitWindow = true; - SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); - Begin("Debug##Default"); - IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true); - - CallContextHooks(&g, ImGuiContextHookType_NewFramePost); -} - -void ImGui::Initialize() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(!g.Initialized && !g.SettingsLoaded); - - // Add .ini handle for ImGuiWindow type - { - ImGuiSettingsHandler ini_handler; - ini_handler.TypeName = "Window"; - ini_handler.TypeHash = ImHashStr("Window"); - ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll; - ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; - ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; - ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll; - ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; - AddSettingsHandler(&ini_handler); - } - - // Add .ini handle for ImGuiTable type - TableSettingsAddSettingsHandler(); - - // Create default viewport - ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); - g.Viewports.push_back(viewport); - g.TempBuffer.resize(1024 * 3 + 1, 0); - -#ifdef IMGUI_HAS_DOCK -#endif - - g.Initialized = true; -} - -// This function is merely here to free heap allocations. -void ImGui::Shutdown() -{ - // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) - ImGuiContext& g = *GImGui; - if (g.IO.Fonts && g.FontAtlasOwnedByContext) - { - g.IO.Fonts->Locked = false; - IM_DELETE(g.IO.Fonts); - } - g.IO.Fonts = NULL; - - // Cleanup of other data are conditional on actually having initialized Dear ImGui. - if (!g.Initialized) - return; - - // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) - if (g.SettingsLoaded && g.IO.IniFilename != NULL) - SaveIniSettingsToDisk(g.IO.IniFilename); - - CallContextHooks(&g, ImGuiContextHookType_Shutdown); - - // Clear everything else - g.Windows.clear_delete(); - g.WindowsFocusOrder.clear(); - g.WindowsTempSortBuffer.clear(); - g.CurrentWindow = NULL; - g.CurrentWindowStack.clear(); - g.WindowsById.Clear(); - g.NavWindow = NULL; - g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; - g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; - g.MovingWindow = NULL; - g.ColorStack.clear(); - g.StyleVarStack.clear(); - g.FontStack.clear(); - g.OpenPopupStack.clear(); - g.BeginPopupStack.clear(); - - g.Viewports.clear_delete(); - - g.TabBars.Clear(); - g.CurrentTabBarStack.clear(); - g.ShrinkWidthBuffer.clear(); - - g.ClipperTempData.clear_destruct(); - - g.Tables.Clear(); - g.TablesTempData.clear_destruct(); - g.DrawChannelsTempMergeBuffer.clear(); - - g.ClipboardHandlerData.clear(); - g.MenusIdSubmittedThisFrame.clear(); - g.InputTextState.ClearFreeMemory(); - - g.SettingsWindows.clear(); - g.SettingsHandlers.clear(); - - if (g.LogFile) - { -#ifndef IMGUI_DISABLE_TTY_FUNCTIONS - if (g.LogFile != stdout) -#endif - ImFileClose(g.LogFile); - g.LogFile = NULL; - } - g.LogBuffer.clear(); - g.DebugLogBuf.clear(); - - g.Initialized = false; -} - -// FIXME: Add a more explicit sort order in the window structure. -static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) -{ - const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs; - const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs; - if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup)) - return d; - if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) - return d; - return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); -} - -static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) -{ - out_sorted_windows->push_back(window); - if (window->Active) - { - int count = window->DC.ChildWindows.Size; - ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); - for (int i = 0; i < count; i++) - { - ImGuiWindow* child = window->DC.ChildWindows[i]; - if (child->Active) - AddWindowToSortBuffer(out_sorted_windows, child); - } - } -} - -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list) -{ - if (draw_list->CmdBuffer.Size == 0) - return; - if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL) - return; - - // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. - // May trigger for you if you are using PrimXXX functions incorrectly. - IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); - IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); - if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) - IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); - - // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) - // If this assert triggers because you are drawing lots of stuff manually: - // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. - // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents. - // - If you want large meshes with more than 64K vertices, you can either: - // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. - // Most example backends already support this from 1.71. Pre-1.71 backends won't. - // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. - // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. - // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time: - // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); - // Your own engine or render API may use different parameters or function calls to specify index sizes. - // 2 and 4 bytes indices are generally supported by most graphics API. - // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching - // the 64K limit to split your draw commands in multiple draw lists. - if (sizeof(ImDrawIdx) == 2) - IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); - - out_list->push_back(draw_list); -} - -static void AddWindowToDrawData(ImGuiWindow* window, int layer) -{ - ImGuiContext& g = *GImGui; - ImGuiViewportP* viewport = g.Viewports[0]; - g.IO.MetricsRenderWindows++; - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList); - for (int i = 0; i < window->DC.ChildWindows.Size; i++) - { - ImGuiWindow* child = window->DC.ChildWindows[i]; - if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active - AddWindowToDrawData(child, layer); - } -} - -static inline int GetWindowDisplayLayer(ImGuiWindow* window) -{ - return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; -} - -// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) -static inline void AddRootWindowToDrawData(ImGuiWindow* window) -{ - AddWindowToDrawData(window, GetWindowDisplayLayer(window)); -} - -void ImDrawDataBuilder::FlattenIntoSingleLayer() -{ - int n = Layers[0].Size; - int size = n; - for (int i = 1; i < IM_ARRAYSIZE(Layers); i++) - size += Layers[i].Size; - Layers[0].resize(size); - for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++) - { - ImVector& layer = Layers[layer_n]; - if (layer.empty()) - continue; - memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); - n += layer.Size; - layer.resize(0); - } -} - -static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector* draw_lists) -{ - ImGuiIO& io = ImGui::GetIO(); - ImDrawData* draw_data = &viewport->DrawDataP; - draw_data->Valid = true; - draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; - draw_data->CmdListsCount = draw_lists->Size; - draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; - draw_data->DisplayPos = viewport->Pos; - draw_data->DisplaySize = viewport->Size; - draw_data->FramebufferScale = io.DisplayFramebufferScale; - for (int n = 0; n < draw_lists->Size; n++) - { - ImDrawList* draw_list = draw_lists->Data[n]; - draw_list->_PopUnusedDrawCmd(); - draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; - draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; - } -} - -// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering. -// - When using this function it is sane to ensure that float are perfectly rounded to integer values, -// so that e.g. (int)(max.x-min.x) in user's render produce correct result. -// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect(): -// some frequently called functions which to modify both channels and clipping simultaneously tend to use the -// more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds. -void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect); - window->ClipRect = window->DrawList->_ClipRectStack.back(); -} - -void ImGui::PopClipRect() -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DrawList->PopClipRect(); - window->ClipRect = window->DrawList->_ClipRectStack.back(); -} - -static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport(); - ImRect viewport_rect = viewport->GetMainRect(); - - // Draw behind window by moving the draw command at the FRONT of the draw list - { - // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows, - // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing. - // FIXME: This is creating complication, might be simpler if we could inject a drawlist in drawdata at a given position and not attempt to manipulate ImDrawCmd order. - ImDrawList* draw_list = window->RootWindow->DrawList; - if (draw_list->CmdBuffer.Size == 0) - draw_list->AddDrawCmd(); - draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // Ensure ImDrawCmd are not merged - draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); - ImDrawCmd cmd = draw_list->CmdBuffer.back(); - IM_ASSERT(cmd.ElemCount == 6); - draw_list->CmdBuffer.pop_back(); - draw_list->CmdBuffer.push_front(cmd); - draw_list->PopClipRect(); - draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command. - } -} - -ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* bottom_most_visible_window = parent_window; - for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--) - { - ImGuiWindow* window = g.Windows[i]; - if (window->Flags & ImGuiWindowFlags_ChildWindow) - continue; - if (!IsWindowWithinBeginStackOf(window, parent_window)) - break; - if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window)) - bottom_most_visible_window = window; - } - return bottom_most_visible_window; -} - -static void ImGui::RenderDimmedBackgrounds() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal(); - if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) - return; - const bool dim_bg_for_modal = (modal_window != NULL); - const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active); - if (!dim_bg_for_modal && !dim_bg_for_window_list) - return; - - if (dim_bg_for_modal) - { - // Draw dimming behind modal or a begin stack child, whichever comes first in draw order. - ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window); - RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio)); - } - else if (dim_bg_for_window_list) - { - // Draw dimming behind CTRL+Tab target window - RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); - - // Draw border around CTRL+Tab target window - ImGuiWindow* window = g.NavWindowingTargetAnim; - ImGuiViewport* viewport = GetMainViewport(); - float distance = g.FontSize; - ImRect bb = window->Rect(); - bb.Expand(distance); - if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) - bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward - if (window->DrawList->CmdBuffer.Size == 0) - window->DrawList->AddDrawCmd(); - window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); - window->DrawList->PopClipRect(); - } -} - -// This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. -void ImGui::EndFrame() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.Initialized); - - // Don't process EndFrame() multiple times. - if (g.FrameCountEnded == g.FrameCount) - return; - IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?"); - - CallContextHooks(&g, ImGuiContextHookType_EndFramePre); - - ErrorCheckEndFrameSanityChecks(); - - // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) - if (g.IO.SetPlatformImeDataFn && memcmp(&g.PlatformImeData, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) - g.IO.SetPlatformImeDataFn(GetMainViewport(), &g.PlatformImeData); - - // Hide implicit/fallback "Debug" window if it hasn't been used - g.WithinFrameScopeWithImplicitWindow = false; - if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) - g.CurrentWindow->Active = false; - End(); - - // Update navigation: CTRL+Tab, wrap-around requests - NavEndFrame(); - - // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) - if (g.DragDropActive) - { - bool is_delivered = g.DragDropPayload.Delivery; - bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton)); - if (is_delivered || is_elapsed) - ClearDragDrop(); - } - - // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing. - if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) - { - g.DragDropWithinSource = true; - SetTooltip("..."); - g.DragDropWithinSource = false; - } - - // End frame - g.WithinFrameScope = false; - g.FrameCountEnded = g.FrameCount; - - // Initiate moving window + handle left-click and right-click focus - UpdateMouseMovingWindowEndFrame(); - - // Sort the window list so that all child windows are after their parent - // We cannot do that on FocusWindow() because children may not exist yet - g.WindowsTempSortBuffer.resize(0); - g.WindowsTempSortBuffer.reserve(g.Windows.Size); - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* window = g.Windows[i]; - if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it - continue; - AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window); - } - - // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong. - IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size); - g.Windows.swap(g.WindowsTempSortBuffer); - g.IO.MetricsActiveWindows = g.WindowsActiveCount; - - // Unlock font atlas - g.IO.Fonts->Locked = false; - - // Clear Input data for next frame - g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; - g.IO.InputQueueCharacters.resize(0); - memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); - - CallContextHooks(&g, ImGuiContextHookType_EndFramePost); -} - -// Prepare the data for rendering so you can call GetDrawData() -// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all: -// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend) -void ImGui::Render() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.Initialized); - - if (g.FrameCountEnded != g.FrameCount) - EndFrame(); - const bool first_render_of_frame = (g.FrameCountRendered != g.FrameCount); - g.FrameCountRendered = g.FrameCount; - g.IO.MetricsRenderWindows = 0; - - CallContextHooks(&g, ImGuiContextHookType_RenderPre); - - // Add background ImDrawList (for each active viewport) - for (int n = 0; n != g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.Clear(); - if (viewport->DrawLists[0] != NULL) - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); - } - - // Draw modal/window whitening backgrounds - if (first_render_of_frame) - RenderDimmedBackgrounds(); - - // Add ImDrawList to render - ImGuiWindow* windows_to_render_top_most[2]; - windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; - windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL); - for (int n = 0; n != g.Windows.Size; n++) - { - ImGuiWindow* window = g.Windows[n]; - IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" - if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) - AddRootWindowToDrawData(window); - } - for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++) - if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window - AddRootWindowToDrawData(windows_to_render_top_most[n]); - - // Draw software mouse cursor if requested by io.MouseDrawCursor flag - if (g.IO.MouseDrawCursor && first_render_of_frame && g.MouseCursor != ImGuiMouseCursor_None) - RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); - - // Setup ImDrawData structures for end-user - g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.FlattenIntoSingleLayer(); - - // Add foreground ImDrawList (for each active viewport) - if (viewport->DrawLists[1] != NULL) - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); - - SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); - ImDrawData* draw_data = &viewport->DrawDataP; - g.IO.MetricsRenderVertices += draw_data->TotalVtxCount; - g.IO.MetricsRenderIndices += draw_data->TotalIdxCount; - } - - CallContextHooks(&g, ImGuiContextHookType_RenderPost); -} - -// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. -// CalcTextSize("") should return ImVec2(0.0f, g.FontSize) -ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) -{ - ImGuiContext& g = *GImGui; - - const char* text_display_end; - if (hide_text_after_double_hash) - text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string - else - text_display_end = text_end; - - ImFont* font = g.Font; - const float font_size = g.FontSize; - if (text == text_display_end || font == NULL) // TODO(mziulek): `|| font == NULL` fixes issue found by UBSAN. Upstream this change. - return ImVec2(0.0f, font_size); - ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); - - // Round - // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out. - // FIXME: Investigate using ceilf or e.g. - // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c - // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html - text_size.x = IM_FLOOR(text_size.x + 0.99999f); - - return text_size; -} - -// Find window given position, search front-to-back -// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically -// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is -// called, aka before the next Begin(). Moving window isn't affected. -static void FindHoveredWindow() -{ - ImGuiContext& g = *GImGui; - - ImGuiWindow* hovered_window = NULL; - ImGuiWindow* hovered_window_ignoring_moving_window = NULL; - if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) - hovered_window = g.MovingWindow; - - ImVec2 padding_regular = g.Style.TouchExtraPadding; - ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular; - for (int i = g.Windows.Size - 1; i >= 0; i--) - { - ImGuiWindow* window = g.Windows[i]; - IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. - if (!window->Active || window->Hidden) - continue; - if (window->Flags & ImGuiWindowFlags_NoMouseInputs) - continue; - - // Using the clipped AABB, a child window will typically be clipped by its parent (not always) - ImRect bb(window->OuterRectClipped); - if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) - bb.Expand(padding_regular); - else - bb.Expand(padding_for_resize); - if (!bb.Contains(g.IO.MousePos)) - continue; - - // Support for one rectangular hole in any given window - // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512) - if (window->HitTestHoleSize.x != 0) - { - ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y); - ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y); - if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos)) - continue; - } - - if (hovered_window == NULL) - hovered_window = window; - IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. - if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) - hovered_window_ignoring_moving_window = window; - if (hovered_window && hovered_window_ignoring_moving_window) - break; - } - - g.HoveredWindow = hovered_window; - g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; -} - -bool ImGui::IsItemActive() -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId) - return g.ActiveId == g.LastItemData.ID; - return false; -} - -bool ImGui::IsItemActivated() -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId) - if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID) - return true; - return false; -} - -bool ImGui::IsItemDeactivated() -{ - ImGuiContext& g = *GImGui; - if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated) - return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; - return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID); -} - -bool ImGui::IsItemDeactivatedAfterEdit() -{ - ImGuiContext& g = *GImGui; - return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore)); -} - -// == GetItemID() == GetFocusID() -bool ImGui::IsItemFocused() -{ - ImGuiContext& g = *GImGui; - if (g.NavId != g.LastItemData.ID || g.NavId == 0) - return false; - return true; -} - -// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()! -// Most widgets have specific reactions based on mouse-up/down state, mouse position etc. -bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button) -{ - return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None); -} - -bool ImGui::IsItemToggledOpen() -{ - ImGuiContext& g = *GImGui; - return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; -} - -bool ImGui::IsItemToggledSelection() -{ - ImGuiContext& g = *GImGui; - return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; -} - -bool ImGui::IsAnyItemHovered() -{ - ImGuiContext& g = *GImGui; - return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0; -} - -bool ImGui::IsAnyItemActive() -{ - ImGuiContext& g = *GImGui; - return g.ActiveId != 0; -} - -bool ImGui::IsAnyItemFocused() -{ - ImGuiContext& g = *GImGui; - return g.NavId != 0 && !g.NavDisableHighlight; -} - -bool ImGui::IsItemVisible() -{ - ImGuiContext& g = *GImGui; - return g.CurrentWindow->ClipRect.Overlaps(g.LastItemData.Rect); -} - -bool ImGui::IsItemEdited() -{ - ImGuiContext& g = *GImGui; - return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0; -} - -// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. -// FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework. -void ImGui::SetItemAllowOverlap() -{ - ImGuiContext& g = *GImGui; - ImGuiID id = g.LastItemData.ID; - if (g.HoveredId == id) - g.HoveredIdAllowOverlap = true; - if (g.ActiveId == id) - g.ActiveIdAllowOverlap = true; -} - -void ImGui::SetItemUsingMouseWheel() -{ - ImGuiContext& g = *GImGui; - ImGuiID id = g.LastItemData.ID; - if (g.HoveredId == id) - g.HoveredIdUsingMouseWheel = true; - if (g.ActiveId == id) - g.ActiveIdUsingMouseWheel = true; -} - -void ImGui::SetActiveIdUsingNavAndKeys() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.ActiveId != 0); - g.ActiveIdUsingNavDirMask = ~(ImU32)0; - g.ActiveIdUsingNavInputMask = ~(ImU32)0; - g.ActiveIdUsingKeyInputMask.SetAllBits(); - NavMoveRequestCancel(); -} - -ImVec2 ImGui::GetItemRectMin() -{ - ImGuiContext& g = *GImGui; - return g.LastItemData.Rect.Min; -} - -ImVec2 ImGui::GetItemRectMax() -{ - ImGuiContext& g = *GImGui; - return g.LastItemData.Rect.Max; -} - -ImVec2 ImGui::GetItemRectSize() -{ - ImGuiContext& g = *GImGui; - return g.LastItemData.Rect.GetSize(); -} - -bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* parent_window = g.CurrentWindow; - - flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow; - flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag - - // Size - const ImVec2 content_avail = GetContentRegionAvail(); - ImVec2 size = ImFloor(size_arg); - const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00); - if (size.x <= 0.0f) - size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) - if (size.y <= 0.0f) - size.y = ImMax(content_avail.y + size.y, 4.0f); - SetNextWindowSize(size); - - // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value. - const char* temp_window_name; - if (name) - ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id); - else - ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id); - - const float backup_border_size = g.Style.ChildBorderSize; - if (!border) - g.Style.ChildBorderSize = 0.0f; - bool ret = Begin(temp_window_name, NULL, flags); - g.Style.ChildBorderSize = backup_border_size; - - ImGuiWindow* child_window = g.CurrentWindow; - child_window->ChildId = id; - child_window->AutoFitChildAxises = (ImS8)auto_fit_axises; - - // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually. - // While this is not really documented/defined, it seems that the expected thing to do. - if (child_window->BeginCount == 1) - parent_window->DC.CursorPos = child_window->Pos; - - // Process navigation-in immediately so NavInit can run on first frame - if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll)) - { - FocusWindow(child_window); - NavInitWindow(child_window, false); - SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item - g.ActiveIdSource = ImGuiInputSource_Nav; - } - return ret; -} - -bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags); -} - -bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) -{ - IM_ASSERT(id != 0); - return BeginChildEx(NULL, id, size_arg, border, extra_flags); -} - -void ImGui::EndChild() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - IM_ASSERT(g.WithinEndChild == false); - IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls - - g.WithinEndChild = true; - if (window->BeginCount > 1) - { - End(); - } - else - { - ImVec2 sz = window->Size; - if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f - sz.x = ImMax(4.0f, sz.x); - if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y)) - sz.y = ImMax(4.0f, sz.y); - End(); - - ImGuiWindow* parent_window = g.CurrentWindow; - ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); - ItemSize(sz); - if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) - { - ItemAdd(bb, window->ChildId); - RenderNavHighlight(bb, window->ChildId); - - // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying) - if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow) - RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); - } - else - { - // Not navigable into - ItemAdd(bb, 0); - } - if (g.HoveredWindow == window) - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; - } - g.WithinEndChild = false; - g.LogLinePosY = -FLT_MAX; // To enforce a carriage return -} - -// Helper to create a child window / scrolling region that looks like a normal widget frame. -bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags) -{ - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); - PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); - PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); - PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); - bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); - PopStyleVar(3); - PopStyleColor(); - return ret; -} - -void ImGui::EndChildFrame() -{ - EndChild(); -} - -static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) -{ - window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); - window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags); - window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); -} - -ImGuiWindow* ImGui::FindWindowByID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); -} - -ImGuiWindow* ImGui::FindWindowByName(const char* name) -{ - ImGuiID id = ImHashStr(name); - return FindWindowByID(id); -} - -static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) -{ - window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y)); - if (settings->Size.x > 0 && settings->Size.y > 0) - window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y)); - window->Collapsed = settings->Collapsed; -} - -static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags) -{ - ImGuiContext& g = *GImGui; - - const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0; - const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild; - if ((just_created || child_flag_changed) && !new_is_explicit_child) - { - IM_ASSERT(!g.WindowsFocusOrder.contains(window)); - g.WindowsFocusOrder.push_back(window); - window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); - } - else if (!just_created && child_flag_changed && new_is_explicit_child) - { - IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window); - for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++) - g.WindowsFocusOrder[n]->FocusOrder--; - g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder); - window->FocusOrder = -1; - } - window->IsExplicitChild = new_is_explicit_child; -} - -static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); - - // Create window the first time - ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); - window->Flags = flags; - g.WindowsById.SetVoidPtr(window->ID, window); - - // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - window->Pos = main_viewport->Pos + ImVec2(60, 60); - - // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. - if (!(flags & ImGuiWindowFlags_NoSavedSettings)) - if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) - { - // Retrieve settings from .ini file - window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); - SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); - ApplyWindowSettings(window, settings); - } - window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values - - if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) - { - window->AutoFitFramesX = window->AutoFitFramesY = 2; - window->AutoFitOnlyGrows = false; - } - else - { - if (window->Size.x <= 0.0f) - window->AutoFitFramesX = 2; - if (window->Size.y <= 0.0f) - window->AutoFitFramesY = 2; - window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); - } - - if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) - g.Windows.push_front(window); // Quite slow but rare and only once - else - g.Windows.push_back(window); - UpdateWindowInFocusOrderList(window, true, window->Flags); - - return window; -} - -static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired) -{ - ImGuiContext& g = *GImGui; - ImVec2 new_size = size_desired; - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) - { - // Using -1,-1 on either X/Y axis to preserve the current size. - ImRect cr = g.NextWindowData.SizeConstraintRect; - new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; - new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; - if (g.NextWindowData.SizeCallback) - { - ImGuiSizeCallbackData data; - data.UserData = g.NextWindowData.SizeCallbackUserData; - data.Pos = window->Pos; - data.CurrentSize = window->SizeFull; - data.DesiredSize = new_size; - g.NextWindowData.SizeCallback(&data); - new_size = data.DesiredSize; - } - new_size.x = IM_FLOOR(new_size.x); - new_size.y = IM_FLOOR(new_size.y); - } - - // Minimum size - if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) - { - ImGuiWindow* window_for_height = window; - const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight(); - new_size = ImMax(new_size, g.Style.WindowMinSize); - new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows - } - return new_size; -} - -static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal) -{ - bool preserve_old_content_sizes = false; - if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) - preserve_old_content_sizes = true; - else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0) - preserve_old_content_sizes = true; - if (preserve_old_content_sizes) - { - *content_size_current = window->ContentSize; - *content_size_ideal = window->ContentSizeIdeal; - return; - } - - content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x); - content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y); - content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x); - content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y); -} - -static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents) -{ - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); - ImVec2 size_pad = window->WindowPadding * 2.0f; - ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height); - if (window->Flags & ImGuiWindowFlags_Tooltip) - { - // Tooltip always resize - return size_desired; - } - else - { - // Maximum window size is determined by the viewport size or monitor size - const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0; - const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0; - ImVec2 size_min = style.WindowMinSize; - if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) - size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); - - // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows? - ImVec2 avail_size = ImGui::GetMainViewport()->Size; - ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f)); - - // When the window cannot fit all contents (either because of constraints, either because screen is too small), - // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. - ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); - bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - 0.0f < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); - bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_up_height < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); - if (will_have_scrollbar_x) - size_auto_fit.y += style.ScrollbarSize; - if (will_have_scrollbar_y) - size_auto_fit.x += style.ScrollbarSize; - return size_auto_fit; - } -} - -ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window) -{ - ImVec2 size_contents_current; - ImVec2 size_contents_ideal; - CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal); - ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal); - ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit); - return size_final; -} - -static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) -{ - if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) - return ImGuiCol_PopupBg; - if (window->Flags & ImGuiWindowFlags_ChildWindow) - return ImGuiCol_ChildBg; - return ImGuiCol_WindowBg; -} - -static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) -{ - ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left - ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right - ImVec2 size_expected = pos_max - pos_min; - ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected); - *out_pos = pos_min; - if (corner_norm.x == 0.0f) - out_pos->x -= (size_constrained.x - size_expected.x); - if (corner_norm.y == 0.0f) - out_pos->y -= (size_constrained.y - size_expected.y); - *out_size = size_constrained; -} - -// Data for resizing from corner -struct ImGuiResizeGripDef -{ - ImVec2 CornerPosN; - ImVec2 InnerDir; - int AngleMin12, AngleMax12; -}; -static const ImGuiResizeGripDef resize_grip_def[4] = -{ - { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right - { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left - { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused) - { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused) -}; - -// Data for resizing from borders -struct ImGuiResizeBorderDef -{ - ImVec2 InnerDir; - ImVec2 SegmentN1, SegmentN2; - float OuterAngle; -}; -static const ImGuiResizeBorderDef resize_border_def[4] = -{ - { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left - { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right - { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up - { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down -}; - -static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) -{ - ImRect rect = window->Rect(); - if (thickness == 0.0f) - rect.Max -= ImVec2(1, 1); - if (border_n == ImGuiDir_Left) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); } - if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); } - if (border_n == ImGuiDir_Up) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); } - if (border_n == ImGuiDir_Down) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); } - IM_ASSERT(0); - return ImRect(); -} - -// 0..3: corners (Lower-right, Lower-left, Unused, Unused) -ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n) -{ - IM_ASSERT(n >= 0 && n < 4); - ImGuiID id = window->ID; - id = ImHashStr("#RESIZE", 0, id); - id = ImHashData(&n, sizeof(int), id); - return id; -} - -// Borders (Left, Right, Up, Down) -ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir) -{ - IM_ASSERT(dir >= 0 && dir < 4); - int n = (int)dir + 4; - ImGuiID id = window->ID; - id = ImHashStr("#RESIZE", 0, id); - id = ImHashData(&n, sizeof(int), id); - return id; -} - -// Handle resize for: Resize Grips, Borders, Gamepad -// Return true when using auto-fit (double click on resize grip) -static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect) -{ - ImGuiContext& g = *GImGui; - ImGuiWindowFlags flags = window->Flags; - - if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) - return false; - if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window. - return false; - - bool ret_auto_fit = false; - const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0; - const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); - const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f); - const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f; - - ImVec2 pos_target(FLT_MAX, FLT_MAX); - ImVec2 size_target(FLT_MAX, FLT_MAX); - - // Resize grips and borders are on layer 1 - window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; - - // Manual resize grips - PushID("#RESIZE"); - for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) - { - const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN); - - // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window - bool hovered, held; - ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size); - if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); - if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); - ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID() - KeepAliveID(resize_grip_id); - ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); - //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); - if (hovered || held) - g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - - if (held && g.IO.MouseClickedCount[0] == 2 && resize_grip_n == 0) - { - // Manual auto-fit when double-clicking - size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); - ret_auto_fit = true; - ClearActiveID(); - } - else if (held) - { - // Resize from any of the four corners - // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, def.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX); - ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX); - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip - corner_target = ImClamp(corner_target, clamp_min, clamp_max); - CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target); - } - - // Only lower-left grip is visible before hovering/activating - if (resize_grip_n == 0 || held || hovered) - resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); - } - for (int border_n = 0; border_n < resize_border_count; border_n++) - { - const ImGuiResizeBorderDef& def = resize_border_def[border_n]; - const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; - - bool hovered, held; - ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING); - ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID() - KeepAliveID(border_id); - ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); - //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); - if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) - { - g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; - if (held) - *border_held = border_n; - } - if (held) - { - ImVec2 clamp_min(border_n == ImGuiDir_Right ? visibility_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down ? visibility_rect.Min.y : -FLT_MAX); - ImVec2 clamp_max(border_n == ImGuiDir_Left ? visibility_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? visibility_rect.Max.y : +FLT_MAX); - ImVec2 border_target = window->Pos; - border_target[axis] = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; - border_target = ImClamp(border_target, clamp_min, clamp_max); - CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target); - } - } - PopID(); - - // Restore nav layer - window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - - // Navigation resize (keyboard/gamepad) - if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) - { - ImVec2 nav_resize_delta; - if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiNavReadMode_Down); - if (g.NavInputSource == ImGuiInputSource_Gamepad) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiNavReadMode_Down); - if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) - { - const float NAV_RESIZE_SPEED = 600.0f; - nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); - nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size); - g.NavWindowingToggleLayer = false; - g.NavDisableMouseHover = true; - resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); - // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. - size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); - } - } - - // Apply back modified position/size to window - if (size_target.x != FLT_MAX) - { - window->SizeFull = size_target; - MarkIniSettingsDirty(window); - } - if (pos_target.x != FLT_MAX) - { - window->Pos = ImFloor(pos_target); - MarkIniSettingsDirty(window); - } - - window->Size = window->SizeFull; - return ret_auto_fit; -} - -static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect) -{ - ImGuiContext& g = *GImGui; - ImVec2 size_for_clamping = window->Size; - if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) - size_for_clamping.y = window->TitleBarHeight(); - window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max); -} - -static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - float rounding = window->WindowRounding; - float border_size = window->WindowBorderSize; - if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground)) - window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); - - int border_held = window->ResizeBorderHeld; - if (border_held != -1) - { - const ImGuiResizeBorderDef& def = resize_border_def[border_held]; - ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f); - window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle); - window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); - window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual - } - if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) - { - float y = window->Pos.y + window->TitleBarHeight() - 1; - window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize); - } -} - -// Draw background and borders -// Draw and handle scrollbars -void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) -{ - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - ImGuiWindowFlags flags = window->Flags; - - // Ensure that ScrollBar doesn't read last frame's SkipItems - IM_ASSERT(window->BeginCount == 0); - window->SkipItems = false; - - // Draw window + handle manual resize - // As we highlight the title bar when want_focus is set, multiple reappearing windows will have have their title bar highlighted on their reappearing frame. - const float window_rounding = window->WindowRounding; - const float window_border_size = window->WindowBorderSize; - if (window->Collapsed) - { - // Title bar only - float backup_border_size = style.FrameBorderSize; - g.Style.FrameBorderSize = window->WindowBorderSize; - ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed); - RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); - g.Style.FrameBorderSize = backup_border_size; - } - else - { - // Window background - if (!(flags & ImGuiWindowFlags_NoBackground)) - { - ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window)); - bool override_alpha = false; - float alpha = 1.0f; - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) - { - alpha = g.NextWindowData.BgAlphaVal; - override_alpha = true; - } - if (override_alpha) - bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); - window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); - } - - // Title bar - if (!(flags & ImGuiWindowFlags_NoTitleBar)) - { - ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop); - } - - // Menu bar - if (flags & ImGuiWindowFlags_MenuBar) - { - ImRect menu_bar_rect = window->MenuBarRect(); - menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. - window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop); - if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) - window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); - } - - // Scrollbars - if (window->ScrollbarX) - Scrollbar(ImGuiAxis_X); - if (window->ScrollbarY) - Scrollbar(ImGuiAxis_Y); - - // Render resize grips (after their input handling so we don't have a frame of latency) - if (!(flags & ImGuiWindowFlags_NoResize)) - { - for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) - { - const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); - window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); - window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); - } - } - - // Borders - RenderWindowOuterBorders(window); - } -} - -// Render title text, collapse button, close button -void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) -{ - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - ImGuiWindowFlags flags = window->Flags; - - const bool has_close_button = (p_open != NULL); - const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); - - // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) - // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref? - const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; - window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; - - // Layout buttons - // FIXME: Would be nice to generalize the subtleties expressed here into reusable code. - float pad_l = style.FramePadding.x; - float pad_r = style.FramePadding.x; - float button_sz = g.FontSize; - ImVec2 close_button_pos; - ImVec2 collapse_button_pos; - if (has_close_button) - { - pad_r += button_sz; - close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); - } - if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right) - { - pad_r += button_sz; - collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); - } - if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left) - { - collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y); - pad_l += button_sz; - } - - // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) - if (has_collapse_button) - if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos)) - window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function - - // Close button - if (has_close_button) - if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) - *p_open = false; - - window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - g.CurrentItemFlags = item_flags_backup; - - // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) - // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. - const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f; - const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); - - // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, - // while uncentered title text will still reach edges correctly. - if (pad_l > style.FramePadding.x) - pad_l += g.Style.ItemInnerSpacing.x; - if (pad_r > style.FramePadding.x) - pad_r += g.Style.ItemInnerSpacing.x; - if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f) - { - float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center - float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x); - pad_l = ImMax(pad_l, pad_extend * centerness); - pad_r = ImMax(pad_r, pad_extend * centerness); - } - - ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y); - ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y); - if (flags & ImGuiWindowFlags_UnsavedDocument) - { - ImVec2 marker_pos; - marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x); - marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f; - if (marker_pos.x > layout_r.Min.x) - { - RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text)); - clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f)); - } - } - //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] - //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] - RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); -} - -void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) -{ - window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; - if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) - window->RootWindow = parent_window->RootWindow; - if (parent_window && (flags & ImGuiWindowFlags_Popup)) - window->RootWindowPopupTree = parent_window->RootWindowPopupTree; - if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) - window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; - while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) - { - IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL); - window->RootWindowForNav = window->RootWindowForNav->ParentWindow; - } -} - -// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) -// should be positioned behind that modal window, unless the window was created inside the modal begin-stack. -// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. -// - Window // FindBlockingModal() returns Modal1 -// - Window // .. returns Modal1 -// - Modal1 // .. returns Modal2 -// - Window // .. returns Modal2 -// - Window // .. returns Modal2 -// - Modal2 // .. returns Modal2 -static ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (g.OpenPopupStack.Size <= 0) - return NULL; - - // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal. - for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) - { - ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window; - if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal)) - continue; - if (!popup_window->Active && !popup_window->WasActive) // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows. - continue; - if (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed. - break; - for (ImGuiWindow* parent = popup_window->ParentWindowInBeginStack->RootWindow; parent != NULL; parent = parent->ParentWindowInBeginStack->RootWindow) - if (IsWindowWithinBeginStackOf(window, parent)) - return popup_window; // Place window above its begin stack parent. - } - return NULL; -} - -// Push a new Dear ImGui window to add widgets to. -// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. -// - Begin/End can be called multiple times during the frame with the same window name to append content. -// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). -// You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file. -// - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned. -// - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed. -bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required - IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame() - IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet - - // Find or create - ImGuiWindow* window = FindWindowByName(name); - const bool window_just_created = (window == NULL); - if (window_just_created) - window = CreateNewWindow(name, flags); - else - UpdateWindowInFocusOrderList(window, window_just_created, flags); - - // Automatically disable manual moving/resizing when NoInputs is set - if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) - flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; - - if (flags & ImGuiWindowFlags_NavFlattened) - IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); - - const int current_frame = g.FrameCount; - const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); - window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow); - - // Update the Appearing flag - bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on - if (flags & ImGuiWindowFlags_Popup) - { - ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; - window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed - window_just_activated_by_user |= (window != popup_ref.Window); - } - window->Appearing = window_just_activated_by_user; - if (window->Appearing) - SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); - - // Update Flags, LastFrameActive, BeginOrderXXX fields - if (first_begin_of_the_frame) - { - window->Flags = (ImGuiWindowFlags)flags; - window->LastFrameActive = current_frame; - window->LastTimeActive = (float)g.Time; - window->BeginOrderWithinParent = 0; - window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++); - } - else - { - flags = window->Flags; - } - - // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack - ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; - ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; - IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); - - // We allow window memory to be compacted so recreate the base stack when needed. - if (window->IDStack.Size == 0) - window->IDStack.push_back(window->ID); - - // Add to stack - // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() - g.CurrentWindow = window; - ImGuiWindowStackData window_stack_data; - window_stack_data.Window = window; - window_stack_data.ParentLastItemDataBackup = g.LastItemData; - window_stack_data.StackSizesOnBegin.SetToCurrentState(); - g.CurrentWindowStack.push_back(window_stack_data); - g.CurrentWindow = NULL; - if (flags & ImGuiWindowFlags_ChildMenu) - g.BeginMenuCount++; - - if (flags & ImGuiWindowFlags_Popup) - { - ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; - popup_ref.Window = window; - popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent; - g.BeginPopupStack.push_back(popup_ref); - window->PopupId = popup_ref.PopupId; - } - - // Update ->RootWindow and others pointers (before any possible call to FocusWindow) - if (first_begin_of_the_frame) - { - UpdateWindowParentAndRootLinks(window, flags, parent_window); - window->ParentWindowInBeginStack = parent_window_in_stack; - } - - // Process SetNextWindow***() calls - // (FIXME: Consider splitting the HasXXX flags into X/Y components - bool window_pos_set_by_api = false; - bool window_size_x_set_by_api = false, window_size_y_set_by_api = false; - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) - { - window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0; - if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f) - { - // May be processed on the next frame if this is our first frame and we are measuring size - // FIXME: Look into removing the branch so everything can go through this same code path for consistency. - window->SetWindowPosVal = g.NextWindowData.PosVal; - window->SetWindowPosPivot = g.NextWindowData.PosPivotVal; - window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); - } - else - { - SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond); - } - } - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) - { - window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f); - window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f); - SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond); - } - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll) - { - if (g.NextWindowData.ScrollVal.x >= 0.0f) - { - window->ScrollTarget.x = g.NextWindowData.ScrollVal.x; - window->ScrollTargetCenterRatio.x = 0.0f; - } - if (g.NextWindowData.ScrollVal.y >= 0.0f) - { - window->ScrollTarget.y = g.NextWindowData.ScrollVal.y; - window->ScrollTargetCenterRatio.y = 0.0f; - } - } - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize) - window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal; - else if (first_begin_of_the_frame) - window->ContentSizeExplicit = ImVec2(0.0f, 0.0f); - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed) - SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus) - FocusWindow(window); - if (window->Appearing) - SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); - - // When reusing window again multiple times a frame, just append content (don't need to setup again) - if (first_begin_of_the_frame) - { - // Initialize - const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) - const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); - window->Active = true; - window->HasCloseButton = (p_open != NULL); - window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX); - window->IDStack.resize(1); - window->DrawList->_ResetForNewFrame(); - window->DC.CurrentTableIdx = -1; - - // Restore buffer capacity when woken from a compacted state, to avoid - if (window->MemoryCompacted) - GcAwakeTransientWindowBuffers(window); - - // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). - // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere. - bool window_title_visible_elsewhere = false; - if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB - window_title_visible_elsewhere = true; - if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) - { - size_t buf_len = (size_t)window->NameBufLen; - window->Name = ImStrdupcpy(window->Name, &buf_len, name); - window->NameBufLen = (int)buf_len; - } - - // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS - - // Update contents size from last frame for auto-fitting (or use explicit size) - CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal); - if (window->HiddenFramesCanSkipItems > 0) - window->HiddenFramesCanSkipItems--; - if (window->HiddenFramesCannotSkipItems > 0) - window->HiddenFramesCannotSkipItems--; - if (window->HiddenFramesForRenderOnly > 0) - window->HiddenFramesForRenderOnly--; - - // Hide new windows for one frame until they calculate their size - if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) - window->HiddenFramesCannotSkipItems = 1; - - // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) - // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size. - if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) - { - window->HiddenFramesCannotSkipItems = 1; - if (flags & ImGuiWindowFlags_AlwaysAutoResize) - { - if (!window_size_x_set_by_api) - window->Size.x = window->SizeFull.x = 0.f; - if (!window_size_y_set_by_api) - window->Size.y = window->SizeFull.y = 0.f; - window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f); - } - } - - // SELECT VIEWPORT - // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style) - - ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); - SetWindowViewport(window, viewport); - SetCurrentWindow(window); - - // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies) - - if (flags & ImGuiWindowFlags_ChildWindow) - window->WindowBorderSize = style.ChildBorderSize; - else - window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; - window->WindowPadding = style.WindowPadding; - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) - window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); - - // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size. - window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); - window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; - - // Collapse window by double-clicking on title bar - // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing - if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) - { - // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar. - ImRect title_bar_rect = window->TitleBarRect(); - if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseClickedCount[0] == 2) - window->WantCollapseToggle = true; - if (window->WantCollapseToggle) - { - window->Collapsed = !window->Collapsed; - MarkIniSettingsDirty(window); - } - } - else - { - window->Collapsed = false; - } - window->WantCollapseToggle = false; - - // SIZE - - // Calculate auto-fit size, handle automatic resize - const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal); - bool use_current_size_for_scrollbar_x = window_just_created; - bool use_current_size_for_scrollbar_y = window_just_created; - if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed) - { - // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. - if (!window_size_x_set_by_api) - { - window->SizeFull.x = size_auto_fit.x; - use_current_size_for_scrollbar_x = true; - } - if (!window_size_y_set_by_api) - { - window->SizeFull.y = size_auto_fit.y; - use_current_size_for_scrollbar_y = true; - } - } - else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) - { - // Auto-fit may only grow window during the first few frames - // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. - if (!window_size_x_set_by_api && window->AutoFitFramesX > 0) - { - window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; - use_current_size_for_scrollbar_x = true; - } - if (!window_size_y_set_by_api && window->AutoFitFramesY > 0) - { - window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; - use_current_size_for_scrollbar_y = true; - } - if (!window->Collapsed) - MarkIniSettingsDirty(window); - } - - // Apply minimum/maximum window size constraints and final size - window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull); - window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; - - // Decoration size - const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); - - // POSITION - - // Popup latch its initial position, will position itself when it appears next frame - if (window_just_activated_by_user) - { - window->AutoPosLastDirection = ImGuiDir_None; - if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos() - window->Pos = g.BeginPopupStack.back().OpenPopupPos; - } - - // Position child window - if (flags & ImGuiWindowFlags_ChildWindow) - { - IM_ASSERT(parent_window && parent_window->Active); - window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size; - parent_window->DC.ChildWindows.push_back(window); - if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip) - window->Pos = parent_window->DC.CursorPos; - } - - const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0); - if (window_pos_with_pivot) - SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) - else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) - window->Pos = FindBestWindowPosForPopup(window); - else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) - window->Pos = FindBestWindowPosForPopup(window); - else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) - window->Pos = FindBestWindowPosForPopup(window); - - // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) - // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. - ImRect viewport_rect(viewport->GetMainRect()); - ImRect viewport_work_rect(viewport->GetWorkRect()); - ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); - ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding); - - // Clamp position/size so window stays visible within its viewport or monitor - // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. - if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) - if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f) - ClampWindowRect(window, visibility_rect); - window->Pos = ImFloor(window->Pos); - - // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) - // Large values tend to lead to variety of artifacts and are not recommended. - window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; - - // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts. - //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar)) - // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f); - - // Apply window focus (new and reactivated windows are moved to front) - bool want_focus = false; - if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) - { - if (flags & ImGuiWindowFlags_Popup) - want_focus = true; - else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) - want_focus = true; - - ImGuiWindow* modal = GetTopMostPopupModal(); - if (modal != NULL && !IsWindowWithinBeginStackOf(window, modal)) - { - // Avoid focusing a window that is created outside of active modal. This will prevent active modal from being closed. - // Since window is not focused it would reappear at the same display position like the last time it was visible. - // In case of completely new windows it would go to the top (over current modal), but input to such window would still be blocked by modal. - // Position window behind a modal that is not a begin-parent of this window. - want_focus = false; - if (window == window->RootWindow) - { - ImGuiWindow* blocking_modal = FindBlockingModal(window); - IM_ASSERT(blocking_modal != NULL); - BringWindowToDisplayBehind(window, blocking_modal); - } - } - } - - // [Test Engine] Register whole window in the item system -#ifdef IMGUI_ENABLE_TEST_ENGINE - if (g.TestEngineHookItems) - { - IM_ASSERT(window->IDStack.Size == 1); - window->IDStack.Size = 0; - IMGUI_TEST_ENGINE_ITEM_ADD(window->Rect(), window->ID); - IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0); - window->IDStack.Size = 1; - } -#endif - - // Handle manual resize: Resize Grips, Borders, Gamepad - int border_held = -1; - ImU32 resize_grip_col[4] = {}; - const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. - const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); - if (!window->Collapsed) - if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) - use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true; - window->ResizeBorderHeld = (signed char)border_held; - - // SCROLLBAR VISIBILITY - - // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size). - if (!window->Collapsed) - { - // When reading the current size we need to read it after size constraints have been applied. - // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again. - ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height); - ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes; - ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f; - float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x; - float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; - //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons? - window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); - window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); - if (window->ScrollbarX && !window->ScrollbarY) - window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar); - window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); - } - - // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING) - // Update various regions. Variables they depends on should be set above in this function. - // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame. - - // Outer rectangle - // Not affected by window border size. Used by: - // - FindHoveredWindow() (w/ extra padding when border resize is enabled) - // - Begin() initial clipping rect for drawing window background and borders. - // - Begin() clipping whole child - const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect; - const ImRect outer_rect = window->Rect(); - const ImRect title_bar_rect = window->TitleBarRect(); - window->OuterRectClipped = outer_rect; - window->OuterRectClipped.ClipWith(host_rect); - - // Inner rectangle - // Not affected by window border size. Used by: - // - InnerClipRect - // - ScrollToRectEx() - // - NavUpdatePageUpPageDown() - // - Scrollbar() - window->InnerRect.Min.x = window->Pos.x; - window->InnerRect.Min.y = window->Pos.y + decoration_up_height; - window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x; - window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y; - - // Inner clipping rectangle. - // Will extend a little bit outside the normal work region. - // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space. - // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. - // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. - // Affected by window/frame border size. Used by: - // - Begin() initial clip rect - float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); - window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); - window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size); - window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); - window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize); - window->InnerClipRect.ClipWithFull(host_rect); - - // Default item width. Make it proportional to window size if window manually resizes - if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) - window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f); - else - window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f); - - // SCROLLING - - // Lock down maximum scrolling - // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate - // for right/bottom aligned items without creating a scrollbar. - window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth()); - window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight()); - - // Apply scrolling - window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window); - window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); - - // DRAWING - - // Setup draw list and outer clipping rectangle - IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0); - window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); - PushClipRect(host_rect.Min, host_rect.Max, false); - - // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) - // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. - // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493) - { - bool render_decorations_in_parent = false; - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) - { - // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here) - // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs - ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL; - bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false; - bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0; - if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping) - render_decorations_in_parent = true; - } - if (render_decorations_in_parent) - window->DrawList = parent_window->DrawList; - - // Handle title bar, scrollbar, resize grips and resize borders - const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; - const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); - RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size); - - if (render_decorations_in_parent) - window->DrawList = &window->DrawListInst; - } - - // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) - - // Work rectangle. - // Affected by window padding and border size. Used by: - // - Columns() for right-most edge - // - TreeNode(), CollapsingHeader() for right-most edge - // - BeginTabBar() for right-most edge - const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar); - const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar); - const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x)); - const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); - window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize)); - window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize)); - window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x; - window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y; - window->ParentWorkRect = window->WorkRect; - - // [LEGACY] Content Region - // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. - // Used by: - // - Mouse wheel scrolling + many other things - window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; - window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height; - window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x)); - window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); - - // Setup drawing context - // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.) - window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; - window->DC.GroupOffset.x = 0.0f; - window->DC.ColumnsOffset.x = 0.0f; - - // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount. - // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64. - double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DC.ColumnsOffset.x; - double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + decoration_up_height; - window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y); - window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y)); - window->DC.CursorPos = window->DC.CursorStartPos; - window->DC.CursorPosPrevLine = window->DC.CursorPos; - window->DC.CursorMaxPos = window->DC.CursorStartPos; - window->DC.IdealMaxPos = window->DC.CursorStartPos; - window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f); - window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; - window->DC.IsSameLine = false; - - window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; - window->DC.NavLayersActiveMaskNext = 0x00; - window->DC.NavHideHighlightOneFrame = false; - window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); - - window->DC.MenuBarAppending = false; - window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user); - window->DC.TreeDepth = 0; - window->DC.TreeJumpToParentOnPopMask = 0x00; - window->DC.ChildWindows.resize(0); - window->DC.StateStorage = &window->StateStorage; - window->DC.CurrentColumns = NULL; - window->DC.LayoutType = ImGuiLayoutType_Vertical; - window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; - - window->DC.ItemWidth = window->ItemWidthDefault; - window->DC.TextWrapPos = -1.0f; // disabled - window->DC.ItemWidthStack.resize(0); - window->DC.TextWrapPosStack.resize(0); - - if (window->AutoFitFramesX > 0) - window->AutoFitFramesX--; - if (window->AutoFitFramesY > 0) - window->AutoFitFramesY--; - - // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) - if (want_focus) - { - FocusWindow(window); - NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls - } - - // Title bar - if (!(flags & ImGuiWindowFlags_NoTitleBar)) - RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open); - - // Clear hit test shape every frame - window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0; - - // Pressing CTRL+C while holding on a window copy its content to the clipboard - // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope. - // Maybe we can support CTRL+C on every element? - /* - //if (g.NavWindow == window && g.ActiveId == 0) - if (g.ActiveId == window->MoveId) - if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) - LogToClipboard(); - */ - - // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). - // This is useful to allow creating context menus on title bar only, etc. - SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); - - // [Test Engine] Register title bar / tab - if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) - IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.Rect, g.LastItemData.ID); - } - else - { - // Append - SetCurrentWindow(window); - } - - // Pull/inherit current state - window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595 - - PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); - - // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused) - window->WriteAccessed = false; - window->BeginCount++; - g.NextWindowData.ClearFlags(); - - // Update visibility - if (first_begin_of_the_frame) - { - if (flags & ImGuiWindowFlags_ChildWindow) - { - // Child window can be out of sight and have "negative" clip windows. - // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). - IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); - if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow?? - { - const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); - if (!g.LogEnabled && !nav_request) - if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) - window->HiddenFramesCanSkipItems = 1; - } - - // Hide along with parent or if parent is collapsed - if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) - window->HiddenFramesCanSkipItems = 1; - if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0)) - window->HiddenFramesCannotSkipItems = 1; - } - - // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point) - if (style.Alpha <= 0.0f) - window->HiddenFramesCanSkipItems = 1; - - // Update the Hidden flag - bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); - window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0); - - // Disable inputs for requested number of frames - if (window->DisableInputsFrames > 0) - { - window->DisableInputsFrames--; - window->Flags |= ImGuiWindowFlags_NoInputs; - } - - // Update the SkipItems flag, used to early out of all items functions (no layout required) - bool skip_items = false; - if (window->Collapsed || !window->Active || hidden_regular) - if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) - skip_items = true; - window->SkipItems = skip_items; - } - - return !window->SkipItems; -} - -void ImGui::End() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Error checking: verify that user hasn't called End() too many times! - if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow) - { - IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!"); - return; - } - IM_ASSERT(g.CurrentWindowStack.Size > 0); - - // Error checking: verify that user doesn't directly call End() on a child window. - if (window->Flags & ImGuiWindowFlags_ChildWindow) - IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!"); - - // Close anything that is open - if (window->DC.CurrentColumns) - EndColumns(); - PopClipRect(); // Inner window clip rectangle - - // Stop logging - if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging - LogFinish(); - - // Pop from window stack - g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; - if (window->Flags & ImGuiWindowFlags_ChildMenu) - g.BeginMenuCount--; - if (window->Flags & ImGuiWindowFlags_Popup) - g.BeginPopupStack.pop_back(); - g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState(); - g.CurrentWindowStack.pop_back(); - SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); -} - -void ImGui::BringWindowToFocusFront(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(window == window->RootWindow); - - const int cur_order = window->FocusOrder; - IM_ASSERT(g.WindowsFocusOrder[cur_order] == window); - if (g.WindowsFocusOrder.back() == window) - return; - - const int new_order = g.WindowsFocusOrder.Size - 1; - for (int n = cur_order; n < new_order; n++) - { - g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1]; - g.WindowsFocusOrder[n]->FocusOrder--; - IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n); - } - g.WindowsFocusOrder[new_order] = window; - window->FocusOrder = (short)new_order; -} - -void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* current_front_window = g.Windows.back(); - if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better) - return; - for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window - if (g.Windows[i] == window) - { - memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*)); - g.Windows[g.Windows.Size - 1] = window; - break; - } -} - -void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (g.Windows[0] == window) - return; - for (int i = 0; i < g.Windows.Size; i++) - if (g.Windows[i] == window) - { - memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*)); - g.Windows[0] = window; - break; - } -} - -void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window) -{ - IM_ASSERT(window != NULL && behind_window != NULL); - ImGuiContext& g = *GImGui; - window = window->RootWindow; - behind_window = behind_window->RootWindow; - int pos_wnd = FindWindowDisplayIndex(window); - int pos_beh = FindWindowDisplayIndex(behind_window); - if (pos_wnd < pos_beh) - { - size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*); - memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes); - g.Windows[pos_beh - 1] = window; - } - else - { - size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*); - memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes); - g.Windows[pos_beh] = window; - } -} - -int ImGui::FindWindowDisplayIndex(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - return g.Windows.index_from_ptr(g.Windows.find(window)); -} - -// Moving window to front of display and set focus (which happens to be back of our sorted list) -void ImGui::FocusWindow(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - - if (g.NavWindow != window) - { - SetNavWindow(window); - if (window && g.NavDisableMouseHover) - g.NavMousePosDirty = true; - g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId - g.NavLayer = ImGuiNavLayer_Main; - g.NavFocusScopeId = 0; - g.NavIdIsAlive = false; - } - - // Close popups if any - ClosePopupsOverWindow(window, false); - - // Move the root window to the top of the pile - IM_ASSERT(window == NULL || window->RootWindow != NULL); - ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop - ImGuiWindow* display_front_window = window ? window->RootWindow : NULL; - - // Steal active widgets. Some of the cases it triggers includes: - // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run. - // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId) - if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window) - if (!g.ActiveIdNoClearOnFocusLoss) - ClearActiveID(); - - // Passing NULL allow to disable keyboard focus - if (!window) - return; - - // Bring to front - BringWindowToFocusFront(focus_front_window); - if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) - BringWindowToDisplayFront(display_front_window); -} - -void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) -{ - ImGuiContext& g = *GImGui; - int start_idx = g.WindowsFocusOrder.Size - 1; - if (under_this_window != NULL) - { - // Aim at root window behind us, if we are in a child window that's our own root (see #4640) - int offset = -1; - while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow) - { - under_this_window = under_this_window->ParentWindow; - offset = 0; - } - start_idx = FindWindowFocusIndex(under_this_window) + offset; - } - for (int i = start_idx; i >= 0; i--) - { - // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. - ImGuiWindow* window = g.WindowsFocusOrder[i]; - IM_ASSERT(window == window->RootWindow); - if (window != ignore_window && window->WasActive) - if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) - { - ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); - FocusWindow(focus_window); - return; - } - } - FocusWindow(NULL); -} - -// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only. -void ImGui::SetCurrentFont(ImFont* font) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? - IM_ASSERT(font->Scale > 0.0f); - g.Font = font; - g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale); - g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; - - ImFontAtlas* atlas = g.Font->ContainerAtlas; - g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; - g.DrawListSharedData.TexUvLines = atlas->TexUvLines; - g.DrawListSharedData.Font = g.Font; - g.DrawListSharedData.FontSize = g.FontSize; -} - -void ImGui::PushFont(ImFont* font) -{ - ImGuiContext& g = *GImGui; - if (!font) - font = GetDefaultFont(); - SetCurrentFont(font); - g.FontStack.push_back(font); - g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID); -} - -void ImGui::PopFont() -{ - ImGuiContext& g = *GImGui; - g.CurrentWindow->DrawList->PopTextureID(); - g.FontStack.pop_back(); - SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back()); -} - -void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) -{ - ImGuiContext& g = *GImGui; - ImGuiItemFlags item_flags = g.CurrentItemFlags; - IM_ASSERT(item_flags == g.ItemFlagsStack.back()); - if (enabled) - item_flags |= option; - else - item_flags &= ~option; - g.CurrentItemFlags = item_flags; - g.ItemFlagsStack.push_back(item_flags); -} - -void ImGui::PopItemFlag() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack. - g.ItemFlagsStack.pop_back(); - g.CurrentItemFlags = g.ItemFlagsStack.back(); -} - -// BeginDisabled()/EndDisabled() -// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) -// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. -// - Feedback welcome at https://github.com/ocornut/imgui/issues/211 -// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. -// - Optimized shortcuts instead of PushStyleVar() + PushItemFlag() -void ImGui::BeginDisabled(bool disabled) -{ - ImGuiContext& g = *GImGui; - bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; - if (!was_disabled && disabled) - { - g.DisabledAlphaBackup = g.Style.Alpha; - g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); - } - if (was_disabled || disabled) - g.CurrentItemFlags |= ImGuiItemFlags_Disabled; - g.ItemFlagsStack.push_back(g.CurrentItemFlags); - g.DisabledStackSize++; -} - -void ImGui::EndDisabled() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.DisabledStackSize > 0); - g.DisabledStackSize--; - bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; - //PopItemFlag(); - g.ItemFlagsStack.pop_back(); - g.CurrentItemFlags = g.ItemFlagsStack.back(); - if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0) - g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar(); -} - -// FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. -void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) -{ - PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus); -} - -void ImGui::PopAllowKeyboardFocus() -{ - PopItemFlag(); -} - -void ImGui::PushButtonRepeat(bool repeat) -{ - PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); -} - -void ImGui::PopButtonRepeat() -{ - PopItemFlag(); -} - -void ImGui::PushTextWrapPos(float wrap_pos_x) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos); - window->DC.TextWrapPos = wrap_pos_x; -} - -void ImGui::PopTextWrapPos() -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.TextWrapPos = window->DC.TextWrapPosStack.back(); - window->DC.TextWrapPosStack.pop_back(); -} - -static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy) -{ - ImGuiWindow* last_window = NULL; - while (last_window != window) - { - last_window = window; - window = window->RootWindow; - if (popup_hierarchy) - window = window->RootWindowPopupTree; - } - return window; -} - -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy) -{ - ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy); - if (window_root == potential_parent) - return true; - while (window != NULL) - { - if (window == potential_parent) - return true; - if (window == window_root) // end of chain - return false; - window = window->ParentWindow; - } - return false; -} - -bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent) -{ - if (window->RootWindow == potential_parent) - return true; - while (window != NULL) - { - if (window == potential_parent) - return true; - window = window->ParentWindowInBeginStack; - } - return false; -} - -bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) -{ - ImGuiContext& g = *GImGui; - - // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array - const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below); - if (display_layer_delta != 0) - return display_layer_delta > 0; - - for (int i = g.Windows.Size - 1; i >= 0; i--) - { - ImGuiWindow* candidate_window = g.Windows[i]; - if (candidate_window == potential_above) - return true; - if (candidate_window == potential_below) - return false; - } - return false; -} - -bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) -{ - IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function - ImGuiContext& g = *GImGui; - ImGuiWindow* ref_window = g.HoveredWindow; - ImGuiWindow* cur_window = g.CurrentWindow; - if (ref_window == NULL) - return false; - - if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) - { - IM_ASSERT(cur_window); // Not inside a Begin()/End() - const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0; - if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); - - bool result; - if (flags & ImGuiHoveredFlags_ChildWindows) - result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy); - else - result = (ref_window == cur_window); - if (!result) - return false; - } - - if (!IsWindowContentHoverable(ref_window, flags)) - return false; - if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) - return false; - return true; -} - -bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* ref_window = g.NavWindow; - ImGuiWindow* cur_window = g.CurrentWindow; - - if (ref_window == NULL) - return false; - if (flags & ImGuiFocusedFlags_AnyWindow) - return true; - - IM_ASSERT(cur_window); // Not inside a Begin()/End() - const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; - if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); - - if (flags & ImGuiHoveredFlags_ChildWindows) - return IsWindowChildOf(ref_window, cur_window, popup_hierarchy); - else - return (ref_window == cur_window); -} - -// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) -// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically. -// If you want a window to never be focused, you may use the e.g. NoInputs flag. -bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) -{ - return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus); -} - -float ImGui::GetWindowWidth() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->Size.x; -} - -float ImGui::GetWindowHeight() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->Size.y; -} - -ImVec2 ImGui::GetWindowPos() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - return window->Pos; -} - -void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) -{ - // Test condition (NB: bit 0 is always true) and clear flags for next time - if (cond && (window->SetWindowPosAllowFlags & cond) == 0) - return; - - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); - window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX); - - // Set - const ImVec2 old_pos = window->Pos; - window->Pos = ImFloor(pos); - ImVec2 offset = window->Pos - old_pos; - if (offset.x == 0.0f && offset.y == 0.0f) - return; - MarkIniSettingsDirty(window); - window->DC.CursorPos += offset; // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor - window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected. - window->DC.IdealMaxPos += offset; - window->DC.CursorStartPos += offset; -} - -void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - SetWindowPos(window, pos, cond); -} - -void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond) -{ - if (ImGuiWindow* window = FindWindowByName(name)) - SetWindowPos(window, pos, cond); -} - -ImVec2 ImGui::GetWindowSize() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->Size; -} - -void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond) -{ - // Test condition (NB: bit 0 is always true) and clear flags for next time - if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) - return; - - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); - - // Set - ImVec2 old_size = window->SizeFull; - window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0; - window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0; - if (size.x <= 0.0f) - window->AutoFitOnlyGrows = false; - else - window->SizeFull.x = IM_FLOOR(size.x); - if (size.y <= 0.0f) - window->AutoFitOnlyGrows = false; - else - window->SizeFull.y = IM_FLOOR(size.y); - if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y) - MarkIniSettingsDirty(window); -} - -void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond) -{ - SetWindowSize(GImGui->CurrentWindow, size, cond); -} - -void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond) -{ - if (ImGuiWindow* window = FindWindowByName(name)) - SetWindowSize(window, size, cond); -} - -void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond) -{ - // Test condition (NB: bit 0 is always true) and clear flags for next time - if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) - return; - window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); - - // Set - window->Collapsed = collapsed; -} - -void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size) -{ - IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters - window->HitTestHoleSize = ImVec2ih(size); - window->HitTestHoleOffset = ImVec2ih(pos - window->Pos); -} - -void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond) -{ - SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond); -} - -bool ImGui::IsWindowCollapsed() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->Collapsed; -} - -bool ImGui::IsWindowAppearing() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->Appearing; -} - -void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond) -{ - if (ImGuiWindow* window = FindWindowByName(name)) - SetWindowCollapsed(window, collapsed, cond); -} - -void ImGui::SetWindowFocus() -{ - FocusWindow(GImGui->CurrentWindow); -} - -void ImGui::SetWindowFocus(const char* name) -{ - if (name) - { - if (ImGuiWindow* window = FindWindowByName(name)) - FocusWindow(window); - } - else - { - FocusWindow(NULL); - } -} - -void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos; - g.NextWindowData.PosVal = pos; - g.NextWindowData.PosPivotVal = pivot; - g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; -} - -void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize; - g.NextWindowData.SizeVal = size; - g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always; -} - -void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data) -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; - g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max); - g.NextWindowData.SizeCallback = custom_callback; - g.NextWindowData.SizeCallbackUserData = custom_callback_user_data; -} - -// Content size = inner scrollable rectangle, padded with WindowPadding. -// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item. -void ImGui::SetNextWindowContentSize(const ImVec2& size) -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize; - g.NextWindowData.ContentSizeVal = ImFloor(size); -} - -void ImGui::SetNextWindowScroll(const ImVec2& scroll) -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll; - g.NextWindowData.ScrollVal = scroll; -} - -void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed; - g.NextWindowData.CollapsedVal = collapsed; - g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always; -} - -void ImGui::SetNextWindowFocus() -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus; -} - -void ImGui::SetNextWindowBgAlpha(float alpha) -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha; - g.NextWindowData.BgAlphaVal = alpha; -} - -ImDrawList* ImGui::GetWindowDrawList() -{ - ImGuiWindow* window = GetCurrentWindow(); - return window->DrawList; -} - -ImFont* ImGui::GetFont() -{ - return GImGui->Font; -} - -float ImGui::GetFontSize() -{ - return GImGui->FontSize; -} - -ImVec2 ImGui::GetFontTexUvWhitePixel() -{ - return GImGui->DrawListSharedData.TexUvWhitePixel; -} - -void ImGui::SetWindowFontScale(float scale) -{ - IM_ASSERT(scale > 0.0f); - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - window->FontWindowScale = scale; - g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); -} - -void ImGui::ActivateItem(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - g.NavNextActivateId = id; - g.NavNextActivateFlags = ImGuiActivateFlags_None; -} - -void ImGui::PushFocusScope(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent); - window->DC.NavFocusScopeIdCurrent = id; -} - -void ImGui::PopFocusScope() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ? - window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back(); - g.FocusScopeStack.pop_back(); -} - -// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system! -void ImGui::SetKeyboardFocusHere(int offset) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(offset >= -1); // -1 is allowed but not below - IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name); - - // It makes sense in the vast majority of cases to never interrupt a drag and drop. - // When we refactor this function into ActivateItem() we may want to make this an option. - // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but - // is also automatically dropped in the event g.ActiveId is stolen. - if (g.DragDropActive || g.MovingWindow != NULL) - { - IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere() ignored while DragDropActive!\n"); - return; - } - - SetNavWindow(window); - - ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; - NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. - if (offset == -1) - { - NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); - } - else - { - g.NavTabbingDir = 1; - g.NavTabbingCounter = offset + 1; - } -} - -void ImGui::SetItemDefaultFocus() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (!window->Appearing) - return; - if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResultId == 0) || g.NavLayer != window->DC.NavLayerCurrent) - return; - - g.NavInitRequest = false; - g.NavInitResultId = g.LastItemData.ID; - g.NavInitResultRectRel = WindowRectAbsToRel(window, g.LastItemData.Rect); - NavUpdateAnyRequestFlag(); - - // Scroll could be done in NavInitRequestApplyResult() via a opt-in flag (we however don't want regular init requests to scroll) - if (!IsItemVisible()) - ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None); -} - -void ImGui::SetStateStorage(ImGuiStorage* tree) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - window->DC.StateStorage = tree ? tree : &window->StateStorage; -} - -ImGuiStorage* ImGui::GetStateStorage() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->DC.StateStorage; -} - -void ImGui::PushID(const char* str_id) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiID id = window->GetID(str_id); - window->IDStack.push_back(id); -} - -void ImGui::PushID(const char* str_id_begin, const char* str_id_end) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiID id = window->GetID(str_id_begin, str_id_end); - window->IDStack.push_back(id); -} - -void ImGui::PushID(const void* ptr_id) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiID id = window->GetID(ptr_id); - window->IDStack.push_back(id); -} - -void ImGui::PushID(int int_id) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiID id = window->GetID(int_id); - window->IDStack.push_back(id); -} - -// Push a given id value ignoring the ID stack as a seed. -void ImGui::PushOverrideID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.DebugHookIdInfo == id) - DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL); - window->IDStack.push_back(id); -} - -// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call -// (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level. -// for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more) -ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) -{ - ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); - ImGuiContext& g = *GImGui; - if (g.DebugHookIdInfo == id) - DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); - return id; -} - -void ImGui::PopID() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window? - window->IDStack.pop_back(); -} - -ImGuiID ImGui::GetID(const char* str_id) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->GetID(str_id); -} - -ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->GetID(str_id_begin, str_id_end); -} - -ImGuiID ImGui::GetID(const void* ptr_id) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->GetID(ptr_id); -} - -bool ImGui::IsRectVisible(const ImVec2& size) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); -} - -bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); -} - - -//----------------------------------------------------------------------------- -// [SECTION] INPUTS -//----------------------------------------------------------------------------- - -// Test if mouse cursor is hovering given rectangle -// NB- Rectangle is clipped by our current clip setting -// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) -bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) -{ - ImGuiContext& g = *GImGui; - - // Clip - ImRect rect_clipped(r_min, r_max); - if (clip) - rect_clipped.ClipWith(g.CurrentWindow->ClipRect); - - // Expand for touch input - const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); - if (!rect_for_touch.Contains(g.IO.MousePos)) - return false; - return true; -} - -ImGuiKeyData* ImGui::GetKeyData(ImGuiKey key) -{ - ImGuiContext& g = *GImGui; - int index; -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END); - if (IsLegacyKey(key)) - index = (g.IO.KeyMap[key] != -1) ? g.IO.KeyMap[key] : key; // Remap native->imgui or imgui->native - else - index = key; -#else - IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); - index = key - ImGuiKey_NamedKey_BEGIN; -#endif - return &g.IO.KeysData[index]; -} - -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO -int ImGui::GetKeyIndex(ImGuiKey key) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(IsNamedKey(key)); - const ImGuiKeyData* key_data = GetKeyData(key); - return (int)(key_data - g.IO.KeysData); -} -#endif - -// Those names a provided for debugging purpose and are not meant to be saved persistently not compared. -static const char* const GKeyNames[] = -{ - "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown", - "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape", - "LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", - "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", - "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", - "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket", - "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen", - "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", - "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply", - "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual", - "GamepadStart", "GamepadBack", "GamepadFaceUp", "GamepadFaceDown", "GamepadFaceLeft", "GamepadFaceRight", - "GamepadDpadUp", "GamepadDpadDown", "GamepadDpadLeft", "GamepadDpadRight", - "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3", - "GamepadLStickUp", "GamepadLStickDown", "GamepadLStickLeft", "GamepadLStickRight", - "GamepadRStickUp", "GamepadRStickDown", "GamepadRStickLeft", "GamepadRStickRight", - "ModCtrl", "ModShift", "ModAlt", "ModSuper" -}; -IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); - -const char* ImGui::GetKeyName(ImGuiKey key) -{ -#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT((IsNamedKey(key) || key == ImGuiKey_None) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); -#else - if (IsLegacyKey(key)) - { - ImGuiIO& io = GetIO(); - if (io.KeyMap[key] == -1) - return "N/A"; - IM_ASSERT(IsNamedKey((ImGuiKey)io.KeyMap[key])); - key = (ImGuiKey)io.KeyMap[key]; - } -#endif - if (key == ImGuiKey_None) - return "None"; - if (!IsNamedKey(key)) - return "Unknown"; - - return GKeyNames[key - ImGuiKey_NamedKey_BEGIN]; -} - -// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) -// t1 = current time (e.g.: g.Time) -// An event is triggered at: -// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N -int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate) -{ - if (t1 == 0.0f) - return 1; - if (t0 >= t1) - return 0; - if (repeat_rate <= 0.0f) - return (t0 < repeat_delay) && (t1 >= repeat_delay); - const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); - const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); - const int count = count_t1 - count_t0; - return count; -} - -int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate) -{ - ImGuiContext& g = *GImGui; - const ImGuiKeyData* key_data = GetKeyData(key); - const float t = key_data->DownDuration; - return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); -} - -// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes. -// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87) -bool ImGui::IsKeyDown(ImGuiKey key) -{ - const ImGuiKeyData* key_data = GetKeyData(key); - if (!key_data->Down) - return false; - return true; -} - -bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat) -{ - ImGuiContext& g = *GImGui; - const ImGuiKeyData* key_data = GetKeyData(key); - const float t = key_data->DownDuration; - if (t < 0.0f) - return false; - const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && GetKeyPressedAmount(key, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0); - if (!pressed) - return false; - return true; -} - -bool ImGui::IsKeyReleased(ImGuiKey key) -{ - const ImGuiKeyData* key_data = GetKeyData(key); - if (key_data->DownDurationPrev < 0.0f || key_data->Down) - return false; - return true; -} - -bool ImGui::IsMouseDown(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDown[button]; -} - -bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - const float t = g.IO.MouseDownDuration[button]; - if (t == 0.0f) - return true; - if (repeat && t > g.IO.KeyRepeatDelay) - return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; - return false; -} - -bool ImGui::IsMouseReleased(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseReleased[button]; -} - -bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseClickedCount[button] == 2; -} - -int ImGui::GetMouseClickedCount(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseClickedCount[button]; -} - -// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. -// [Internal] This doesn't test if the button is pressed -bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (lock_threshold < 0.0f) - lock_threshold = g.IO.MouseDragThreshold; - return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; -} - -bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (!g.IO.MouseDown[button]) - return false; - return IsMouseDragPastThreshold(button, lock_threshold); -} - -ImVec2 ImGui::GetMousePos() -{ - ImGuiContext& g = *GImGui; - return g.IO.MousePos; -} - -// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! -ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() -{ - ImGuiContext& g = *GImGui; - if (g.BeginPopupStack.Size > 0) - return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos; - return g.IO.MousePos; -} - -// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position. -bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) -{ - // The assert is only to silence a false-positive in XCode Static Analysis. - // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions). - IM_ASSERT(GImGui != NULL); - const float MOUSE_INVALID = -256000.0f; - ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; - return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; -} - -// [WILL OBSOLETE] This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid. -bool ImGui::IsAnyMouseDown() -{ - ImGuiContext& g = *GImGui; - for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) - if (g.IO.MouseDown[n]) - return true; - return false; -} - -// Return the delta from the initial clicking position while the mouse button is clicked or was just released. -// This is locked and return 0.0f until the mouse moves past a distance threshold at least once. -// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window. -ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (lock_threshold < 0.0f) - lock_threshold = g.IO.MouseDragThreshold; - if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) - if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) - if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) - return g.IO.MousePos - g.IO.MouseClickedPos[button]; - return ImVec2(0.0f, 0.0f); -} - -void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr - g.IO.MouseClickedPos[button] = g.IO.MousePos; -} - -ImGuiMouseCursor ImGui::GetMouseCursor() -{ - ImGuiContext& g = *GImGui; - return g.MouseCursor; -} - -void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) -{ - ImGuiContext& g = *GImGui; - g.MouseCursor = cursor_type; -} - -void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard) -{ - ImGuiContext& g = *GImGui; - g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0; -} - -void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse) -{ - ImGuiContext& g = *GImGui; - g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0; -} - -#ifndef IMGUI_DISABLE_DEBUG_TOOLS -static const char* GetInputSourceName(ImGuiInputSource source) -{ - const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; - IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT); - return input_source_names[source]; -} -#endif - -/*static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) -{ - if (e->Type == ImGuiInputEventType_MousePos) { IMGUI_DEBUG_LOG_IO("%s: MousePos (%.1f %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } - if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.1f %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } - if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } - if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } -}*/ - -// Process input queue -// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'. -// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost) -// - trickle_fast_inputs = true : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87) -void ImGui::UpdateInputEvents(bool trickle_fast_inputs) -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - - // Only trickle chars<>key when working with InputText() - // FIXME: InputText() could parse event trail? - // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters) - const bool trickle_interleaved_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1); - - bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputted = false; - int mouse_button_changed = 0x00; - ImBitArray key_changed_mask; - - int event_n = 0; - for (; event_n < g.InputEventsQueue.Size; event_n++) - { - const ImGuiInputEvent* e = &g.InputEventsQueue[event_n]; - if (e->Type == ImGuiInputEventType_MousePos) - { - ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY); - if (IsMousePosValid(&event_pos)) - event_pos = ImVec2(ImFloorSigned(event_pos.x), ImFloorSigned(event_pos.y)); // Apply same flooring as UpdateMouseInputs() - if (io.MousePos.x != event_pos.x || io.MousePos.y != event_pos.y) - { - // Trickling Rule: Stop processing queued events if we already handled a mouse button change - if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted)) - break; - io.MousePos = event_pos; - mouse_moved = true; - } - } - else if (e->Type == ImGuiInputEventType_MouseButton) - { - const ImGuiMouseButton button = e->MouseButton.Button; - IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); - if (io.MouseDown[button] != e->MouseButton.Down) - { - // Trickling Rule: Stop processing queued events if we got multiple action on the same button - if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled)) - break; - io.MouseDown[button] = e->MouseButton.Down; - mouse_button_changed |= (1 << button); - } - } - else if (e->Type == ImGuiInputEventType_MouseWheel) - { - if (e->MouseWheel.WheelX != 0.0f || e->MouseWheel.WheelY != 0.0f) - { - // Trickling Rule: Stop processing queued events if we got multiple action on the event - if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0)) - break; - io.MouseWheelH += e->MouseWheel.WheelX; - io.MouseWheel += e->MouseWheel.WheelY; - mouse_wheeled = true; - } - } - else if (e->Type == ImGuiInputEventType_Key) - { - ImGuiKey key = e->Key.Key; - IM_ASSERT(key != ImGuiKey_None); - const int keydata_index = (key - ImGuiKey_KeysData_OFFSET); - ImGuiKeyData* keydata = &io.KeysData[keydata_index]; - if (keydata->Down != e->Key.Down || keydata->AnalogValue != e->Key.AnalogValue) - { - // Trickling Rule: Stop processing queued events if we got multiple action on the same button - if (trickle_fast_inputs && keydata->Down != e->Key.Down && (key_changed_mask.TestBit(keydata_index) || text_inputted || mouse_button_changed != 0)) - break; - keydata->Down = e->Key.Down; - keydata->AnalogValue = e->Key.AnalogValue; - key_changed = true; - key_changed_mask.SetBit(keydata_index); - - if (key == ImGuiKey_ModCtrl || key == ImGuiKey_ModShift || key == ImGuiKey_ModAlt || key == ImGuiKey_ModSuper) - { - if (key == ImGuiKey_ModCtrl) { io.KeyCtrl = keydata->Down; } - if (key == ImGuiKey_ModShift) { io.KeyShift = keydata->Down; } - if (key == ImGuiKey_ModAlt) { io.KeyAlt = keydata->Down; } - if (key == ImGuiKey_ModSuper) { io.KeySuper = keydata->Down; } - io.KeyMods = GetMergedModFlags(); - } - - // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - io.KeysDown[key] = keydata->Down; - if (io.KeyMap[key] != -1) - io.KeysDown[io.KeyMap[key]] = keydata->Down; -#endif - } - } - else if (e->Type == ImGuiInputEventType_Text) - { - // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with - if (trickle_fast_inputs && ((key_changed && trickle_interleaved_keys_and_text) || mouse_button_changed != 0 || mouse_moved || mouse_wheeled)) - break; - unsigned int c = e->Text.Char; - io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); - if (trickle_interleaved_keys_and_text) - text_inputted = true; - } - else if (e->Type == ImGuiInputEventType_Focus) - { - // We intentionally overwrite this and process lower, in order to give a chance - // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame. - io.AppFocusLost = !e->AppFocused.Focused; - } - else - { - IM_ASSERT(0 && "Unknown event!"); - } - } - - // Record trail (for domain-specific applications wanting to access a precise trail) - //if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n); - for (int n = 0; n < event_n; n++) - g.InputEventsTrail.push_back(g.InputEventsQueue[n]); - - // [DEBUG] - /*if (event_n != 0) - for (int n = 0; n < g.InputEventsQueue.Size; n++) - DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);*/ - - // Remaining events will be processed on the next frame - if (event_n == g.InputEventsQueue.Size) - g.InputEventsQueue.resize(0); - else - g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n); - - // Clear buttons state when focus is lost - // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) - if (g.IO.AppFocusLost) - { - g.IO.ClearInputKeys(); - g.IO.AppFocusLost = false; - } -} - - -//----------------------------------------------------------------------------- -// [SECTION] ERROR CHECKING -//----------------------------------------------------------------------------- - -// Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui. -// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit -// If this triggers you have an issue: -// - Most commonly: mismatched headers and compiled code version. -// - Or: mismatched configuration #define, compilation settings, packing pragma etc. -// The configuration settings mentioned in imconfig.h must be set for all compilation units involved with Dear ImGui, -// which is way it is required you put them in your imconfig file (and not just before including imgui.h). -// Otherwise it is possible that different compilation units would see different structure layout -bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx) -{ - bool error = false; - if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); } - if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } - if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } - if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } - if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } - if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } - if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); } - return !error; -} - -static void ImGui::ErrorCheckNewFrameSanityChecks() -{ - ImGuiContext& g = *GImGui; - - // Check user IM_ASSERT macro - // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined! - // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block. - // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.) - // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong! - // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct! - if (true) IM_ASSERT(1); else IM_ASSERT(0); - - // Check user data - // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument) - IM_ASSERT(g.Initialized); - IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); - IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); - IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); - IM_ASSERT(g.IO.Fonts->IsBuilt() && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()"); - IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); - IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!"); - IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations - IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); - IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); - IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right); -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++) - IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < ImGuiKey_LegacyNativeKey_END && "io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)"); - - // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP) - if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1) - IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); -#endif - - // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. - if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) - g.IO.ConfigWindowsResizeFromEdges = false; -} - -static void ImGui::ErrorCheckEndFrameSanityChecks() -{ - ImGuiContext& g = *GImGui; - - // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame() - // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame(). - // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will - // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs. - // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), - // while still correctly asserting on mid-frame key press events. - const ImGuiModFlags key_mods = GetMergedModFlags(); - IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); - IM_UNUSED(key_mods); - - // [EXPERIMENTAL] Recover from errors: You may call this yourself before EndFrame(). - //ErrorCheckEndFrameRecover(); - - // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you - // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). - if (g.CurrentWindowStack.Size != 1) - { - if (g.CurrentWindowStack.Size > 1) - { - IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"); - while (g.CurrentWindowStack.Size > 1) - End(); - } - else - { - IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); - } - } - - IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!"); -} - -// Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls. -// Must be called during or before EndFrame(). -// This is generally flawed as we are not necessarily End/Popping things in the right order. -// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet. -// FIXME: Can't recover from interleaved BeginTabBar/Begin -void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data) -{ - // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" - ImGuiContext& g = *GImGui; - while (g.CurrentWindowStack.Size > 0) //-V1044 - { - ErrorCheckEndWindowRecover(log_callback, user_data); - ImGuiWindow* window = g.CurrentWindow; - if (g.CurrentWindowStack.Size == 1) - { - IM_ASSERT(window->IsFallbackWindow); - break; - } - if (window->Flags & ImGuiWindowFlags_ChildWindow) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name); - EndChild(); - } - else - { - if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name); - End(); - } - } -} - -// Must be called before End()/EndChild() -void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data) -{ - ImGuiContext& g = *GImGui; - while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); - EndTable(); - } - - ImGuiWindow* window = g.CurrentWindow; - ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin; - IM_ASSERT(window != NULL); - while (g.CurrentTabBar != NULL) //-V1044 - { - if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); - EndTabBar(); - } - while (window->DC.TreeDepth > 0) - { - if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); - TreePop(); - } - while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044 - { - if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); - EndGroup(); - } - while (window->IDStack.Size > 1) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); - PopID(); - } - while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044 - { - if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); - EndDisabled(); - } - while (g.ColorStack.Size > stack_sizes->SizeOfColorStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); - PopStyleColor(); - } - while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044 - { - if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); - PopItemFlag(); - } - while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044 - { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); - PopStyleVar(); - } - while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) //-V1044 - { - if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); - PopFocusScope(); - } -} - -// Save current stack sizes for later compare -void ImGuiStackSizes::SetToCurrentState() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - SizeOfIDStack = (short)window->IDStack.Size; - SizeOfColorStack = (short)g.ColorStack.Size; - SizeOfStyleVarStack = (short)g.StyleVarStack.Size; - SizeOfFontStack = (short)g.FontStack.Size; - SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size; - SizeOfGroupStack = (short)g.GroupStack.Size; - SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size; - SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size; - SizeOfDisabledStack = (short)g.DisabledStackSize; -} - -// Compare to detect usage errors -void ImGuiStackSizes::CompareWithCurrentState() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_UNUSED(window); - - // Window stacks - // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) - IM_ASSERT(SizeOfIDStack == window->IDStack.Size && "PushID/PopID or TreeNode/TreePop Mismatch!"); - - // Global stacks - // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. - IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!"); - IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!"); - IM_ASSERT(SizeOfDisabledStack == g.DisabledStackSize && "BeginDisabled/EndDisabled Mismatch!"); - IM_ASSERT(SizeOfItemFlagsStack >= g.ItemFlagsStack.Size && "PushItemFlag/PopItemFlag Mismatch!"); - IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!"); - IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!"); - IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!"); - IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!"); -} - - -//----------------------------------------------------------------------------- -// [SECTION] LAYOUT -//----------------------------------------------------------------------------- -// - ItemSize() -// - ItemAdd() -// - SameLine() -// - GetCursorScreenPos() -// - SetCursorScreenPos() -// - GetCursorPos(), GetCursorPosX(), GetCursorPosY() -// - SetCursorPos(), SetCursorPosX(), SetCursorPosY() -// - GetCursorStartPos() -// - Indent() -// - Unindent() -// - SetNextItemWidth() -// - PushItemWidth() -// - PushMultiItemsWidths() -// - PopItemWidth() -// - CalcItemWidth() -// - CalcItemSize() -// - GetTextLineHeight() -// - GetTextLineHeightWithSpacing() -// - GetFrameHeight() -// - GetFrameHeightWithSpacing() -// - GetContentRegionMax() -// - GetContentRegionMaxAbs() [Internal] -// - GetContentRegionAvail(), -// - GetWindowContentRegionMin(), GetWindowContentRegionMax() -// - BeginGroup() -// - EndGroup() -// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns. -//----------------------------------------------------------------------------- - -// Advance cursor given item size for layout. -// Register minimum needed size so it can extend the bounding box used for auto-fit calculation. -// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different. -void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return; - - // We increase the height in this function to accommodate for baseline offset. - // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor, - // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect. - const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f; - - const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y; - const float line_height = ImMax(window->DC.CurrLineSize.y, /*ImMax(*/window->DC.CursorPos.y - line_y1/*, 0.0f)*/ + size.y + offset_to_match_baseline_y); - - // Always align ourselves on pixel boundaries - //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG] - window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x; - window->DC.CursorPosPrevLine.y = line_y1; - window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line - window->DC.CursorPos.y = IM_FLOOR(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line - window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); - //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] - - window->DC.PrevLineSize.y = line_height; - window->DC.CurrLineSize.y = 0.0f; - window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y); - window->DC.CurrLineTextBaseOffset = 0.0f; - window->DC.IsSameLine = false; - - // Horizontal layout mode - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) - SameLine(); -} - -// Declare item bounding box for clipping and interaction. -// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface -// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. -bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Set item data - // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set) - g.LastItemData.ID = id; - g.LastItemData.Rect = bb; - g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; - g.LastItemData.InFlags = g.CurrentItemFlags | extra_flags; - g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; - - // Directional navigation processing - if (id != 0) - { - KeepAliveID(id); - - // Runs prior to clipping early-out - // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget - // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests - // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of - // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. - // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able - // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). - // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. - // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. - window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); - if (g.NavId == id || g.NavAnyRequest) - if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) - if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) - NavProcessItem(); - - // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". - // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". - // READ THE FAQ: https://dearimgui.org/faq - IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); - - // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() -#ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX - if (id == g.DebugItemPickerBreakId) - { - IM_DEBUG_BREAK(); - g.DebugItemPickerBreakId = 0; - } -#endif - } - g.NextItemData.Flags = ImGuiNextItemDataFlags_None; - -#ifdef IMGUI_ENABLE_TEST_ENGINE - if (id != 0) - IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id); -#endif - - // Clipping test - const bool is_clipped = IsClippedEx(bb, id); - if (is_clipped) - return false; - //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] - - // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) - if (IsMouseHoveringRect(bb.Min, bb.Max)) - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; - return true; -} - -// Gets back to previous line and continue with horizontal layout -// offset_from_start_x == 0 : follow right after previous item -// offset_from_start_x != 0 : align to specified x position (relative to window/group left) -// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 -// spacing_w >= 0 : enforce spacing amount -void ImGui::SameLine(float offset_from_start_x, float spacing_w) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return; - - if (offset_from_start_x != 0.0f) - { - if (spacing_w < 0.0f) - spacing_w = 0.0f; - window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x; - window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; - } - else - { - if (spacing_w < 0.0f) - spacing_w = g.Style.ItemSpacing.x; - window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w; - window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; - } - window->DC.CurrLineSize = window->DC.PrevLineSize; - window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; - window->DC.IsSameLine = true; -} - -ImVec2 ImGui::GetCursorScreenPos() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorPos; -} - -void ImGui::SetCursorScreenPos(const ImVec2& pos) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos = pos; - window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); -} - -// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient. -// Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'. -ImVec2 ImGui::GetCursorPos() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorPos - window->Pos + window->Scroll; -} - -float ImGui::GetCursorPosX() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x; -} - -float ImGui::GetCursorPosY() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y; -} - -void ImGui::SetCursorPos(const ImVec2& local_pos) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos = window->Pos - window->Scroll + local_pos; - window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); -} - -void ImGui::SetCursorPosX(float x) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; - window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); -} - -void ImGui::SetCursorPosY(float y) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); -} - -ImVec2 ImGui::GetCursorStartPos() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorStartPos - window->Pos; -} - -void ImGui::Indent(float indent_w) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; -} - -void ImGui::Unindent(float indent_w) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; -} - -// Affect large frame+labels widgets only. -void ImGui::SetNextItemWidth(float item_width) -{ - ImGuiContext& g = *GImGui; - g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth; - g.NextItemData.Width = item_width; -} - -// FIXME: Remove the == 0.0f behavior? -void ImGui::PushItemWidth(float item_width) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width - window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); - g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; -} - -void ImGui::PushMultiItemsWidths(int components, float w_full) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const ImGuiStyle& style = g.Style; - const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components)); - const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1))); - window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width - window->DC.ItemWidthStack.push_back(w_item_last); - for (int i = 0; i < components - 2; i++) - window->DC.ItemWidthStack.push_back(w_item_one); - window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one; - g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; -} - -void ImGui::PopItemWidth() -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.ItemWidth = window->DC.ItemWidthStack.back(); - window->DC.ItemWidthStack.pop_back(); -} - -// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth(). -// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags() -float ImGui::CalcItemWidth() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - float w; - if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth) - w = g.NextItemData.Width; - else - w = window->DC.ItemWidth; - if (w < 0.0f) - { - float region_max_x = GetContentRegionMaxAbs().x; - w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w); - } - w = IM_FLOOR(w); - return w; -} - -// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth(). -// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical. -// Note that only CalcItemWidth() is publicly exposed. -// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable) -ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - ImVec2 region_max; - if (size.x < 0.0f || size.y < 0.0f) - region_max = GetContentRegionMaxAbs(); - - if (size.x == 0.0f) - size.x = default_w; - else if (size.x < 0.0f) - size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x); - - if (size.y == 0.0f) - size.y = default_h; - else if (size.y < 0.0f) - size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y); - - return size; -} - -float ImGui::GetTextLineHeight() -{ - ImGuiContext& g = *GImGui; - return g.FontSize; -} - -float ImGui::GetTextLineHeightWithSpacing() -{ - ImGuiContext& g = *GImGui; - return g.FontSize + g.Style.ItemSpacing.y; -} - -float ImGui::GetFrameHeight() -{ - ImGuiContext& g = *GImGui; - return g.FontSize + g.Style.FramePadding.y * 2.0f; -} - -float ImGui::GetFrameHeightWithSpacing() -{ - ImGuiContext& g = *GImGui; - return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y; -} - -// FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience! - -// FIXME: This is in window space (not screen space!). -ImVec2 ImGui::GetContentRegionMax() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImVec2 mx = window->ContentRegionRect.Max - window->Pos; - if (window->DC.CurrentColumns || g.CurrentTable) - mx.x = window->WorkRect.Max.x - window->Pos.x; - return mx; -} - -// [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features. -ImVec2 ImGui::GetContentRegionMaxAbs() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImVec2 mx = window->ContentRegionRect.Max; - if (window->DC.CurrentColumns || g.CurrentTable) - mx.x = window->WorkRect.Max.x; - return mx; -} - -ImVec2 ImGui::GetContentRegionAvail() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return GetContentRegionMaxAbs() - window->DC.CursorPos; -} - -// In window space (not screen space!) -ImVec2 ImGui::GetWindowContentRegionMin() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ContentRegionRect.Min - window->Pos; -} - -ImVec2 ImGui::GetWindowContentRegionMax() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ContentRegionRect.Max - window->Pos; -} - -// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) -// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. -// FIXME-OPT: Could we safely early out on ->SkipItems? -void ImGui::BeginGroup() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - g.GroupStack.resize(g.GroupStack.Size + 1); - ImGuiGroupData& group_data = g.GroupStack.back(); - group_data.WindowID = window->ID; - group_data.BackupCursorPos = window->DC.CursorPos; - group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; - group_data.BackupIndent = window->DC.Indent; - group_data.BackupGroupOffset = window->DC.GroupOffset; - group_data.BackupCurrLineSize = window->DC.CurrLineSize; - group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset; - group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; - group_data.BackupHoveredIdIsAlive = g.HoveredId != 0; - group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; - group_data.EmitItem = true; - - window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x; - window->DC.Indent = window->DC.GroupOffset; - window->DC.CursorMaxPos = window->DC.CursorPos; - window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); - if (g.LogEnabled) - g.LogLinePosY = -FLT_MAX; // To enforce a carriage return -} - -void ImGui::EndGroup() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls - - ImGuiGroupData& group_data = g.GroupStack.back(); - IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window? - - ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos)); - - window->DC.CursorPos = group_data.BackupCursorPos; - window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); - window->DC.Indent = group_data.BackupIndent; - window->DC.GroupOffset = group_data.BackupGroupOffset; - window->DC.CurrLineSize = group_data.BackupCurrLineSize; - window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset; - if (g.LogEnabled) - g.LogLinePosY = -FLT_MAX; // To enforce a carriage return - - if (!group_data.EmitItem) - { - g.GroupStack.pop_back(); - return; - } - - window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. - ItemSize(group_bb.GetSize()); - ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop); - - // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group. - // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. - // Also if you grep for LastItemId you'll notice it is only used in that context. - // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.) - const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; - const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true); - if (group_contains_curr_active_id) - g.LastItemData.ID = g.ActiveId; - else if (group_contains_prev_active_id) - g.LastItemData.ID = g.ActiveIdPreviousFrame; - g.LastItemData.Rect = group_bb; - - // Forward Hovered flag - const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0; - if (group_contains_curr_hovered_id) - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; - - // Forward Edited flag - if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; - - // Forward Deactivated flag - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated; - if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame) - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated; - - g.GroupStack.pop_back(); - //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] -} - - -//----------------------------------------------------------------------------- -// [SECTION] SCROLLING -//----------------------------------------------------------------------------- - -// Helper to snap on edges when aiming at an item very close to the edge, -// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling. -// When we refactor the scrolling API this may be configurable with a flag? -// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default. -static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio) -{ - if (target <= snap_min + snap_threshold) - return ImLerp(snap_min, target, center_ratio); - if (target >= snap_max - snap_threshold) - return ImLerp(target, snap_max, center_ratio); - return target; -} - -static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) -{ - ImVec2 scroll = window->Scroll; - if (window->ScrollTarget.x < FLT_MAX) - { - float decoration_total_width = window->ScrollbarSizes.x; - float center_x_ratio = window->ScrollTargetCenterRatio.x; - float scroll_target_x = window->ScrollTarget.x; - if (window->ScrollTargetEdgeSnapDist.x > 0.0f) - { - float snap_x_min = 0.0f; - float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width; - scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio); - } - scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width); - } - if (window->ScrollTarget.y < FLT_MAX) - { - float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y; - float center_y_ratio = window->ScrollTargetCenterRatio.y; - float scroll_target_y = window->ScrollTarget.y; - if (window->ScrollTargetEdgeSnapDist.y > 0.0f) - { - float snap_y_min = 0.0f; - float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height; - scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio); - } - scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height); - } - scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f)); - scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); - if (!window->Collapsed && !window->SkipItems) - { - scroll.x = ImMin(scroll.x, window->ScrollMax.x); - scroll.y = ImMin(scroll.y, window->ScrollMax.y); - } - return scroll; -} - -void ImGui::ScrollToItem(ImGuiScrollFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ScrollToRectEx(window, g.LastItemData.NavRect, flags); -} - -void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) -{ - ScrollToRectEx(window, item_rect, flags); -} - -// Scroll to keep newly navigated item fully into view -ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) -{ - ImGuiContext& g = *GImGui; - ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); - //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] - - // Check that only one behavior is selected per axis - IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_)); - IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_)); - - // Defaults - ImGuiScrollFlags in_flags = flags; - if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX) - flags |= ImGuiScrollFlags_KeepVisibleEdgeX; - if ((flags & ImGuiScrollFlags_MaskY_) == 0) - flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY; - - const bool fully_visible_x = item_rect.Min.x >= window_rect.Min.x && item_rect.Max.x <= window_rect.Max.x; - const bool fully_visible_y = item_rect.Min.y >= window_rect.Min.y && item_rect.Max.y <= window_rect.Max.y; - const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= window_rect.GetWidth(); - const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= window_rect.GetHeight(); - - if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x) - { - if (item_rect.Min.x < window_rect.Min.x || !can_be_fully_visible_x) - SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f); - else if (item_rect.Max.x >= window_rect.Max.x) - SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f); - } - else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX)) - { - float target_x = can_be_fully_visible_x ? ImFloor((item_rect.Min.x + item_rect.Max.x - window->InnerRect.GetWidth()) * 0.5f) : item_rect.Min.x; - SetScrollFromPosX(window, target_x - window->Pos.x, 0.0f); - } - - if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y) - { - if (item_rect.Min.y < window_rect.Min.y || !can_be_fully_visible_y) - SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f); - else if (item_rect.Max.y >= window_rect.Max.y) - SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f); - } - else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY)) - { - float target_y = can_be_fully_visible_y ? ImFloor((item_rect.Min.y + item_rect.Max.y - window->InnerRect.GetHeight()) * 0.5f) : item_rect.Min.y; - SetScrollFromPosY(window, target_y - window->Pos.y, 0.0f); - } - - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); - ImVec2 delta_scroll = next_scroll - window->Scroll; - - // Also scroll parent window to keep us into view if necessary - if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow)) - { - // FIXME-SCROLL: May be an option? - if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0) - in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX; - if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0) - in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY; - delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags); - } - - return delta_scroll; -} - -float ImGui::GetScrollX() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->Scroll.x; -} - -float ImGui::GetScrollY() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->Scroll.y; -} - -float ImGui::GetScrollMaxX() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ScrollMax.x; -} - -float ImGui::GetScrollMaxY() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ScrollMax.y; -} - -void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x) -{ - window->ScrollTarget.x = scroll_x; - window->ScrollTargetCenterRatio.x = 0.0f; - window->ScrollTargetEdgeSnapDist.x = 0.0f; -} - -void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y) -{ - window->ScrollTarget.y = scroll_y; - window->ScrollTargetCenterRatio.y = 0.0f; - window->ScrollTargetEdgeSnapDist.y = 0.0f; -} - -void ImGui::SetScrollX(float scroll_x) -{ - ImGuiContext& g = *GImGui; - SetScrollX(g.CurrentWindow, scroll_x); -} - -void ImGui::SetScrollY(float scroll_y) -{ - ImGuiContext& g = *GImGui; - SetScrollY(g.CurrentWindow, scroll_y); -} - -// Note that a local position will vary depending on initial scroll value, -// This is a little bit confusing so bear with us: -// - local_pos = (absolution_pos - window->Pos) -// - So local_x/local_y are 0.0f for a position at the upper-left corner of a window, -// and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area. -// - They mostly exists because of legacy API. -// Following the rules above, when trying to work with scrolling code, consider that: -// - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect! -// - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense -// We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size -void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio) -{ - IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); - window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset - window->ScrollTargetCenterRatio.x = center_x_ratio; - window->ScrollTargetEdgeSnapDist.x = 0.0f; -} - -void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) -{ - IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); - const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect; - local_y -= decoration_up_height; - window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset - window->ScrollTargetCenterRatio.y = center_y_ratio; - window->ScrollTargetEdgeSnapDist.y = 0.0f; -} - -void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio) -{ - ImGuiContext& g = *GImGui; - SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio); -} - -void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio) -{ - ImGuiContext& g = *GImGui; - SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio); -} - -// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item. -void ImGui::SetScrollHereX(float center_x_ratio) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x); - float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio); - SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos - - // Tweak: snap on edges when aiming at an item very close to the edge - window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x); -} - -// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. -void ImGui::SetScrollHereY(float center_y_ratio) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y); - float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio); - SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos - - // Tweak: snap on edges when aiming at an item very close to the edge - window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y); -} - -//----------------------------------------------------------------------------- -// [SECTION] TOOLTIPS -//----------------------------------------------------------------------------- - -void ImGui::BeginTooltip() -{ - BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); -} - -void ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags) -{ - ImGuiContext& g = *GImGui; - - if (g.DragDropWithinSource || g.DragDropWithinTarget) - { - // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) - // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. - // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do. - //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; - ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); - SetNextWindowPos(tooltip_pos); - SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); - //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( - tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip; - } - - char window_name[16]; - ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); - if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip) - if (ImGuiWindow* window = FindWindowByName(window_name)) - if (window->Active) - { - // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. - window->Hidden = true; - window->HiddenFramesCanSkipItems = 1; // FIXME: This may not be necessary? - ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); - } - ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; - Begin(window_name, NULL, flags | extra_window_flags); -} - -void ImGui::EndTooltip() -{ - IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls - End(); -} - -void ImGui::SetTooltipV(const char* fmt, va_list args) -{ - BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); - TextV(fmt, args); - EndTooltip(); -} - -void ImGui::SetTooltip(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - SetTooltipV(fmt, args); - va_end(args); -} - -//----------------------------------------------------------------------------- -// [SECTION] POPUPS -//----------------------------------------------------------------------------- - -// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel -bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags) -{ - ImGuiContext& g = *GImGui; - if (popup_flags & ImGuiPopupFlags_AnyPopupId) - { - // Return true if any popup is open at the current BeginPopup() level of the popup stack - // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level. - IM_ASSERT(id == 0); - if (popup_flags & ImGuiPopupFlags_AnyPopupLevel) - return g.OpenPopupStack.Size > 0; - else - return g.OpenPopupStack.Size > g.BeginPopupStack.Size; - } - else - { - if (popup_flags & ImGuiPopupFlags_AnyPopupLevel) - { - // Return true if the popup is open anywhere in the popup stack - for (int n = 0; n < g.OpenPopupStack.Size; n++) - if (g.OpenPopupStack[n].PopupId == id) - return true; - return false; - } - else - { - // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query) - return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id; - } - } -} - -bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id); - if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0) - IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally - return IsPopupOpen(id, popup_flags); -} - -ImGuiWindow* ImGui::GetTopMostPopupModal() -{ - ImGuiContext& g = *GImGui; - for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) - if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) - if (popup->Flags & ImGuiWindowFlags_Modal) - return popup; - return NULL; -} - -ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal() -{ - ImGuiContext& g = *GImGui; - for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) - if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) - if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup)) - return popup; - return NULL; -} - -void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiID id = g.CurrentWindow->GetID(str_id); - IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X\n", str_id, id); - OpenPopupEx(id, popup_flags); -} - -void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags) -{ - OpenPopupEx(id, popup_flags); -} - -// Mark popup as open (toggle toward open state). -// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. -// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). -// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) -void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* parent_window = g.CurrentWindow; - const int current_stack_size = g.BeginPopupStack.Size; - - if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup) - if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId)) - return; - - ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. - popup_ref.PopupId = id; - popup_ref.Window = NULL; - popup_ref.SourceWindow = g.NavWindow; - popup_ref.OpenFrameCount = g.FrameCount; - popup_ref.OpenParentId = parent_window->IDStack.back(); - popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); - popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; - - IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id); - if (g.OpenPopupStack.Size < current_stack_size + 1) - { - g.OpenPopupStack.push_back(popup_ref); - } - else - { - // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui - // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing - // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand. - if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) - { - g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; - } - else - { - // Close child popups if any, then flag popup for open/reopen - ClosePopupToLevel(current_stack_size, false); - g.OpenPopupStack.push_back(popup_ref); - } - - // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). - // This is equivalent to what ClosePopupToLevel() does. - //if (g.OpenPopupStack[current_stack_size].PopupId == id) - // FocusWindow(parent_window); - } -} - -// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. -// This function closes any popups that are over 'ref_window'. -void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup) -{ - ImGuiContext& g = *GImGui; - if (g.OpenPopupStack.Size == 0) - return; - - // Don't close our own child popup windows. - int popup_count_to_keep = 0; - if (ref_window) - { - // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow) - for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++) - { - ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep]; - if (!popup.Window) - continue; - IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); - if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) - continue; - - // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow) - // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3: - // Window -> Popup1 -> Popup2 -> Popup3 - // - Each popups may contain child windows, which is why we compare ->RootWindow! - // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child - bool ref_window_is_descendent_of_popup = false; - for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) - if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) - if (IsWindowWithinBeginStackOf(ref_window, popup_window)) - { - ref_window_is_descendent_of_popup = true; - break; - } - if (!ref_window_is_descendent_of_popup) - break; - } - } - if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below - { - IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : ""); - ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup); - } -} - -void ImGui::ClosePopupsExceptModals() -{ - ImGuiContext& g = *GImGui; - - int popup_count_to_keep; - for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--) - { - ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window; - if (!window || window->Flags & ImGuiWindowFlags_Modal) - break; - } - if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below - ClosePopupToLevel(popup_count_to_keep, true); -} - -void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) -{ - ImGuiContext& g = *GImGui; - IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup); - IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); - - // Trim open popup stack - ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow; - ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; - g.OpenPopupStack.resize(remaining); - - if (restore_focus_to_window_under_popup) - { - if (focus_window && !focus_window->WasActive && popup_window) - { - // Fallback - FocusTopMostWindowUnderOne(popup_window, NULL); - } - else - { - if (g.NavLayer == ImGuiNavLayer_Main && focus_window) - focus_window = NavRestoreLastChildNavWindow(focus_window); - FocusWindow(focus_window); - } - } -} - -// Close the popup we have begin-ed into. -void ImGui::CloseCurrentPopup() -{ - ImGuiContext& g = *GImGui; - int popup_idx = g.BeginPopupStack.Size - 1; - if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) - return; - - // Closing a menu closes its top-most parent popup (unless a modal) - while (popup_idx > 0) - { - ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window; - ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; - bool close_parent = false; - if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) - if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar)) - close_parent = true; - if (!close_parent) - break; - popup_idx--; - } - IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); - ClosePopupToLevel(popup_idx, true); - - // A common pattern is to close a popup when selecting a menu item/selectable that will open another window. - // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window. - // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic. - if (ImGuiWindow* window = g.NavWindow) - window->DC.NavHideHighlightOneFrame = true; -} - -// Attention! BeginPopup() adds default flags which BeginPopupEx()! -bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - if (!IsPopupOpen(id, ImGuiPopupFlags_None)) - { - g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values - return false; - } - - char name[20]; - if (flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuCount); // Recycle windows based on depth - else - ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame - - flags |= ImGuiWindowFlags_Popup; - bool is_open = Begin(name, NULL, flags); - if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) - EndPopup(); - - return is_open; -} - -bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance - { - g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values - return false; - } - flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; - ImGuiID id = g.CurrentWindow->GetID(str_id); - return BeginPopupEx(id, flags); -} - -// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup. -// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here. -bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const ImGuiID id = window->GetID(name); - if (!IsPopupOpen(id, ImGuiPopupFlags_None)) - { - g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values - return false; - } - - // Center modal windows by default for increased visibility - // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves) - // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. - if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) - { - const ImGuiViewport* viewport = GetMainViewport(); - SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); - } - - flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse; - const bool is_open = Begin(name, p_open, flags); - if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - { - EndPopup(); - if (is_open) - ClosePopupToLevel(g.BeginPopupStack.Size, true); - return false; - } - return is_open; -} - -void ImGui::EndPopup() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls - IM_ASSERT(g.BeginPopupStack.Size > 0); - - // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests) - if (g.NavWindow == window) - NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); - - // Child-popups don't need to be laid out - IM_ASSERT(g.WithinEndChild == false); - if (window->Flags & ImGuiWindowFlags_ChildWindow) - g.WithinEndChild = true; - End(); - g.WithinEndChild = false; -} - -// Helper to open a popup if mouse button is released over the item -// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() -void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); - if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - { - ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - OpenPopupEx(id, popup_flags); - } -} - -// This is a helper to handle the simplest case of associating one named popup to one given widget. -// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id. -// - To create a popup with a specific identifier, pass it in str_id. -// - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call. -// - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id. -// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). -// This is essentially the same as: -// id = str_id ? GetID(str_id) : GetItemID(); -// OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight); -// return BeginPopup(id); -// Which is essentially the same as: -// id = str_id ? GetID(str_id) : GetItemID(); -// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) -// OpenPopup(id); -// return BeginPopup(id); -// The main difference being that this is tweaked to avoid computing the ID twice. -bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return false; - ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); - if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - OpenPopupEx(id, popup_flags); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (!str_id) - str_id = "window_context"; - ImGuiID id = window->GetID(str_id); - int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); - if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered()) - OpenPopupEx(id, popup_flags); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (!str_id) - str_id = "void_context"; - ImGuiID id = window->GetID(str_id); - int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); - if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) - if (GetTopMostPopupModal() == NULL) - OpenPopupEx(id, popup_flags); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); -} - -// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) -// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. -// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor -// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport. -// this allows us to have tooltips/popups displayed out of the parent viewport.) -ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) -{ - ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); - //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); - //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); - - // Combo Box policy (we want a connecting edge) - if (policy == ImGuiPopupPositionPolicy_ComboBox) - { - const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; - for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) - { - const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; - if (n != -1 && dir == *last_dir) // Already tried this direction? - continue; - ImVec2 pos; - if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) - if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right - if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left - if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left - if (!r_outer.Contains(ImRect(pos, pos + size))) - continue; - *last_dir = dir; - return pos; - } - } - - // Tooltip and Default popup policy - // (Always first try the direction we used on the last frame, if any) - if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default) - { - const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; - for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) - { - const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; - if (n != -1 && dir == *last_dir) // Already tried this direction? - continue; - - const float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x); - const float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y); - - // If there not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width) - if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right)) - continue; - if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down)) - continue; - - ImVec2 pos; - pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; - pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; - - // Clamp top-left corner of popup - pos.x = ImMax(pos.x, r_outer.Min.x); - pos.y = ImMax(pos.y, r_outer.Min.y); - - *last_dir = dir; - return pos; - } - } - - // Fallback when not enough room: - *last_dir = ImGuiDir_None; - - // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. - if (policy == ImGuiPopupPositionPolicy_Tooltip) - return ref_pos + ImVec2(2, 2); - - // Otherwise try to keep within display - ImVec2 pos = ref_pos; - pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); - pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); - return pos; -} - -// Note that this is used for popups, which can overlap the non work-area of individual viewports. -ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - IM_UNUSED(window); - ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect(); - ImVec2 padding = g.Style.DisplaySafeAreaPadding; - r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); - return r_screen; -} - -ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - - ImRect r_outer = GetPopupAllowedExtentRect(window); - if (window->Flags & ImGuiWindowFlags_ChildMenu) - { - // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. - // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. - IM_ASSERT(g.CurrentWindow == window); - ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window; - float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). - ImRect r_avoid; - if (parent_window->DC.MenuBarAppending) - r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field - else - r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); - return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); - } - if (window->Flags & ImGuiWindowFlags_Popup) - { - return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here - } - if (window->Flags & ImGuiWindowFlags_Tooltip) - { - // Position tooltip (always follows mouse) - float sc = g.Style.MouseCursorScale; - ImVec2 ref_pos = NavCalcPreferredRefPos(); - ImRect r_avoid; - if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) - r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); - else - r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. - return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); - } - IM_ASSERT(0); - return window->Pos; -} - -//----------------------------------------------------------------------------- -// [SECTION] KEYBOARD/GAMEPAD NAVIGATION -//----------------------------------------------------------------------------- - -// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked. -// In our terminology those should be interchangeable, yet right now this is super confusing. -// Those two functions are merely a legacy artifact, so at minimum naming should be clarified. - -void ImGui::SetNavWindow(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (g.NavWindow != window) - { - IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : ""); - g.NavWindow = window; - } - g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; - NavUpdateAnyRequestFlag(); -} - -void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindow != NULL); - IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu); - g.NavId = id; - g.NavLayer = nav_layer; - g.NavFocusScopeId = focus_scope_id; - g.NavWindow->NavLastIds[nav_layer] = id; - g.NavWindow->NavRectRel[nav_layer] = rect_rel; -} - -void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(id != 0); - - if (g.NavWindow != window) - SetNavWindow(window); - - // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid. - // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text) - const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; - g.NavId = id; - g.NavLayer = nav_layer; - g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; - window->NavLastIds[nav_layer] = id; - if (g.LastItemData.ID == id) - window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); - - if (g.ActiveIdSource == ImGuiInputSource_Nav) - g.NavDisableMouseHover = true; - else - g.NavDisableHighlight = true; -} - -ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) -{ - if (ImFabs(dx) > ImFabs(dy)) - return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; - return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; -} - -static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1) -{ - if (a1 < b0) - return a1 - b0; - if (b1 < a0) - return a0 - b1; - return 0.0f; -} - -static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect) -{ - if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) - { - r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); - r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); - } - else // FIXME: PageUp/PageDown are leaving move_dir == None - { - r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); - r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); - } -} - -// Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool ImGui::NavScoreItem(ImGuiNavItemData* result) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.NavLayer != window->DC.NavLayerCurrent) - return false; - - // FIXME: Those are not good variables names - ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle - const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) - g.NavScoringDebugCount++; - - // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring - if (window->ParentWindow == g.NavWindow) - { - IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened); - if (!window->ClipRect.Overlaps(cand)) - return false; - cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window - } - - // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items) - // For example, this ensure that items in one column are not reached when moving vertically from items in another column. - NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); - - // Compute distance between boxes - // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. - float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); - float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items - if (dby != 0.0f && dbx != 0.0f) - dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); - float dist_box = ImFabs(dbx) + ImFabs(dby); - - // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter) - float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); - float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); - float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee) - - // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance - ImGuiDir quadrant; - float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; - if (dbx != 0.0f || dby != 0.0f) - { - // For non-overlapping boxes, use distance between boxes - dax = dbx; - day = dby; - dist_axial = dist_box; - quadrant = ImGetDirQuadrantFromDelta(dbx, dby); - } - else if (dcx != 0.0f || dcy != 0.0f) - { - // For overlapping boxes with different centers, use distance between centers - dax = dcx; - day = dcy; - dist_axial = dist_center; - quadrant = ImGetDirQuadrantFromDelta(dcx, dcy); - } - else - { - // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) - quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; - } - -#if IMGUI_DEBUG_NAV_SCORING - char buf[128]; - if (IsMouseHoveringRect(cand.Min, cand.Max)) - { - ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); - ImDrawList* draw_list = GetForegroundDrawList(window); - draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); - draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); - draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150)); - draw_list->AddText(cand.Max, ~0U, buf); - } - else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. - { - if (quadrant == g.NavMoveDir) - { - ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); - ImDrawList* draw_list = GetForegroundDrawList(window); - draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); - draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf); - } - } -#endif - - // Is it in the quadrant we're interesting in moving to? - bool new_best = false; - const ImGuiDir move_dir = g.NavMoveDir; - if (quadrant == move_dir) - { - // Does it beat the current best candidate? - if (dist_box < result->DistBox) - { - result->DistBox = dist_box; - result->DistCenter = dist_center; - return true; - } - if (dist_box == result->DistBox) - { - // Try using distance between center points to break ties - if (dist_center < result->DistCenter) - { - result->DistCenter = dist_center; - new_best = true; - } - else if (dist_center == result->DistCenter) - { - // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items - // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), - // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. - if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance - new_best = true; - } - } - } - - // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches - // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) - // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. - // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. - // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? - if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match - if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f)) - { - result->DistAxial = dist_axial; - new_best = true; - } - - return new_best; -} - -static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - result->Window = window; - result->ID = g.LastItemData.ID; - result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; - result->InFlags = g.LastItemData.InFlags; - result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect); -} - -// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) -// This is called after LastItemData is set. -static void ImGui::NavProcessItem() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const ImGuiID id = g.LastItemData.ID; - const ImRect nav_bb = g.LastItemData.NavRect; - const ImGuiItemFlags item_flags = g.LastItemData.InFlags; - - // Process Init Request - if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0) - { - // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback - const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0; - if (candidate_for_nav_default_focus || g.NavInitResultId == 0) - { - g.NavInitResultId = id; - g.NavInitResultRectRel = WindowRectAbsToRel(window, nav_bb); - } - if (candidate_for_nav_default_focus) - { - g.NavInitRequest = false; // Found a match, clear request - NavUpdateAnyRequestFlag(); - } - } - - // Process Move Request (scoring for navigation) - // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) - if (g.NavMoveScoringItems) - { - const bool is_tab_stop = (item_flags & ImGuiItemFlags_Inputable) && (item_flags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; - const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) != 0; - if (is_tabbing) - { - if (is_tab_stop || (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)) - NavProcessItemForTabbingRequest(id); - } - else if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) - { - ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - if (!is_tabbing) - { - if (NavScoreItem(result)) - NavApplyItemToResult(result); - - // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. - const float VISIBLE_RATIO = 0.70f; - if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) - if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) - if (NavScoreItem(&g.NavMoveResultLocalVisible)) - NavApplyItemToResult(&g.NavMoveResultLocalVisible); - } - } - } - - // Update window-relative bounding box of navigated item - if (g.NavId == id) - { - if (g.NavWindow != window) - SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window. - g.NavLayer = window->DC.NavLayerCurrent; - g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; - g.NavIdIsAlive = true; - window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position) - } -} - -// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest(). -// Note that SetKeyboardFocusHere() API calls are considered tabbing requests! -// - Case 1: no nav/active id: set result to first eligible item, stop storing. -// - Case 2: tab forward: on ref id set counter, on counter elapse store result -// - Case 3: tab forward wrap: set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request -// - Case 4: tab backward: store all results, on ref id pick prev, stop storing -// - Case 5: tab backward wrap: store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested -void ImGui::NavProcessItemForTabbingRequest(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - - // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows) - ImGuiNavItemData* result = &g.NavMoveResultLocal; - if (g.NavTabbingDir == +1) - { - // Tab Forward or SetKeyboardFocusHere() with >= 0 - if (g.NavTabbingResultFirst.ID == 0) - NavApplyItemToResult(&g.NavTabbingResultFirst); - if (--g.NavTabbingCounter == 0) - NavMoveRequestResolveWithLastItem(result); - else if (g.NavId == id) - g.NavTabbingCounter = 1; - } - else if (g.NavTabbingDir == -1) - { - // Tab Backward - if (g.NavId == id) - { - if (result->ID) - { - g.NavMoveScoringItems = false; - NavUpdateAnyRequestFlag(); - } - } - else - { - NavApplyItemToResult(result); - } - } - else if (g.NavTabbingDir == 0) - { - // Tab Init - if (g.NavTabbingResultFirst.ID == 0) - NavMoveRequestResolveWithLastItem(&g.NavTabbingResultFirst); - } -} - -bool ImGui::NavMoveRequestButNoResultYet() -{ - ImGuiContext& g = *GImGui; - return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; -} - -// FIXME: ScoringRect is not set -void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindow != NULL); - - if (move_flags & ImGuiNavMoveFlags_Tabbing) - move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; - - g.NavMoveSubmitted = g.NavMoveScoringItems = true; - g.NavMoveDir = move_dir; - g.NavMoveDirForDebug = move_dir; - g.NavMoveClipDir = clip_dir; - g.NavMoveFlags = move_flags; - g.NavMoveScrollFlags = scroll_flags; - g.NavMoveForwardToNextFrame = false; - g.NavMoveKeyMods = g.IO.KeyMods; - g.NavMoveResultLocal.Clear(); - g.NavMoveResultLocalVisible.Clear(); - g.NavMoveResultOther.Clear(); - g.NavTabbingCounter = 0; - g.NavTabbingResultFirst.Clear(); - NavUpdateAnyRequestFlag(); -} - -void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result) -{ - ImGuiContext& g = *GImGui; - g.NavMoveScoringItems = false; // Ensure request doesn't need more processing - NavApplyItemToResult(result); - NavUpdateAnyRequestFlag(); -} - -void ImGui::NavMoveRequestCancel() -{ - ImGuiContext& g = *GImGui; - g.NavMoveSubmitted = g.NavMoveScoringItems = false; - NavUpdateAnyRequestFlag(); -} - -// Forward will reuse the move request again on the next frame (generally with modifications done to it) -void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavMoveForwardToNextFrame == false); - NavMoveRequestCancel(); - g.NavMoveForwardToNextFrame = true; - g.NavMoveDir = move_dir; - g.NavMoveClipDir = clip_dir; - g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded; - g.NavMoveScrollFlags = scroll_flags; -} - -// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire -// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. -void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY - // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it, NavEndFrame() will do the same test - if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main) - g.NavMoveFlags |= wrap_flags; -} - -// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). -// This way we could find the last focused window among our children. It would be much less confusing this way? -static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) -{ - ImGuiWindow* parent = nav_window; - while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) - parent = parent->ParentWindow; - if (parent && parent != nav_window) - parent->NavLastChildNavWindow = nav_window; -} - -// Restore the last focused child. -// Call when we are expected to land on the Main Layer (0) after FocusWindow() -static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) -{ - if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive) - return window->NavLastChildNavWindow; - return window; -} - -void ImGui::NavRestoreLayer(ImGuiNavLayer layer) -{ - ImGuiContext& g = *GImGui; - if (layer == ImGuiNavLayer_Main) - { - ImGuiWindow* prev_nav_window = g.NavWindow; - g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests? - if (prev_nav_window) - IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name); - } - ImGuiWindow* window = g.NavWindow; - if (window->NavLastIds[layer] != 0) - { - SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); - } - else - { - g.NavLayer = layer; - NavInitWindow(window, true); - } -} - -void ImGui::NavRestoreHighlightAfterMove() -{ - ImGuiContext& g = *GImGui; - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; -} - -static inline void ImGui::NavUpdateAnyRequestFlag() -{ - ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); - if (g.NavAnyRequest) - IM_ASSERT(g.NavWindow != NULL); -} - -// This needs to be called before we submit any widget (aka in or before Begin) -void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(window == g.NavWindow); - - if (window->Flags & ImGuiWindowFlags_NoNavInputs) - { - g.NavId = g.NavFocusScopeId = 0; - return; - } - - bool init_for_nav = false; - if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) - init_for_nav = true; - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer); - if (init_for_nav) - { - SetNavID(0, g.NavLayer, 0, ImRect()); - g.NavInitRequest = true; - g.NavInitRequestFromMove = false; - g.NavInitResultId = 0; - g.NavInitResultRectRel = ImRect(); - NavUpdateAnyRequestFlag(); - } - else - { - g.NavId = window->NavLastIds[0]; - g.NavFocusScopeId = 0; - } -} - -static ImVec2 ImGui::NavCalcPreferredRefPos() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.NavWindow; - if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window) - { - // Mouse (we need a fallback in case the mouse becomes invalid after being used) - // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard. - // In theory we could move that +1.0f offset in OpenPopupEx() - ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos; - return ImVec2(p.x + 1.0f, p.y); - } - else - { - // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item - // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?) - ImRect rect_rel = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); - if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX)) - { - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); - rect_rel.Translate(window->Scroll - next_scroll); - } - ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImGuiViewport* viewport = GetMainViewport(); - return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. - } -} - -const char* ImGui::GetNavInputName(ImGuiNavInput n) -{ - static const char* names[] = - { - "Activate", "Cancel", "Input", "Menu", "DpadLeft", "DpadRight", "DpadUp", "DpadDown", "LStickLeft", "LStickRight", "LStickUp", "LStickDown", - "FocusPrev", "FocusNext", "TweakSlow", "TweakFast", "KeyLeft", "KeyRight", "KeyUp", "KeyDown" - }; - IM_ASSERT(IM_ARRAYSIZE(names) == ImGuiNavInput_COUNT); - IM_ASSERT(n >= 0 && n < ImGuiNavInput_COUNT); - return names[n]; -} - -float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode) -{ - ImGuiContext& g = *GImGui; - if (mode == ImGuiNavReadMode_Down) - return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) - - const float t = g.IO.NavInputsDownDuration[n]; - if (t < 0.0f && mode == ImGuiNavReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. - return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); - if (t < 0.0f) - return 0.0f; - if (mode == ImGuiNavReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. - return (t == 0.0f) ? 1.0f : 0.0f; - if (mode == ImGuiNavReadMode_Repeat) - return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f); - if (mode == ImGuiNavReadMode_RepeatSlow) - return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f); - if (mode == ImGuiNavReadMode_RepeatFast) - return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f); - return 0.0f; -} - -ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiNavReadMode mode, float slow_factor, float fast_factor) -{ - ImVec2 delta(0.0f, 0.0f); - if (dir_sources & ImGuiNavDirSourceFlags_RawKeyboard) - delta += ImVec2((float)IsKeyDown(ImGuiKey_RightArrow) - (float)IsKeyDown(ImGuiKey_LeftArrow), (float)IsKeyDown(ImGuiKey_DownArrow) - (float)IsKeyDown(ImGuiKey_UpArrow)); - if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); - if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta *= slow_factor; - if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) - delta *= fast_factor; - return delta; -} - -static void ImGui::NavUpdate() -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - - io.WantSetMousePos = false; - //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV("[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); - - // Update Gamepad->Nav inputs mapping - // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) - const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; - if (nav_gamepad_active && g.IO.BackendUsingLegacyNavInputArray == false) - { - for (int n = 0; n < ImGuiNavInput_COUNT; n++) - IM_ASSERT(io.NavInputs[n] == 0.0f && "Backend needs to either only use io.AddKeyEvent()/io.AddKeyAnalogEvent(), either only fill legacy io.NavInputs[]. Not both!"); - #define NAV_MAP_KEY(_KEY, _NAV_INPUT, _ACTIVATE_NAV) do { io.NavInputs[_NAV_INPUT] = io.KeysData[_KEY - ImGuiKey_KeysData_OFFSET].AnalogValue; if (_ACTIVATE_NAV && io.NavInputs[_NAV_INPUT] > 0.0f) { g.NavInputSource = ImGuiInputSource_Gamepad; } } while (0) - NAV_MAP_KEY(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate, true); - NAV_MAP_KEY(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel, true); - NAV_MAP_KEY(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu, true); - NAV_MAP_KEY(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input, true); - NAV_MAP_KEY(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft, true); - NAV_MAP_KEY(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight, true); - NAV_MAP_KEY(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp, true); - NAV_MAP_KEY(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown, true); - NAV_MAP_KEY(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, false); - NAV_MAP_KEY(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, false); - NAV_MAP_KEY(ImGuiKey_GamepadL1, ImGuiNavInput_TweakSlow, false); - NAV_MAP_KEY(ImGuiKey_GamepadR1, ImGuiNavInput_TweakFast, false); - NAV_MAP_KEY(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft, false); - NAV_MAP_KEY(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight, false); - NAV_MAP_KEY(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp, false); - NAV_MAP_KEY(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown, false); - #undef NAV_MAP_KEY - } - - // Update Keyboard->Nav inputs mapping - const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - if (nav_keyboard_active) - { - #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(_KEY)) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0) - NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); - NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); - NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); - NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); - NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); - NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); - NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); - if (io.KeyCtrl) - io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; - if (io.KeyShift) - io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - #undef NAV_MAP_KEY - } - memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) - io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f; - - // Process navigation init request (select first/default focus) - if (g.NavInitResultId != 0) - NavInitRequestApplyResult(); - g.NavInitRequest = false; - g.NavInitRequestFromMove = false; - g.NavInitResultId = 0; - g.NavJustMovedToId = 0; - - // Process navigation move request - if (g.NavMoveSubmitted) - NavMoveRequestApplyResult(); - g.NavTabbingCounter = 0; - g.NavMoveSubmitted = g.NavMoveScoringItems = false; - - // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling) - bool set_mouse_pos = false; - if (g.NavMousePosDirty && g.NavIdIsAlive) - if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) - set_mouse_pos = true; - g.NavMousePosDirty = false; - IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu); - - // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0 - if (g.NavWindow) - NavSaveLastChildNavWindowIntoParent(g.NavWindow); - if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) - g.NavWindow->NavLastChildNavWindow = NULL; - - // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) - NavUpdateWindowing(); - - // Set output flags for user application - io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); - io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); - - // Process NavCancel input (to close a popup, get back to parent, clear focus) - NavUpdateCancelRequest(); - - // Process manual activation request - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavActivateInputId = 0; - g.NavActivateFlags = ImGuiActivateFlags_None; - if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); - bool input_down = IsNavInputDown(ImGuiNavInput_Input); - bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiNavReadMode_Pressed); - bool input_pressed = input_down && IsNavInputTest(ImGuiNavInput_Input, ImGuiNavReadMode_Pressed); - if (g.ActiveId == 0 && activate_pressed) - { - g.NavActivateId = g.NavId; - g.NavActivateFlags = ImGuiActivateFlags_PreferTweak; - } - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed) - { - g.NavActivateInputId = g.NavId; - g.NavActivateFlags = ImGuiActivateFlags_PreferInput; - } - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) - g.NavActivateDownId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) - g.NavActivatePressedId = g.NavId; - } - if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - g.NavDisableHighlight = true; - if (g.NavActivateId != 0) - IM_ASSERT(g.NavActivateDownId == g.NavActivateId); - - // Process programmatic activation request - // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others) - if (g.NavNextActivateId != 0) - { - if (g.NavNextActivateFlags & ImGuiActivateFlags_PreferInput) - g.NavActivateInputId = g.NavNextActivateId; - else - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId; - g.NavActivateFlags = g.NavNextActivateFlags; - } - g.NavNextActivateId = 0; - - // Process move requests - NavUpdateCreateMoveRequest(); - if (g.NavMoveDir == ImGuiDir_None) - NavUpdateCreateTabbingRequest(); - NavUpdateAnyRequestFlag(); - g.NavIdIsAlive = false; - - // Scrolling - if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) - { - // *Fallback* manual-scroll with Nav directional keys when window has no navigable item - ImGuiWindow* window = g.NavWindow; - const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - const ImGuiDir move_dir = g.NavMoveDir; - if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && move_dir != ImGuiDir_None) - { - if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) - SetScrollX(window, ImFloor(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); - if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) - SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); - } - - // *Normal* Manual scroll with NavScrollXXX keys - // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. - ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiNavReadMode_Down, 1.0f / 10.0f, 10.0f); - if (scroll_dir.x != 0.0f && window->ScrollbarX) - SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); - if (scroll_dir.y != 0.0f) - SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); - } - - // Always prioritize mouse highlight if navigation is disabled - if (!nav_keyboard_active && !nav_gamepad_active) - { - g.NavDisableHighlight = true; - g.NavDisableMouseHover = set_mouse_pos = false; - } - - // Update mouse position if requested - // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied) - if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - { - io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); - io.WantSetMousePos = true; - //IMGUI_DEBUG_LOG_IO("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); - } - - // [DEBUG] - g.NavScoringDebugCount = 0; -#if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) - { - ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); - if (1) { for (int layer = 0; layer < 2; layer++) { ImRect r = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255,200,0,255)); } } // [DEBUG] - if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } - } -#endif -} - -void ImGui::NavInitRequestApplyResult() -{ - // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) - ImGuiContext& g = *GImGui; - if (!g.NavWindow) - return; - - // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) - // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); - SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); - g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result - if (g.NavInitRequestFromMove) - NavRestoreHighlightAfterMove(); -} - -void ImGui::NavUpdateCreateMoveRequest() -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - ImGuiWindow* window = g.NavWindow; - - if (g.NavMoveForwardToNextFrame && window != NULL) - { - // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) - // (preserve most state, which were already set by the NavMoveRequestForward() function) - IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); - IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded); - IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); - } - else - { - // Initiate directional inputs request - g.NavMoveDir = ImGuiDir_None; - g.NavMoveFlags = ImGuiNavMoveFlags_None; - g.NavMoveScrollFlags = ImGuiScrollFlags_None; - if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) - { - const ImGuiNavReadMode read_mode = ImGuiNavReadMode_Repeat; - if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } - } - g.NavMoveClipDir = g.NavMoveDir; - g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); - } - - // Update PageUp/PageDown/Home/End scroll - // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? - const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - float scoring_rect_offset_y = 0.0f; - if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) - scoring_rect_offset_y = NavUpdatePageUpPageDown(); - if (scoring_rect_offset_y != 0.0f) - { - g.NavScoringNoClipRect = window->InnerRect; - g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y); - } - - // [DEBUG] Always send a request -#if IMGUI_DEBUG_NAV_SCORING - if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C)) - g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3); - if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None) - { - g.NavMoveDir = g.NavMoveDirForDebug; - g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult; - } -#endif - - // Submit - g.NavMoveForwardToNextFrame = false; - if (g.NavMoveDir != ImGuiDir_None) - NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); - - // Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match) - if (g.NavMoveSubmitted && g.NavId == 0) - { - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "", g.NavLayer); - g.NavInitRequest = g.NavInitRequestFromMove = true; - g.NavInitResultId = 0; - g.NavDisableHighlight = false; - } - - // When using gamepad, we project the reference nav bounding box into window visible area. - // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative - // (can't focus a visible object like we can with the mouse). - if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded)) - { - bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0; - bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0; - ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1))); - if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer])) - { - //IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n"); - float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f); - float pad_y = ImMin(inner_rect_rel.GetHeight(), window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item - inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX; - inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX; - inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX; - inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX; - window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel); - g.NavId = g.NavFocusScopeId = 0; - } - } - - // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) - ImRect scoring_rect; - if (window != NULL) - { - ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); - scoring_rect = WindowRectRelToAbs(window, nav_rect_rel); - scoring_rect.TranslateY(scoring_rect_offset_y); - scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x); - scoring_rect.Max.x = scoring_rect.Min.x; - IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). - //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG] - //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG] - } - g.NavScoringRect = scoring_rect; - g.NavScoringNoClipRect.Add(scoring_rect); -} - -void ImGui::NavUpdateCreateTabbingRequest() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.NavWindow; - IM_ASSERT(g.NavMoveDir == ImGuiDir_None); - if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs)) - return; - - const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !g.IO.KeyCtrl && !g.IO.KeyAlt; - if (!tab_pressed) - return; - - // Initiate tabbing request - // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!) - // Initially this was designed to use counters and modulo arithmetic, but that could not work with unsubmitted items (list clipper). Instead we use a strategy close to other move requests. - // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping. - //// FIXME: We use (g.ActiveId == 0) but (g.NavDisableHighlight == false) might be righter once we can tab through anything - g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1; - ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; - ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down; - NavMoveRequestSubmit(ImGuiDir_None, clip_dir, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. - g.NavTabbingCounter = -1; -} - -// Apply result from previous frame navigation directional move request. Always called from NavUpdate() -void ImGui::NavMoveRequestApplyResult() -{ - ImGuiContext& g = *GImGui; -#if IMGUI_DEBUG_NAV_SCORING - if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times - return; -#endif - - // Select which result to use - ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL; - - // Tabbing forward wrap - if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) - if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID) - result = &g.NavTabbingResultFirst; - - // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) - if (result == NULL) - { - if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) - g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; - if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) - NavRestoreHighlightAfterMove(); - return; - } - - // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. - if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) - if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId) - result = &g.NavMoveResultLocalVisible; - - // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. - if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) - if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter)) - result = &g.NavMoveResultOther; - IM_ASSERT(g.NavWindow && result->Window); - - // Scroll to keep newly navigated item fully into view. - if (g.NavLayer == ImGuiNavLayer_Main) - { - if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY) - { - // FIXME: Should remove this - float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; - SetScrollY(result->Window, scroll_target); - } - else - { - ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); - ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); - } - } - - if (g.NavWindow != result->Window) - { - IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name); - g.NavWindow = result->Window; - } - if (g.ActiveId != result->ID) - ClearActiveID(); - if (g.NavId != result->ID) - { - // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) - g.NavJustMovedToId = result->ID; - g.NavJustMovedToFocusScopeId = result->FocusScopeId; - g.NavJustMovedToKeyMods = g.NavMoveKeyMods; - } - - // Focus - IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); - SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); - - // Tabbing: Activates Inputable or Focus non-Inputable - if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable)) - { - g.NavNextActivateId = result->ID; - g.NavNextActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState; - g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; - } - - // Activate - if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate) - { - g.NavNextActivateId = result->ID; - g.NavNextActivateFlags = ImGuiActivateFlags_None; - } - - // Enable nav highlight - if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) - NavRestoreHighlightAfterMove(); -} - -// Process NavCancel input (to close a popup, get back to parent, clear focus) -// FIXME: In order to support e.g. Escape to clear a selection we'll need: -// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it. -// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept -static void ImGui::NavUpdateCancelRequest() -{ - ImGuiContext& g = *GImGui; - if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiNavReadMode_Pressed)) - return; - - IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); - if (g.ActiveId != 0) - { - if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) - ClearActiveID(); - } - else if (g.NavLayer != ImGuiNavLayer_Main) - { - // Leave the "menu" layer - NavRestoreLayer(ImGuiNavLayer_Main); - NavRestoreHighlightAfterMove(); - } - else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) - { - // Exit child window - ImGuiWindow* child_window = g.NavWindow; - ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - IM_ASSERT(child_window->ChildId != 0); - ImRect child_rect = child_window->Rect(); - FocusWindow(parent_window); - SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect)); - NavRestoreHighlightAfterMove(); - } - else if (g.OpenPopupStack.Size > 0 && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) - { - // Close open popup/menu - ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); - } - else - { - // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were - if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) - g.NavWindow->NavLastIds[0] = 0; - g.NavId = g.NavFocusScopeId = 0; - } -} - -// Handle PageUp/PageDown/Home/End keys -// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request -// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference -// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid? -static float ImGui::NavUpdatePageUpPageDown() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.NavWindow; - if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL) - return 0.0f; - - const bool page_up_held = IsKeyDown(ImGuiKey_PageUp) && !IsActiveIdUsingKey(ImGuiKey_PageUp); - const bool page_down_held = IsKeyDown(ImGuiKey_PageDown) && !IsActiveIdUsingKey(ImGuiKey_PageDown); - const bool home_pressed = IsKeyPressed(ImGuiKey_Home) && !IsActiveIdUsingKey(ImGuiKey_Home); - const bool end_pressed = IsKeyPressed(ImGuiKey_End) && !IsActiveIdUsingKey(ImGuiKey_End); - if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out - return 0.0f; - - if (g.NavLayer != ImGuiNavLayer_Main) - NavRestoreLayer(ImGuiNavLayer_Main); - - if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) - { - // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(ImGuiKey_PageUp, true)) - SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); - else if (IsKeyPressed(ImGuiKey_PageDown, true)) - SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); - else if (home_pressed) - SetScrollY(window, 0.0f); - else if (end_pressed) - SetScrollY(window, window->ScrollMax.y); - } - else - { - ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; - const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); - float nav_scoring_rect_offset_y = 0.0f; - if (IsKeyPressed(ImGuiKey_PageUp, true)) - { - nav_scoring_rect_offset_y = -page_offset_y; - g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Up; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (IsKeyPressed(ImGuiKey_PageDown, true)) - { - nav_scoring_rect_offset_y = +page_offset_y; - g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Down; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (home_pressed) - { - // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y - // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result. - // Preserve current horizontal position if we have any. - nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f; - if (nav_rect_rel.IsInverted()) - nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; - g.NavMoveDir = ImGuiDir_Down; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; - // FIXME-NAV: MoveClipDir left to _None, intentional? - } - else if (end_pressed) - { - nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y; - if (nav_rect_rel.IsInverted()) - nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; - g.NavMoveDir = ImGuiDir_Up; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; - // FIXME-NAV: MoveClipDir left to _None, intentional? - } - return nav_scoring_rect_offset_y; - } - return 0.0f; -} - -static void ImGui::NavEndFrame() -{ - ImGuiContext& g = *GImGui; - - // Show CTRL+TAB list window - if (g.NavWindowingTarget != NULL) - NavUpdateWindowingOverlay(); - - // Perform wrap-around in menus - // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly. - // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame. - const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; - if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) - NavUpdateCreateWrappingRequest(); -} - -static void ImGui::NavUpdateCreateWrappingRequest() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.NavWindow; - - bool do_forward = false; - ImRect bb_rel = window->NavRectRel[g.NavLayer]; - ImGuiDir clip_dir = g.NavMoveDir; - const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; - if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) - { - bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row - clip_dir = ImGuiDir_Up; - } - do_forward = true; - } - if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) - { - bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row - clip_dir = ImGuiDir_Down; - } - do_forward = true; - } - if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column - clip_dir = ImGuiDir_Left; - } - do_forward = true; - } - if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column - clip_dir = ImGuiDir_Right; - } - do_forward = true; - } - if (!do_forward) - return; - window->NavRectRel[g.NavLayer] = bb_rel; - NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); -} - -static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - IM_UNUSED(g); - int order = window->FocusOrder; - IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking) - IM_ASSERT(g.WindowsFocusOrder[order] == window); - return order; -} - -static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) -{ - ImGuiContext& g = *GImGui; - for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir) - if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i])) - return g.WindowsFocusOrder[i]; - return NULL; -} - -static void NavUpdateWindowingHighlightWindow(int focus_change_dir) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindowingTarget); - if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) - return; - - const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget); - ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); - if (!window_target) - window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); - if (window_target) // Don't reset windowing target if there's a single window in the list - g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; - g.NavWindowingToggleLayer = false; -} - -// Windowing management mode -// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer) -// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer) -static void ImGui::NavUpdateWindowing() -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - - ImGuiWindow* apply_focus_window = NULL; - bool apply_toggle_layer = false; - - ImGuiWindow* modal_window = GetTopMostPopupModal(); - bool allow_windowing = (modal_window == NULL); - if (!allow_windowing) - g.NavWindowingTarget = NULL; - - // Fade out - if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) - { - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f); - if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) - g.NavWindowingTargetAnim = NULL; - } - - // Start CTRL+Tab or Square+L/R window selection - const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiNavReadMode_Pressed); - const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab); - if (start_windowing_with_gamepad || start_windowing_with_keyboard) - if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) - { - g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; - g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; - g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer - g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; - } - - // Gamepad update - g.NavWindowingTimer += io.DeltaTime; - if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad) - { - // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); - - // Select window to focus - const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiNavReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiNavReadMode_RepeatSlow); - if (focus_change_dir != 0) - { - NavUpdateWindowingHighlightWindow(focus_change_dir); - g.NavWindowingHighlightAlpha = 1.0f; - } - - // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most) - if (!IsNavInputDown(ImGuiNavInput_Menu)) - { - g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. - if (g.NavWindowingToggleLayer && g.NavWindow) - apply_toggle_layer = true; - else if (!g.NavWindowingToggleLayer) - apply_focus_window = g.NavWindowingTarget; - g.NavWindowingTarget = NULL; - } - } - - // Keyboard: Focus - if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard) - { - // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f - if (IsKeyPressed(ImGuiKey_Tab, true)) - NavUpdateWindowingHighlightWindow(io.KeyShift ? +1 : -1); - if (!io.KeyCtrl) - apply_focus_window = g.NavWindowingTarget; - } - - // Keyboard: Press and Release ALT to toggle menu layer - // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer. - // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. - const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - if (nav_keyboard_active && IsKeyPressed(ImGuiKey_ModAlt)) - { - g.NavWindowingToggleLayer = true; - g.NavInputSource = ImGuiInputSource_Keyboard; - } - if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard) - { - // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370) - // We cancel toggling nav layer when other modifiers are pressed. (See #4439) - if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper) - g.NavWindowingToggleLayer = false; - - // Apply layer toggle on release - // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss. - if (IsKeyReleased(ImGuiKey_ModAlt) && g.NavWindowingToggleLayer) - if (g.ActiveId == 0 || g.ActiveIdAllowOverlap) - if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev)) - apply_toggle_layer = true; - if (!IsKeyDown(ImGuiKey_ModAlt)) - g.NavWindowingToggleLayer = false; - } - - // Move window - if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) - { - ImVec2 move_delta; - if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiNavReadMode_Down); - if (g.NavInputSource == ImGuiInputSource_Gamepad) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiNavReadMode_Down); - if (move_delta.x != 0.0f || move_delta.y != 0.0f) - { - const float NAV_MOVE_SPEED = 800.0f; - const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well - ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; - SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always); - g.NavDisableMouseHover = true; - } - } - - // Apply final focus - if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) - { - ClearActiveID(); - NavRestoreHighlightAfterMove(); - apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); - ClosePopupsOverWindow(apply_focus_window, false); - FocusWindow(apply_focus_window); - if (apply_focus_window->NavLastIds[0] == 0) - NavInitWindow(apply_focus_window, false); - - // If the window has ONLY a menu layer (no main layer), select it directly - // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame, - // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since - // the target window as already been previewed once. - // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases, - // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask* - // won't be valid. - if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu)) - g.NavLayer = ImGuiNavLayer_Menu; - } - if (apply_focus_window) - g.NavWindowingTarget = NULL; - - // Apply menu/layer toggle - if (apply_toggle_layer && g.NavWindow) - { - ClearActiveID(); - - // Move to parent menu if necessary - ImGuiWindow* new_nav_window = g.NavWindow; - while (new_nav_window->ParentWindow - && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 - && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 - && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) - new_nav_window = new_nav_window->ParentWindow; - if (new_nav_window != g.NavWindow) - { - ImGuiWindow* old_nav_window = g.NavWindow; - FocusWindow(new_nav_window); - new_nav_window->NavLastChildNavWindow = old_nav_window; - } - - // Toggle layer - const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; - if (new_nav_layer != g.NavLayer) - { - // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?) - if (new_nav_layer == ImGuiNavLayer_Menu) - g.NavWindow->NavLastIds[new_nav_layer] = 0; - NavRestoreLayer(new_nav_layer); - NavRestoreHighlightAfterMove(); - } - } -} - -// Window has already passed the IsWindowNavFocusable() -static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) -{ - if (window->Flags & ImGuiWindowFlags_Popup) - return "(Popup)"; - if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) - return "(Main menu bar)"; - return "(Untitled)"; -} - -// Overlay displayed when using CTRL+TAB. Called by EndFrame(). -void ImGui::NavUpdateWindowingOverlay() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindowingTarget != NULL); - - if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) - return; - - if (g.NavWindowingListWindow == NULL) - g.NavWindowingListWindow = FindWindowByName("###NavWindowingList"); - const ImGuiViewport* viewport = GetMainViewport(); - SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); - SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); - Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); - for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) - { - ImGuiWindow* window = g.WindowsFocusOrder[n]; - IM_ASSERT(window != NULL); // Fix static analyzers - if (!IsWindowNavFocusable(window)) - continue; - const char* label = window->Name; - if (label == FindRenderedTextEnd(label)) - label = GetFallbackWindowNameForWindowingList(window); - Selectable(label, g.NavWindowingTarget == window); - } - End(); - PopStyleVar(); -} - - -//----------------------------------------------------------------------------- -// [SECTION] DRAG AND DROP -//----------------------------------------------------------------------------- - -bool ImGui::IsDragDropActive() -{ - ImGuiContext& g = *GImGui; - return g.DragDropActive; -} - -void ImGui::ClearDragDrop() -{ - ImGuiContext& g = *GImGui; - g.DragDropActive = false; - g.DragDropPayload.Clear(); - g.DragDropAcceptFlags = ImGuiDragDropFlags_None; - g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; - g.DragDropAcceptIdCurrRectSurface = FLT_MAX; - g.DragDropAcceptFrameCount = -1; - - g.DragDropPayloadBufHeap.clear(); - memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); -} - -// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource() -// If the item has an identifier: -// - This assume/require the item to be activated (typically via ButtonBehavior). -// - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button. -// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag. -// If the item has no identifier: -// - Currently always assume left mouse button. -bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button, - // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic). - ImGuiMouseButton mouse_button = ImGuiMouseButton_Left; - - bool source_drag_active = false; - ImGuiID source_id = 0; - ImGuiID source_parent_id = 0; - if (!(flags & ImGuiDragDropFlags_SourceExtern)) - { - source_id = g.LastItemData.ID; - if (source_id != 0) - { - // Common path: items with ID - if (g.ActiveId != source_id) - return false; - if (g.ActiveIdMouseButton != -1) - mouse_button = g.ActiveIdMouseButton; - if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) - return false; - g.ActiveIdAllowOverlap = false; - } - else - { - // Uncommon path: items without ID - if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) - return false; - if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) - return false; - - // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: - // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag. - if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) - { - IM_ASSERT(0); - return false; - } - - // Magic fallback to handle items with no assigned ID, e.g. Text(), Image() - // We build a throwaway ID based on current ID stack + relative AABB of items in window. - // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled. - // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. - // Rely on keeping other window->LastItemXXX fields intact. - source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect); - KeepAliveID(source_id); - bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id); - if (is_hovered && g.IO.MouseClicked[mouse_button]) - { - SetActiveID(source_id, window); - FocusWindow(window); - } - if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker. - g.ActiveIdAllowOverlap = is_hovered; - } - if (g.ActiveId != source_id) - return false; - source_parent_id = window->IDStack.back(); - source_drag_active = IsMouseDragging(mouse_button); - - // Disable navigation and key inputs while dragging + cancel existing request if any - SetActiveIdUsingNavAndKeys(); - } - else - { - window = NULL; - source_id = ImHashStr("#SourceExtern"); - source_drag_active = true; - } - - if (source_drag_active) - { - if (!g.DragDropActive) - { - IM_ASSERT(source_id != 0); - ClearDragDrop(); - ImGuiPayload& payload = g.DragDropPayload; - payload.SourceId = source_id; - payload.SourceParentId = source_parent_id; - g.DragDropActive = true; - g.DragDropSourceFlags = flags; - g.DragDropMouseButton = mouse_button; - if (payload.SourceId == g.ActiveId) - g.ActiveIdNoClearOnFocusLoss = true; - } - g.DragDropSourceFrameCount = g.FrameCount; - g.DragDropWithinSource = true; - - if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) - { - // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) - // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. - BeginTooltip(); - if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) - { - ImGuiWindow* tooltip_window = g.CurrentWindow; - tooltip_window->Hidden = tooltip_window->SkipItems = true; - tooltip_window->HiddenFramesCanSkipItems = 1; - } - } - - if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) - g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; - - return true; - } - return false; -} - -void ImGui::EndDragDropSource() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.DragDropActive); - IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?"); - - if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) - EndTooltip(); - - // Discard the drag if have not called SetDragDropPayload() - if (g.DragDropPayload.DataFrameCount == -1) - ClearDragDrop(); - g.DragDropWithinSource = false; -} - -// Use 'cond' to choose to submit payload on drag start or every frame -bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - ImGuiPayload& payload = g.DragDropPayload; - if (cond == 0) - cond = ImGuiCond_Always; - - IM_ASSERT(type != NULL); - IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); - IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); - IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); - IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() - - if (cond == ImGuiCond_Always || payload.DataFrameCount == -1) - { - // Copy payload - ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType)); - g.DragDropPayloadBufHeap.resize(0); - if (data_size > sizeof(g.DragDropPayloadBufLocal)) - { - // Store in heap - g.DragDropPayloadBufHeap.resize((int)data_size); - payload.Data = g.DragDropPayloadBufHeap.Data; - memcpy(payload.Data, data, data_size); - } - else if (data_size > 0) - { - // Store locally - memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); - payload.Data = g.DragDropPayloadBufLocal; - memcpy(payload.Data, data, data_size); - } - else - { - payload.Data = NULL; - } - payload.DataSize = (int)data_size; - } - payload.DataFrameCount = g.FrameCount; - - // Return whether the payload has been accepted - return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1); -} - -bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) -{ - ImGuiContext& g = *GImGui; - if (!g.DragDropActive) - return false; - - ImGuiWindow* window = g.CurrentWindow; - ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; - if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) - return false; - IM_ASSERT(id != 0); - if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) - return false; - if (window->SkipItems) - return false; - - IM_ASSERT(g.DragDropWithinTarget == false); - g.DragDropTargetRect = bb; - g.DragDropTargetId = id; - g.DragDropWithinTarget = true; - return true; -} - -// We don't use BeginDragDropTargetCustom() and duplicate its code because: -// 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them. -// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can. -// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case) -bool ImGui::BeginDragDropTarget() -{ - ImGuiContext& g = *GImGui; - if (!g.DragDropActive) - return false; - - ImGuiWindow* window = g.CurrentWindow; - if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect)) - return false; - ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; - if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems) - return false; - - const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect; - ImGuiID id = g.LastItemData.ID; - if (id == 0) - { - id = window->GetIDFromRectangle(display_rect); - KeepAliveID(id); - } - if (g.DragDropPayload.SourceId == id) - return false; - - IM_ASSERT(g.DragDropWithinTarget == false); - g.DragDropTargetRect = display_rect; - g.DragDropTargetId = id; - g.DragDropWithinTarget = true; - return true; -} - -bool ImGui::IsDragDropPayloadBeingAccepted() -{ - ImGuiContext& g = *GImGui; - return g.DragDropActive && g.DragDropAcceptIdPrev != 0; -} - -const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiPayload& payload = g.DragDropPayload; - IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ? - IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ? - if (type != NULL && !payload.IsDataType(type)) - return NULL; - - // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints. - // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function! - const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId); - ImRect r = g.DragDropTargetRect; - float r_surface = r.GetWidth() * r.GetHeight(); - if (r_surface <= g.DragDropAcceptIdCurrRectSurface) - { - g.DragDropAcceptFlags = flags; - g.DragDropAcceptIdCurr = g.DragDropTargetId; - g.DragDropAcceptIdCurrRectSurface = r_surface; - } - - // Render default drop visuals - // FIXME-DRAGDROP: Settle on a proper default visuals for drop target. - payload.Preview = was_accepted_previously; - flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame) - if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) - window->DrawList->AddRect(r.Min - ImVec2(3.5f,3.5f), r.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); - - g.DragDropAcceptFrameCount = g.FrameCount; - payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting os window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased() - if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery)) - return NULL; - - return &payload; -} - -const ImGuiPayload* ImGui::GetDragDropPayload() -{ - ImGuiContext& g = *GImGui; - return g.DragDropActive ? &g.DragDropPayload : NULL; -} - -// We don't really use/need this now, but added it for the sake of consistency and because we might need it later. -void ImGui::EndDragDropTarget() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.DragDropActive); - IM_ASSERT(g.DragDropWithinTarget); - g.DragDropWithinTarget = false; -} - -//----------------------------------------------------------------------------- -// [SECTION] LOGGING/CAPTURING -//----------------------------------------------------------------------------- -// All text output from the interface can be captured into tty/file/clipboard. -// By default, tree nodes are automatically opened during logging. -//----------------------------------------------------------------------------- - -// Pass text data straight to log (without being displayed) -static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args) -{ - if (g.LogFile) - { - g.LogBuffer.Buf.resize(0); - g.LogBuffer.appendfv(fmt, args); - ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile); - } - else - { - g.LogBuffer.appendfv(fmt, args); - } -} - -void ImGui::LogText(const char* fmt, ...) -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - va_list args; - va_start(args, fmt); - LogTextV(g, fmt, args); - va_end(args); -} - -void ImGui::LogTextV(const char* fmt, va_list args) -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - LogTextV(g, fmt, args); -} - -// Internal version that takes a position to decide on newline placement and pad items according to their depth. -// We split text into individual lines to add current tree level padding -// FIXME: This code is a little complicated perhaps, considering simplifying the whole system. -void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const char* prefix = g.LogNextPrefix; - const char* suffix = g.LogNextSuffix; - g.LogNextPrefix = g.LogNextSuffix = NULL; - - if (!text_end) - text_end = FindRenderedTextEnd(text, text_end); - - const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1); - if (ref_pos) - g.LogLinePosY = ref_pos->y; - if (log_new_line) - { - LogText(IM_NEWLINE); - g.LogLineFirstItem = true; - } - - if (prefix) - LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here. - - // Re-adjust padding if we have popped out of our starting depth - if (g.LogDepthRef > window->DC.TreeDepth) - g.LogDepthRef = window->DC.TreeDepth; - const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); - - const char* text_remaining = text; - for (;;) - { - // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry. - // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured. - const char* line_start = text_remaining; - const char* line_end = ImStreolRange(line_start, text_end); - const bool is_last_line = (line_end == text_end); - if (line_start != line_end || !is_last_line) - { - const int line_length = (int)(line_end - line_start); - const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1; - LogText("%*s%.*s", indentation, "", line_length, line_start); - g.LogLineFirstItem = false; - if (*line_end == '\n') - { - LogText(IM_NEWLINE); - g.LogLineFirstItem = true; - } - } - if (is_last_line) - break; - text_remaining = line_end + 1; - } - - if (suffix) - LogRenderedText(ref_pos, suffix, suffix + strlen(suffix)); -} - -// Start logging/capturing text output -void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(g.LogEnabled == false); - IM_ASSERT(g.LogFile == NULL); - IM_ASSERT(g.LogBuffer.empty()); - g.LogEnabled = true; - g.LogType = type; - g.LogNextPrefix = g.LogNextSuffix = NULL; - g.LogDepthRef = window->DC.TreeDepth; - g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); - g.LogLinePosY = FLT_MAX; - g.LogLineFirstItem = true; -} - -// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText) -void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix) -{ - ImGuiContext& g = *GImGui; - g.LogNextPrefix = prefix; - g.LogNextSuffix = suffix; -} - -void ImGui::LogToTTY(int auto_open_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - IM_UNUSED(auto_open_depth); -#ifndef IMGUI_DISABLE_TTY_FUNCTIONS - LogBegin(ImGuiLogType_TTY, auto_open_depth); - g.LogFile = stdout; -#endif -} - -// Start logging/capturing text output to given file -void ImGui::LogToFile(int auto_open_depth, const char* filename) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - - // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still - // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE. - // By opening the file in binary mode "ab" we have consistent output everywhere. - if (!filename) - filename = g.IO.LogFilename; - if (!filename || !filename[0]) - return; - ImFileHandle f = ImFileOpen(filename, "ab"); - if (!f) - { - IM_ASSERT(0); - return; - } - - LogBegin(ImGuiLogType_File, auto_open_depth); - g.LogFile = f; -} - -// Start logging/capturing text output to clipboard -void ImGui::LogToClipboard(int auto_open_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - LogBegin(ImGuiLogType_Clipboard, auto_open_depth); -} - -void ImGui::LogToBuffer(int auto_open_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - LogBegin(ImGuiLogType_Buffer, auto_open_depth); -} - -void ImGui::LogFinish() -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - LogText(IM_NEWLINE); - switch (g.LogType) - { - case ImGuiLogType_TTY: -#ifndef IMGUI_DISABLE_TTY_FUNCTIONS - fflush(g.LogFile); -#endif - break; - case ImGuiLogType_File: - ImFileClose(g.LogFile); - break; - case ImGuiLogType_Buffer: - break; - case ImGuiLogType_Clipboard: - if (!g.LogBuffer.empty()) - SetClipboardText(g.LogBuffer.begin()); - break; - case ImGuiLogType_None: - IM_ASSERT(0); - break; - } - - g.LogEnabled = false; - g.LogType = ImGuiLogType_None; - g.LogFile = NULL; - g.LogBuffer.clear(); -} - -// Helper to display logging buttons -// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!) -void ImGui::LogButtons() -{ - ImGuiContext& g = *GImGui; - - PushID("LogButtons"); -#ifndef IMGUI_DISABLE_TTY_FUNCTIONS - const bool log_to_tty = Button("Log To TTY"); SameLine(); -#else - const bool log_to_tty = false; -#endif - const bool log_to_file = Button("Log To File"); SameLine(); - const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); - PushAllowKeyboardFocus(false); - SetNextItemWidth(80.0f); - SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); - PopAllowKeyboardFocus(); - PopID(); - - // Start logging at the end of the function so that the buttons don't appear in the log - if (log_to_tty) - LogToTTY(); - if (log_to_file) - LogToFile(); - if (log_to_clipboard) - LogToClipboard(); -} - - -//----------------------------------------------------------------------------- -// [SECTION] SETTINGS -//----------------------------------------------------------------------------- -// - UpdateSettings() [Internal] -// - MarkIniSettingsDirty() [Internal] -// - CreateNewWindowSettings() [Internal] -// - FindWindowSettings() [Internal] -// - FindOrCreateWindowSettings() [Internal] -// - FindSettingsHandler() [Internal] -// - ClearIniSettings() [Internal] -// - LoadIniSettingsFromDisk() -// - LoadIniSettingsFromMemory() -// - SaveIniSettingsToDisk() -// - SaveIniSettingsToMemory() -// - WindowSettingsHandler_***() [Internal] -//----------------------------------------------------------------------------- - -// Called by NewFrame() -void ImGui::UpdateSettings() -{ - // Load settings on first frame (if not explicitly loaded manually before) - ImGuiContext& g = *GImGui; - if (!g.SettingsLoaded) - { - IM_ASSERT(g.SettingsWindows.empty()); - if (g.IO.IniFilename) - LoadIniSettingsFromDisk(g.IO.IniFilename); - g.SettingsLoaded = true; - } - - // Save settings (with a delay after the last modification, so we don't spam disk too much) - if (g.SettingsDirtyTimer > 0.0f) - { - g.SettingsDirtyTimer -= g.IO.DeltaTime; - if (g.SettingsDirtyTimer <= 0.0f) - { - if (g.IO.IniFilename != NULL) - SaveIniSettingsToDisk(g.IO.IniFilename); - else - g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves. - g.SettingsDirtyTimer = 0.0f; - } - } -} - -void ImGui::MarkIniSettingsDirty() -{ - ImGuiContext& g = *GImGui; - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; -} - -void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; -} - -ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) -{ - ImGuiContext& g = *GImGui; - -#if !IMGUI_DEBUG_INI_SETTINGS - // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() - // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier. - if (const char* p = strstr(name, "###")) - name = p; -#endif - const size_t name_len = strlen(name); - - // Allocate chunk - const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1; - ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size); - IM_PLACEMENT_NEW(settings) ImGuiWindowSettings(); - settings->ID = ImHashStr(name, name_len); - memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator - - return settings; -} - -ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - if (settings->ID == id) - return settings; - return NULL; -} - -ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name) -{ - if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name))) - return settings; - return CreateNewWindowSettings(name); -} - -void ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL); - g.SettingsHandlers.push_back(*handler); -} - -void ImGui::RemoveSettingsHandler(const char* type_name) -{ - ImGuiContext& g = *GImGui; - if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name)) - g.SettingsHandlers.erase(handler); -} - -ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) -{ - ImGuiContext& g = *GImGui; - const ImGuiID type_hash = ImHashStr(type_name); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].TypeHash == type_hash) - return &g.SettingsHandlers[handler_n]; - return NULL; -} - -void ImGui::ClearIniSettings() -{ - ImGuiContext& g = *GImGui; - g.SettingsIniData.clear(); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].ClearAllFn) - g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]); -} - -void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) -{ - size_t file_data_size = 0; - char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); - if (!file_data) - return; - if (file_data_size > 0) - LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); - IM_FREE(file_data); -} - -// Zero-tolerance, no error reporting, cheap .ini parsing -void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.Initialized); - //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()"); - //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); - - // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). - // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy.. - if (ini_size == 0) - ini_size = strlen(ini_data); - g.SettingsIniData.Buf.resize((int)ini_size + 1); - char* const buf = g.SettingsIniData.Buf.Data; - char* const buf_end = buf + ini_size; - memcpy(buf, ini_data, ini_size); - buf_end[0] = 0; - - // Call pre-read handlers - // Some types will clear their data (e.g. dock information) some types will allow merge/override (window) - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].ReadInitFn) - g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]); - - void* entry_data = NULL; - ImGuiSettingsHandler* entry_handler = NULL; - - char* line_end = NULL; - for (char* line = buf; line < buf_end; line = line_end + 1) - { - // Skip new lines markers, then find end of the line - while (*line == '\n' || *line == '\r') - line++; - line_end = line; - while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') - line_end++; - line_end[0] = 0; - if (line[0] == ';') - continue; - if (line[0] == '[' && line_end > line && line_end[-1] == ']') - { - // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. - line_end[-1] = 0; - const char* name_end = line_end - 1; - const char* type_start = line + 1; - char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']'); - const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; - if (!type_end || !name_start) - continue; - *type_end = 0; // Overwrite first ']' - name_start++; // Skip second '[' - entry_handler = FindSettingsHandler(type_start); - entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; - } - else if (entry_handler != NULL && entry_data != NULL) - { - // Let type handler parse the line - entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); - } - } - g.SettingsLoaded = true; - - // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary) - memcpy(buf, ini_data, ini_size); - - // Call post-read handlers - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].ApplyAllFn) - g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]); -} - -void ImGui::SaveIniSettingsToDisk(const char* ini_filename) -{ - ImGuiContext& g = *GImGui; - g.SettingsDirtyTimer = 0.0f; - if (!ini_filename) - return; - - size_t ini_data_size = 0; - const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); - ImFileHandle f = ImFileOpen(ini_filename, "wt"); - if (!f) - return; - ImFileWrite(ini_data, sizeof(char), ini_data_size, f); - ImFileClose(f); -} - -// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer -const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) -{ - ImGuiContext& g = *GImGui; - g.SettingsDirtyTimer = 0.0f; - g.SettingsIniData.Buf.resize(0); - g.SettingsIniData.Buf.push_back(0); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - { - ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; - handler->WriteAllFn(&g, handler, &g.SettingsIniData); - } - if (out_size) - *out_size = (size_t)g.SettingsIniData.size(); - return g.SettingsIniData.c_str(); -} - -static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) -{ - ImGuiContext& g = *ctx; - for (int i = 0; i != g.Windows.Size; i++) - g.Windows[i]->SettingsOffset = -1; - g.SettingsWindows.clear(); -} - -static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) -{ - ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name); - ImGuiID id = settings->ID; - *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry - settings->ID = id; - settings->WantApply = true; - return (void*)settings; -} - -static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) -{ - ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; - int x, y; - int i; - if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); } - else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); } - else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } -} - -// Apply to existing windows (if any) -static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) -{ - ImGuiContext& g = *ctx; - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - if (settings->WantApply) - { - if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID)) - ApplyWindowSettings(window, settings); - settings->WantApply = false; - } -} - -static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -{ - // Gather data from windows that were active during this session - // (if a window wasn't opened in this session we preserve its settings) - ImGuiContext& g = *ctx; - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* window = g.Windows[i]; - if (window->Flags & ImGuiWindowFlags_NoSavedSettings) - continue; - - ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID); - if (!settings) - { - settings = ImGui::CreateNewWindowSettings(window->Name); - window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); - } - IM_ASSERT(settings->ID == window->ID); - settings->Pos = ImVec2ih(window->Pos); - settings->Size = ImVec2ih(window->SizeFull); - - settings->Collapsed = window->Collapsed; - } - - // Write to text buffer - buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - { - const char* settings_name = settings->GetName(); - buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); - buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); - buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); - buf->appendf("Collapsed=%d\n", settings->Collapsed); - buf->append("\n"); - } -} - - -//----------------------------------------------------------------------------- -// [SECTION] VIEWPORTS, PLATFORM WINDOWS -//----------------------------------------------------------------------------- -// - GetMainViewport() -// - SetWindowViewport() [Internal] -// - UpdateViewportsNewFrame() [Internal] -// (this section is more complete in the 'docking' branch) -//----------------------------------------------------------------------------- - -ImGuiViewport* ImGui::GetMainViewport() -{ - ImGuiContext& g = *GImGui; - return g.Viewports[0]; -} - -void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) -{ - window->Viewport = viewport; -} - -// Update viewports and monitor infos -static void ImGui::UpdateViewportsNewFrame() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.Viewports.Size == 1); - - // Update main viewport with current platform position. - // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent. - ImGuiViewportP* main_viewport = g.Viewports[0]; - main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp; - main_viewport->Pos = ImVec2(0.0f, 0.0f); - main_viewport->Size = g.IO.DisplaySize; - - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - - // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again. - viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin; - viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax; - viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f); - viewport->UpdateWorkRect(); - } -} - -//----------------------------------------------------------------------------- -// [SECTION] DOCKING -//----------------------------------------------------------------------------- - -// (this section is filled in the 'docking' branch) - - -//----------------------------------------------------------------------------- -// [SECTION] PLATFORM DEPENDENT HELPERS -//----------------------------------------------------------------------------- - -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) - -#ifdef _MSC_VER -#pragma comment(lib, "user32") -#pragma comment(lib, "kernel32") -#endif - -// Win32 clipboard implementation -// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown() -static const char* GetClipboardTextFn_DefaultImpl(void*) -{ - ImGuiContext& g = *GImGui; - g.ClipboardHandlerData.clear(); - if (!::OpenClipboard(NULL)) - return NULL; - HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); - if (wbuf_handle == NULL) - { - ::CloseClipboard(); - return NULL; - } - if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle)) - { - int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL); - g.ClipboardHandlerData.resize(buf_len); - ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL); - } - ::GlobalUnlock(wbuf_handle); - ::CloseClipboard(); - return g.ClipboardHandlerData.Data; -} - -static void SetClipboardTextFn_DefaultImpl(void*, const char* text) -{ - if (!::OpenClipboard(NULL)) - return; - const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); - HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR)); - if (wbuf_handle == NULL) - { - ::CloseClipboard(); - return; - } - WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle); - ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length); - ::GlobalUnlock(wbuf_handle); - ::EmptyClipboard(); - if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) - ::GlobalFree(wbuf_handle); - ::CloseClipboard(); -} - -#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) - -#include // Use old API to avoid need for separate .mm file -static PasteboardRef main_clipboard = 0; - -// OSX clipboard implementation -// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line! -static void SetClipboardTextFn_DefaultImpl(void*, const char* text) -{ - if (!main_clipboard) - PasteboardCreate(kPasteboardClipboard, &main_clipboard); - PasteboardClear(main_clipboard); - CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text)); - if (cf_data) - { - PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0); - CFRelease(cf_data); - } -} - -static const char* GetClipboardTextFn_DefaultImpl(void*) -{ - if (!main_clipboard) - PasteboardCreate(kPasteboardClipboard, &main_clipboard); - PasteboardSynchronize(main_clipboard); - - ItemCount item_count = 0; - PasteboardGetItemCount(main_clipboard, &item_count); - for (ItemCount i = 0; i < item_count; i++) - { - PasteboardItemID item_id = 0; - PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id); - CFArrayRef flavor_type_array = 0; - PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array); - for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++) - { - CFDataRef cf_data; - if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr) - { - ImGuiContext& g = *GImGui; - g.ClipboardHandlerData.clear(); - int length = (int)CFDataGetLength(cf_data); - g.ClipboardHandlerData.resize(length + 1); - CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data); - g.ClipboardHandlerData[length] = 0; - CFRelease(cf_data); - return g.ClipboardHandlerData.Data; - } - } - } - return NULL; -} - -#else - -// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers. -static const char* GetClipboardTextFn_DefaultImpl(void*) -{ - ImGuiContext& g = *GImGui; - return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin(); -} - -static void SetClipboardTextFn_DefaultImpl(void*, const char* text) -{ - ImGuiContext& g = *GImGui; - g.ClipboardHandlerData.clear(); - const char* text_end = text + strlen(text); - g.ClipboardHandlerData.resize((int)(text_end - text) + 1); - memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text)); - g.ClipboardHandlerData[(int)(text_end - text)] = 0; -} - -#endif - -// Win32 API IME support (for Asian languages, etc.) -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) - -#include -#ifdef _MSC_VER -#pragma comment(lib, "imm32") -#endif - -static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data) -{ - // Notify OS Input Method Editor of text input position - HWND hwnd = (HWND)viewport->PlatformHandleRaw; -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (hwnd == 0) - hwnd = (HWND)ImGui::GetIO().ImeWindowHandle; -#endif - if (hwnd == 0) - return; - - ::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0); - - if (HIMC himc = ::ImmGetContext(hwnd)) - { - COMPOSITIONFORM composition_form = {}; - composition_form.ptCurrentPos.x = (LONG)data->InputPos.x; - composition_form.ptCurrentPos.y = (LONG)data->InputPos.y; - composition_form.dwStyle = CFS_FORCE_POSITION; - ::ImmSetCompositionWindow(himc, &composition_form); - CANDIDATEFORM candidate_form = {}; - candidate_form.dwStyle = CFS_CANDIDATEPOS; - candidate_form.ptCurrentPos.x = (LONG)data->InputPos.x; - candidate_form.ptCurrentPos.y = (LONG)data->InputPos.y; - ::ImmSetCandidateWindow(himc, &candidate_form); - ::ImmReleaseContext(hwnd, himc); - } -} - -#else - -static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeData*) {} - -#endif - -//----------------------------------------------------------------------------- -// [SECTION] METRICS/DEBUGGER WINDOW -//----------------------------------------------------------------------------- -// - RenderViewportThumbnail() [Internal] -// - RenderViewportsThumbnails() [Internal] -// - DebugTextEncoding() -// - MetricsHelpMarker() [Internal] -// - ShowFontAtlas() [Internal] -// - ShowMetricsWindow() -// - DebugNodeColumns() [Internal] -// - DebugNodeDrawList() [Internal] -// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal] -// - DebugNodeFont() [Internal] -// - DebugNodeFontGlyph() [Internal] -// - DebugNodeStorage() [Internal] -// - DebugNodeTabBar() [Internal] -// - DebugNodeViewport() [Internal] -// - DebugNodeWindow() [Internal] -// - DebugNodeWindowSettings() [Internal] -// - DebugNodeWindowsList() [Internal] -// - DebugNodeWindowsListByBeginStackParent() [Internal] -//----------------------------------------------------------------------------- - -#ifndef IMGUI_DISABLE_DEBUG_TOOLS - -void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - ImVec2 scale = bb.GetSize() / viewport->Size; - ImVec2 off = bb.Min - viewport->Pos * scale; - float alpha_mul = 1.0f; - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f)); - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* thumb_window = g.Windows[i]; - if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) - continue; - - ImRect thumb_r = thumb_window->Rect(); - ImRect title_r = thumb_window->TitleBarRect(); - thumb_r = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale)); - title_r = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height - thumb_r.ClipWithFull(bb); - title_r.ClipWithFull(bb); - const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); - window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul)); - window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul)); - window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul)); - window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name)); - } - draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul)); -} - -static void RenderViewportsThumbnails() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports. - float SCALE = 1.0f / 8.0f; - ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); - for (int n = 0; n < g.Viewports.Size; n++) - bb_full.Add(g.Viewports[n]->GetMainRect()); - ImVec2 p = window->DC.CursorPos; - ImVec2 off = p - bb_full.Min * SCALE; - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE); - ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb); - } - ImGui::Dummy(bb_full.GetSize() * SCALE); -} - -// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct. -void ImGui::DebugTextEncoding(const char* str) -{ - Text("Text: \"%s\"", str); - if (!BeginTable("list", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) - return; - TableSetupColumn("Offset"); - TableSetupColumn("UTF-8"); - TableSetupColumn("Glyph"); - TableSetupColumn("Codepoint"); - TableHeadersRow(); - for (const char* p = str; *p != 0; ) - { - unsigned int c; - const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL); - TableNextColumn(); - Text("%d", (int)(p - str)); - TableNextColumn(); - for (int byte_index = 0; byte_index < c_utf8_len; byte_index++) - { - if (byte_index > 0) - SameLine(); - Text("0x%02X", (int)(unsigned char)p[byte_index]); - } - TableNextColumn(); - if (GetFont()->FindGlyphNoFallback((ImWchar)c)) - TextUnformatted(p, p + c_utf8_len); - else - TextUnformatted((c == IM_UNICODE_CODEPOINT_INVALID) ? "[invalid]" : "[missing]"); - TableNextColumn(); - Text("U+%04X", (int)c); - p += c_utf8_len; - } - EndTable(); -} - -// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. -static void MetricsHelpMarker(const char* desc) -{ - ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(desc); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } -} - -// [DEBUG] List fonts in a font atlas and display its texture -void ImGui::ShowFontAtlas(ImFontAtlas* atlas) -{ - for (int i = 0; i < atlas->Fonts.Size; i++) - { - ImFont* font = atlas->Fonts[i]; - PushID(font); - DebugNodeFont(font); - PopID(); - } - if (TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) - { - ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); - ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); - Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col); - TreePop(); - } -} - -void ImGui::ShowMetricsWindow(bool* p_open) -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; - if (cfg->ShowDebugLog) - ShowDebugLogWindow(&cfg->ShowDebugLog); - if (cfg->ShowStackTool) - ShowStackToolWindow(&cfg->ShowStackTool); - - if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1) - { - End(); - return; - } - - // Basic info - Text("Dear ImGui %s", GetVersion()); - Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); - Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); - Text("%d visible windows, %d active allocations", io.MetricsRenderWindows, io.MetricsActiveAllocations); - //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; } - - Separator(); - - // Debugging enums - enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type - const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" }; - enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type - const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" }; - if (cfg->ShowWindowsRectsType < 0) - cfg->ShowWindowsRectsType = WRT_WorkRect; - if (cfg->ShowTablesRectsType < 0) - cfg->ShowTablesRectsType = TRT_WorkRect; - - struct Funcs - { - static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n) - { - ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance - if (rect_type == TRT_OuterRect) { return table->OuterRect; } - else if (rect_type == TRT_InnerRect) { return table->InnerRect; } - else if (rect_type == TRT_WorkRect) { return table->WorkRect; } - else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; } - else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; } - else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; } - else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); } - else if (rect_type == TRT_ColumnsWorkRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); } - else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; } - else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } // Note: y1/y2 not always accurate - else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } - else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } - else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); } - IM_ASSERT(0); - return ImRect(); - } - - static ImRect GetWindowRect(ImGuiWindow* window, int rect_type) - { - if (rect_type == WRT_OuterRect) { return window->Rect(); } - else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; } - else if (rect_type == WRT_InnerRect) { return window->InnerRect; } - else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; } - else if (rect_type == WRT_WorkRect) { return window->WorkRect; } - else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } - else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); } - else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; } - IM_ASSERT(0); - return ImRect(); - } - }; - - // Tools - if (TreeNode("Tools")) - { - bool show_encoding_viewer = TreeNode("UTF-8 Encoding viewer"); - SameLine(); - MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct."); - if (show_encoding_viewer) - { - static char buf[100] = ""; - SetNextItemWidth(-FLT_MIN); - InputText("##Text", buf, IM_ARRAYSIZE(buf)); - if (buf[0] != 0) - DebugTextEncoding(buf); - TreePop(); - } - - // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. - if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive) - DebugStartItemPicker(); - SameLine(); - MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); - - // Stack Tool is your best friend! - Checkbox("Show Debug Log", &cfg->ShowDebugLog); - SameLine(); - MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code."); - - // Stack Tool is your best friend! - Checkbox("Show Stack Tool", &cfg->ShowStackTool); - SameLine(); - MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code."); - - Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder); - Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); - SameLine(); - SetNextItemWidth(GetFontSize() * 12); - cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count); - if (cfg->ShowWindowsRects && g.NavWindow != NULL) - { - BulletText("'%s':", g.NavWindow->Name); - Indent(); - for (int rect_n = 0; rect_n < WRT_Count; rect_n++) - { - ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n); - Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]); - } - Unindent(); - } - - Checkbox("Show tables rectangles", &cfg->ShowTablesRects); - SameLine(); - SetNextItemWidth(GetFontSize() * 12); - cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count); - if (cfg->ShowTablesRects && g.NavWindow != NULL) - { - for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) - { - ImGuiTable* table = g.Tables.TryGetMapData(table_n); - if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow)) - continue; - - BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name); - if (IsItemHovered()) - GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); - Indent(); - char buf[128]; - for (int rect_n = 0; rect_n < TRT_Count; rect_n++) - { - if (rect_n >= TRT_ColumnsRect) - { - if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect) - continue; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImRect r = Funcs::GetTableRect(table, rect_n, column_n); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]); - Selectable(buf); - if (IsItemHovered()) - GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); - } - } - else - { - ImRect r = Funcs::GetTableRect(table, rect_n, -1); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]); - Selectable(buf); - if (IsItemHovered()) - GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); - } - } - Unindent(); - } - } - - TreePop(); - } - - // Windows - if (TreeNode("Windows", "Windows (%d)", g.Windows.Size)) - { - //SetNextItemOpen(true, ImGuiCond_Once); - DebugNodeWindowsList(&g.Windows, "By display order"); - DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)"); - if (TreeNode("By submission order (begin stack)")) - { - // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship! - ImVector& temp_buffer = g.WindowsTempSortBuffer; - temp_buffer.resize(0); - for (int i = 0; i < g.Windows.Size; i++) - if (g.Windows[i]->LastFrameActive + 1 >= g.FrameCount) - temp_buffer.push_back(g.Windows[i]); - struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } }; - ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder); - DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL); - TreePop(); - } - - TreePop(); - } - - // DrawLists - int drawlist_count = 0; - for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) - drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); - if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) - { - Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); - Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); - for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) - { - ImGuiViewportP* viewport = g.Viewports[viewport_i]; - for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); - } - TreePop(); - } - - // Viewports - if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size)) - { - Indent(GetTreeNodeToLabelSpacing()); - RenderViewportsThumbnails(); - Unindent(GetTreeNodeToLabelSpacing()); - for (int i = 0; i < g.Viewports.Size; i++) - DebugNodeViewport(g.Viewports[i]); - TreePop(); - } - - // Details for Popups - if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) - { - for (int i = 0; i < g.OpenPopupStack.Size; i++) - { - ImGuiWindow* window = g.OpenPopupStack[i].Window; - BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); - } - TreePop(); - } - - // Details for TabBars - if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount())) - { - for (int n = 0; n < g.TabBars.GetMapSize(); n++) - if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n)) - { - PushID(tab_bar); - DebugNodeTabBar(tab_bar, "TabBar"); - PopID(); - } - TreePop(); - } - - // Details for Tables - if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount())) - { - for (int n = 0; n < g.Tables.GetMapSize(); n++) - if (ImGuiTable* table = g.Tables.TryGetMapData(n)) - DebugNodeTable(table); - TreePop(); - } - - // Details for Fonts - ImFontAtlas* atlas = g.IO.Fonts; - if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size)) - { - ShowFontAtlas(atlas); - TreePop(); - } - - // Details for InputText - if (TreeNode("InputText")) - { - DebugNodeInputTextState(&g.InputTextState); - TreePop(); - } - - // Details for Docking -#ifdef IMGUI_HAS_DOCK - if (TreeNode("Docking")) - { - TreePop(); - } -#endif // #ifdef IMGUI_HAS_DOCK - - // Settings - if (TreeNode("Settings")) - { - if (SmallButton("Clear")) - ClearIniSettings(); - SameLine(); - if (SmallButton("Save to memory")) - SaveIniSettingsToMemory(); - SameLine(); - if (SmallButton("Save to disk")) - SaveIniSettingsToDisk(g.IO.IniFilename); - SameLine(); - if (g.IO.IniFilename) - Text("\"%s\"", g.IO.IniFilename); - else - TextUnformatted(""); - Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer); - if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) - { - for (int n = 0; n < g.SettingsHandlers.Size; n++) - BulletText("%s", g.SettingsHandlers[n].TypeName); - TreePop(); - } - if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) - { - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - DebugNodeWindowSettings(settings); - TreePop(); - } - - if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size())) - { - for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) - DebugNodeTableSettings(settings); - TreePop(); - } - -#ifdef IMGUI_HAS_DOCK -#endif // #ifdef IMGUI_HAS_DOCK - - if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size())) - { - InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly); - TreePop(); - } - TreePop(); - } - - // Misc Details - if (TreeNode("Internal state")) - { - Text("WINDOWING"); - Indent(); - Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); - Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL"); - Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); - Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); - Unindent(); - - Text("ITEMS"); - Indent(); - Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource)); - Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - - int active_id_using_key_input_count = 0; - for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) - active_id_using_key_input_count += g.ActiveIdUsingKeyInputMask[n] ? 1 : 0; - Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %d key(s)", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, active_id_using_key_input_count); - Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame - Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); - Unindent(); - - Text("NAV,FOCUS"); - Indent(); - Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); - Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); - Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource)); - Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); - Text("NavActivateId/DownId/PressedId/InputId: %08X/%08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId, g.NavActivateInputId); - Text("NavActivateFlags: %04X", g.NavActivateFlags); - Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); - Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); - Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); - Unindent(); - - TreePop(); - } - - // Overlay: Display windows Rectangles and Begin Order - if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder) - { - for (int n = 0; n < g.Windows.Size; n++) - { - ImGuiWindow* window = g.Windows[n]; - if (!window->WasActive) - continue; - ImDrawList* draw_list = GetForegroundDrawList(window); - if (cfg->ShowWindowsRects) - { - ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType); - draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)); - } - if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow)) - { - char buf[32]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); - float font_size = GetFontSize(); - draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); - draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf); - } - } - } - - // Overlay: Display Tables Rectangles - if (cfg->ShowTablesRects) - { - for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) - { - ImGuiTable* table = g.Tables.TryGetMapData(table_n); - if (table == NULL || table->LastFrameActive < g.FrameCount - 1) - continue; - ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow); - if (cfg->ShowTablesRectsType >= TRT_ColumnsRect) - { - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n); - ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255); - float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f; - draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness); - } - } - else - { - ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1); - draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)); - } - } - } - -#ifdef IMGUI_HAS_DOCK - // Overlay: Display Docking info - if (show_docking_nodes && g.IO.KeyCtrl) - { - } -#endif // #ifdef IMGUI_HAS_DOCK - - End(); -} - -// [DEBUG] Display contents of Columns -void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) -{ - if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) - return; - BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX); - for (int column_n = 0; column_n < columns->Columns.Size; column_n++) - BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm)); - TreePop(); -} - -// [DEBUG] Display contents of ImDrawList -void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label) -{ - ImGuiContext& g = *GImGui; - ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; - int cmd_count = draw_list->CmdBuffer.Size; - if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL) - cmd_count--; - bool node_open = TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, cmd_count); - if (draw_list == GetWindowDrawList()) - { - SameLine(); - TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) - if (node_open) - TreePop(); - return; - } - - ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list - if (window && IsItemHovered() && fg_draw_list) - fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); - if (!node_open) - return; - - if (window && !window->WasActive) - TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!"); - - for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++) - { - if (pcmd->UserCallback) - { - BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); - continue; - } - - char buf[300]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", - pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId, - pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); - bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); - if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list) - DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes); - if (!pcmd_node_open) - continue; - - // Calculate approximate coverage area (touched pixel count) - // This will be in pixels squared as long there's no post-scaling happening to the renderer output. - const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; - const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset; - float total_area = 0.0f; - for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; ) - { - ImVec2 triangle[3]; - for (int n = 0; n < 3; n++, idx_n++) - triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos; - total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]); - } - - // Display vertex information summary. Hover to get all triangles drawn in wire-frame - ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); - Selectable(buf); - if (IsItemHovered() && fg_draw_list) - DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false); - - // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. - ImGuiListClipper clipper; - clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. - while (clipper.Step()) - for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++) - { - char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf); - ImVec2 triangle[3]; - for (int n = 0; n < 3; n++, idx_i++) - { - const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i]; - triangle[n] = v.pos; - buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", - (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); - } - - Selectable(buf, false); - if (fg_draw_list && IsItemHovered()) - { - ImDrawListFlags backup_flags = fg_draw_list->Flags; - fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. - fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); - fg_draw_list->Flags = backup_flags; - } - } - TreePop(); - } - TreePop(); -} - -// [DEBUG] Display mesh/aabb of a ImDrawCmd -void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb) -{ - IM_ASSERT(show_mesh || show_aabb); - - // Draw wire-frame version of all triangles - ImRect clip_rect = draw_cmd->ClipRect; - ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); - ImDrawListFlags backup_flags = out_draw_list->Flags; - out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. - for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; ) - { - ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list - ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset; - - ImVec2 triangle[3]; - for (int n = 0; n < 3; n++, idx_n++) - vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos)); - if (show_mesh) - out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles - } - // Draw bounding boxes - if (show_aabb) - { - out_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU - out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles - } - out_draw_list->Flags = backup_flags; -} - -// [DEBUG] Display details for a single font, called by ShowStyleEditor(). -void ImGui::DebugNodeFont(ImFont* font) -{ - bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)", - font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); - SameLine(); - if (SmallButton("Set as default")) - GetIO().FontDefault = font; - if (!opened) - return; - - // Display preview text - PushFont(font); - Text("The quick brown fox jumps over the lazy dog"); - PopFont(); - - // Display details - SetNextItemWidth(GetFontSize() * 8); - DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); - SameLine(); MetricsHelpMarker( - "Note than the default embedded font is NOT meant to be scaled.\n\n" - "Font are currently rendered into bitmaps at a given size at the time of building the atlas. " - "You may oversample them to get some flexibility with scaling. " - "You can also render at multiple sizes and select which one to use at runtime.\n\n" - "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)"); - Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); - char c_str[5]; - Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar); - Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar); - const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface); - Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt); - for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) - if (font->ConfigData) - if (const ImFontConfig* cfg = &font->ConfigData[config_i]) - BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", - config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); - - // Display all glyphs of the fonts in separate pages of 256 characters - if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) - { - ImDrawList* draw_list = GetWindowDrawList(); - const ImU32 glyph_col = GetColorU32(ImGuiCol_Text); - const float cell_size = font->FontSize * 1; - const float cell_spacing = GetStyle().ItemSpacing.y; - for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) - { - // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) - // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT - // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) - if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095)) - { - base += 4096 - 256; - continue; - } - - int count = 0; - for (unsigned int n = 0; n < 256; n++) - if (font->FindGlyphNoFallback((ImWchar)(base + n))) - count++; - if (count <= 0) - continue; - if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) - continue; - - // Draw a 16x16 grid of glyphs - ImVec2 base_pos = GetCursorScreenPos(); - for (unsigned int n = 0; n < 256; n++) - { - // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions - // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. - ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); - ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); - const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); - draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); - if (!glyph) - continue; - font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); - if (IsMouseHoveringRect(cell_p1, cell_p2)) - { - BeginTooltip(); - DebugNodeFontGlyph(font, glyph); - EndTooltip(); - } - } - Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); - TreePop(); - } - TreePop(); - } - TreePop(); -} - -void ImGui::DebugNodeFontGlyph(ImFont*, const ImFontGlyph* glyph) -{ - Text("Codepoint: U+%04X", glyph->Codepoint); - Separator(); - Text("Visible: %d", glyph->Visible); - Text("AdvanceX: %.1f", glyph->AdvanceX); - Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); - Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); -} - -// [DEBUG] Display contents of ImGuiStorage -void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label) -{ - if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes())) - return; - for (int n = 0; n < storage->Data.Size; n++) - { - const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n]; - BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer. - } - TreePop(); -} - -// [DEBUG] Display contents of ImGuiTabBar -void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) -{ - // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. - char buf[256]; - char* p = buf; - const char* buf_end = buf + IM_ARRAYSIZE(buf); - const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2); - p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); - p += ImFormatString(p, buf_end - p, " { "); - for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) - { - ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - p += ImFormatString(p, buf_end - p, "%s'%s'", - tab_n > 0 ? ", " : "", (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???"); - } - p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); - if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } - bool open = TreeNode(label, "%s", buf); - if (!is_active) { PopStyleColor(); } - if (is_active && IsItemHovered()) - { - ImDrawList* draw_list = GetForegroundDrawList(); - draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255)); - draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255)); - draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255)); - } - if (open) - { - for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) - { - const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - PushID(tab); - if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2); - if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine(); - Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", - tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth); - PopID(); - } - TreePop(); - } -} - -void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) -{ - SetNextItemOpen(true, ImGuiCond_Once); - if (TreeNode("viewport0", "Viewport #%d", 0)) - { - ImGuiWindowFlags flags = viewport->Flags; - BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f", - viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, - viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y); - BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags, - (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", - (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "", - (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : ""); - for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); - TreePop(); - } -} - -void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) -{ - if (window == NULL) - { - BulletText("%s: NULL", label); - return; - } - - ImGuiContext& g = *GImGui; - const bool is_active = window->WasActive; - ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None; - if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } - const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*"); - if (!is_active) { PopStyleColor(); } - if (IsItemHovered() && is_active) - GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); - if (!open) - return; - - if (window->MemoryCompacted) - TextDisabled("Note: some memory buffers have been compacted/freed."); - - ImGuiWindowFlags flags = window->Flags; - DebugNodeDrawList(window, window->DrawList, "DrawList"); - BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y); - BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, - (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", - (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", - (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); - BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); - BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); - BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); - for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) - { - ImRect r = window->NavRectRel[layer]; - if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y) - { - BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]); - continue; - } - BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y); - if (IsItemHovered()) - GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255)); - } - BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } - if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } - if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } - if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) - { - for (int n = 0; n < window->ColumnsStorage.Size; n++) - DebugNodeColumns(&window->ColumnsStorage[n]); - TreePop(); - } - DebugNodeStorage(&window->StateStorage, "Storage"); - TreePop(); -} - -void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings) -{ - Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d", - settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed); -} - -void ImGui::DebugNodeWindowsList(ImVector* windows, const char* label) -{ - if (!TreeNode(label, "%s (%d)", label, windows->Size)) - return; - for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back - { - PushID((*windows)[i]); - DebugNodeWindow((*windows)[i], "Window"); - PopID(); - } - TreePop(); -} - -// FIXME-OPT: This is technically suboptimal, but it is simpler this way. -void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack) -{ - for (int i = 0; i < windows_size; i++) - { - ImGuiWindow* window = windows[i]; - if (window->ParentWindowInBeginStack != parent_in_begin_stack) - continue; - char buf[20]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext); - //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name); - DebugNodeWindow(window, buf); - Indent(); - DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window); - Unindent(); - } -} - -//----------------------------------------------------------------------------- -// [SECTION] DEBUG LOG -//----------------------------------------------------------------------------- - -void ImGui::DebugLog(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - DebugLogV(fmt, args); - va_end(args); -} - -void ImGui::DebugLogV(const char* fmt, va_list args) -{ - ImGuiContext& g = *GImGui; - const int old_size = g.DebugLogBuf.size(); - g.DebugLogBuf.appendf("[%05d] ", g.FrameCount); - g.DebugLogBuf.appendfv(fmt, args); - if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY) - IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size); -} - -void ImGui::ShowDebugLogWindow(bool* p_open) -{ - ImGuiContext& g = *GImGui; - if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)) - SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver); - if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1) - { - End(); - return; - } - - AlignTextToFramePadding(); - Text("Log events:"); - SameLine(); CheckboxFlags("All", &g.DebugLogFlags, ImGuiDebugLogFlags_EventMask_); - SameLine(); CheckboxFlags("ActiveId", &g.DebugLogFlags, ImGuiDebugLogFlags_EventActiveId); - SameLine(); CheckboxFlags("Focus", &g.DebugLogFlags, ImGuiDebugLogFlags_EventFocus); - SameLine(); CheckboxFlags("Popup", &g.DebugLogFlags, ImGuiDebugLogFlags_EventPopup); - SameLine(); CheckboxFlags("Nav", &g.DebugLogFlags, ImGuiDebugLogFlags_EventNav); - - if (SmallButton("Clear")) - g.DebugLogBuf.clear(); - SameLine(); - if (SmallButton("Copy")) - SetClipboardText(g.DebugLogBuf.c_str()); - BeginChild("##log", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); - TextUnformatted(g.DebugLogBuf.begin(), g.DebugLogBuf.end()); // FIXME-OPT: Could use a line index, but TextUnformatted() has a semi-decent fast path for large text. - if (GetScrollY() >= GetScrollMaxY()) - SetScrollHereY(1.0f); - EndChild(); - - End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) -//----------------------------------------------------------------------------- - -// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. -void ImGui::UpdateDebugToolItemPicker() -{ - ImGuiContext& g = *GImGui; - g.DebugItemPickerBreakId = 0; - if (!g.DebugItemPickerActive) - return; - - const ImGuiID hovered_id = g.HoveredIdPreviousFrame; - SetMouseCursor(ImGuiMouseCursor_Hand); - if (IsKeyPressed(ImGuiKey_Escape)) - g.DebugItemPickerActive = false; - if (IsMouseClicked(0) && hovered_id) - { - g.DebugItemPickerBreakId = hovered_id; - g.DebugItemPickerActive = false; - } - SetNextWindowBgAlpha(0.60f); - BeginTooltip(); - Text("HoveredId: 0x%08X", hovered_id); - Text("Press ESC to abort picking."); - TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); - EndTooltip(); -} - -// [DEBUG] Stack Tool: update queries. Called by NewFrame() -void ImGui::UpdateDebugToolStackQueries() -{ - ImGuiContext& g = *GImGui; - ImGuiStackTool* tool = &g.DebugStackTool; - - // Clear hook when stack tool is not visible - g.DebugHookIdInfo = 0; - if (g.FrameCount != tool->LastActiveFrame + 1) - return; - - // Update queries. The steps are: -1: query Stack, >= 0: query each stack item - // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time - const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId; - if (tool->QueryId != query_id) - { - tool->QueryId = query_id; - tool->StackLevel = -1; - tool->Results.resize(0); - } - if (query_id == 0) - return; - - // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result) - int stack_level = tool->StackLevel; - if (stack_level >= 0 && stack_level < tool->Results.Size) - if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2) - tool->StackLevel++; - - // Update hook - stack_level = tool->StackLevel; - if (stack_level == -1) - g.DebugHookIdInfo = query_id; - if (stack_level >= 0 && stack_level < tool->Results.Size) - { - g.DebugHookIdInfo = tool->Results[stack_level].ID; - tool->Results[stack_level].QueryFrameCount++; - } -} - -// [DEBUG] Stack tool: hooks called by GetID() family functions -void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiStackTool* tool = &g.DebugStackTool; - - // Step 0: stack query - // This assume that the ID was computed with the current ID stack, which tends to be the case for our widget. - if (tool->StackLevel == -1) - { - tool->StackLevel++; - tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo()); - for (int n = 0; n < window->IDStack.Size + 1; n++) - tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id; - return; - } - - // Step 1+: query for individual level - IM_ASSERT(tool->StackLevel >= 0); - if (tool->StackLevel != window->IDStack.Size) - return; - ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel]; - IM_ASSERT(info->ID == id && info->QueryFrameCount > 0); - - switch (data_type) - { - case ImGuiDataType_S32: - ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id); - break; - case ImGuiDataType_String: - ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id), (const char*)data_id); - break; - case ImGuiDataType_Pointer: - ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id); - break; - case ImGuiDataType_ID: - if (info->Desc[0] != 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one. - return; - ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id); - break; - default: - IM_ASSERT(0); - } - info->QuerySuccess = true; - info->DataType = data_type; -} - -static int StackToolFormatLevelInfo(ImGuiStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size) -{ - ImGuiStackLevelInfo* info = &tool->Results[n]; - ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL; - if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked) - return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", window->Name); - if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id) - return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", info->Desc); - if (tool->StackLevel < tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers. - return (*buf = 0); -#ifdef IMGUI_ENABLE_TEST_ENGINE - if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo() - return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", label); -#endif - return ImFormatString(buf, buf_size, "???"); -} - -// Stack Tool: Display UI -void ImGui::ShowStackToolWindow(bool* p_open) -{ - ImGuiContext& g = *GImGui; - if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)) - SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver); - if (!Begin("Dear ImGui Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1) - { - End(); - return; - } - - // Display hovered/active status - ImGuiStackTool* tool = &g.DebugStackTool; - const ImGuiID hovered_id = g.HoveredIdPreviousFrame; - const ImGuiID active_id = g.ActiveId; -#ifdef IMGUI_ENABLE_TEST_ENGINE - Text("HoveredId: 0x%08X (\"%s\"), ActiveId: 0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : ""); -#else - Text("HoveredId: 0x%08X, ActiveId: 0x%08X", hovered_id, active_id); -#endif - SameLine(); - MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details."); - - // CTRL+C to copy path - const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime; - Checkbox("Ctrl+C: copy path to clipboard", &tool->CopyToClipboardOnCtrlC); - SameLine(); - TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*"); - if (tool->CopyToClipboardOnCtrlC && IsKeyDown(ImGuiKey_ModCtrl) && IsKeyPressed(ImGuiKey_C)) - { - tool->CopyToClipboardLastTime = (float)g.Time; - char* p = g.TempBuffer.Data; - char* p_end = p + g.TempBuffer.Size; - for (int stack_n = 0; stack_n < tool->Results.Size && p + 3 < p_end; stack_n++) - { - *p++ = '/'; - char level_desc[256]; - StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc)); - for (int n = 0; level_desc[n] && p + 2 < p_end; n++) - { - if (level_desc[n] == '/') - *p++ = '\\'; - *p++ = level_desc[n]; - } - } - *p = '\0'; - SetClipboardText(g.TempBuffer.Data); - } - - // Display decorated stack - tool->LastActiveFrame = g.FrameCount; - if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders)) - { - const float id_width = CalcTextSize("0xDDDDDDDD").x; - TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width); - TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch); - TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width); - TableHeadersRow(); - for (int n = 0; n < tool->Results.Size; n++) - { - ImGuiStackLevelInfo* info = &tool->Results[n]; - TableNextColumn(); - Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0); - TableNextColumn(); - StackToolFormatLevelInfo(tool, n, true, g.TempBuffer.Data, g.TempBuffer.Size); - TextUnformatted(g.TempBuffer.Data); - TableNextColumn(); - Text("0x%08X", info->ID); - if (n == tool->Results.Size - 1) - TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header)); - } - EndTable(); - } - End(); -} - -#else - -void ImGui::ShowMetricsWindow(bool*) {} -void ImGui::ShowFontAtlas(ImFontAtlas*) {} -void ImGui::DebugNodeColumns(ImGuiOldColumns*) {} -void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {} -void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {} -void ImGui::DebugNodeFont(ImFont*) {} -void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {} -void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {} -void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {} -void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {} -void ImGui::DebugNodeWindowsList(ImVector*, const char*) {} -void ImGui::DebugNodeViewport(ImGuiViewportP*) {} - -void ImGui::DebugLog(const char*, ...) {} -void ImGui::DebugLogV(const char*, va_list) {} -void ImGui::ShowDebugLogWindow(bool*) {} -void ImGui::ShowStackToolWindow(bool*) {} -void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {} -void ImGui::UpdateDebugToolItemPicker() {} -void ImGui::UpdateDebugToolStackQueries() {} - -#endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS - -//----------------------------------------------------------------------------- - -// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed. -// Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github. -#ifdef IMGUI_INCLUDE_IMGUI_USER_INL -#include "imgui_user.inl" -#endif - -//----------------------------------------------------------------------------- - -#endif // #ifndef IMGUI_DISABLE diff --git a/libs/zgui/libs/imgui/imgui.h b/libs/zgui/libs/imgui/imgui.h deleted file mode 100644 index bc5fbd61a..000000000 --- a/libs/zgui/libs/imgui/imgui.h +++ /dev/null @@ -1,3065 +0,0 @@ -// dear imgui, v1.88 -// (headers) - -// Help: -// - Read FAQ at http://dearimgui.org/faq -// - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. -// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. -// Read imgui.cpp for details, links and comments. - -// Resources: -// - FAQ http://dearimgui.org/faq -// - Homepage & latest https://github.com/ocornut/imgui -// - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/5243 (please post your screenshots/video there!) -// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) -// - Glossary https://github.com/ocornut/imgui/wiki/Glossary -// - Issues & support https://github.com/ocornut/imgui/issues - -// Getting Started? -// - For first-time users having issues compiling/linking/running or issues loading fonts: -// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. - -/* - -Index of this file: -// [SECTION] Header mess -// [SECTION] Forward declarations and basic types -// [SECTION] Dear ImGui end-user API functions -// [SECTION] Flags & Enumerations -// [SECTION] Helpers: Memory allocations macros, ImVector<> -// [SECTION] ImGuiStyle -// [SECTION] ImGuiIO -// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs) -// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) -// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) -// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) -// [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) -// [SECTION] Platform Dependent Interfaces (ImGuiPlatformImeData) -// [SECTION] Obsolete functions and types - -*/ - -#pragma once - -// Configuration file with compile-time options (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system') -#ifdef IMGUI_USER_CONFIG -#include IMGUI_USER_CONFIG -#endif -#if !defined(IMGUI_DISABLE_INCLUDE_IMCONFIG_H) || defined(IMGUI_INCLUDE_IMCONFIG_H) -#include "imconfig.h" -#endif - -#ifndef IMGUI_DISABLE - -//----------------------------------------------------------------------------- -// [SECTION] Header mess -//----------------------------------------------------------------------------- - -// Includes -#include // FLT_MIN, FLT_MAX -#include // va_list, va_start, va_end -#include // ptrdiff_t, NULL -#include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp - -// Version -// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.88" -#define IMGUI_VERSION_NUM 18800 -#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) -#define IMGUI_HAS_TABLE - -// Define attributes of all API symbols declarations (e.g. for DLL under Windows) -// IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default backends files (imgui_impl_xxx.h) -// Using dear imgui via a shared library is not recommended, because we don't guarantee backward nor forward ABI compatibility (also function call overhead, as dear imgui is a call-heavy API) -#ifndef IMGUI_API -#define IMGUI_API -#endif -#ifndef IMGUI_IMPL_API -#define IMGUI_IMPL_API IMGUI_API -#endif - -// Helper Macros -#ifndef IM_ASSERT -#include -#define IM_ASSERT(_EXPR) assert(_EXPR) // You can override the default assert handler by editing imconfig.h -#endif -#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! -#define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. -#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in C++11 - -// Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. -#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) -#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) -#define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) -#elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__)) -#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) -#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) -#else -#define IM_FMTARGS(FMT) -#define IM_FMTLIST(FMT) -#endif - -// Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions) -#if defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(IMGUI_DEBUG_PARANOID) -#define IM_MSVC_RUNTIME_CHECKS_OFF __pragma(runtime_checks("",off)) __pragma(check_stack(off)) __pragma(strict_gs_check(push,off)) -#define IM_MSVC_RUNTIME_CHECKS_RESTORE __pragma(runtime_checks("",restore)) __pragma(check_stack()) __pragma(strict_gs_check(pop)) -#else -#define IM_MSVC_RUNTIME_CHECKS_OFF -#define IM_MSVC_RUNTIME_CHECKS_RESTORE -#endif - -// Warnings -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wold-style-cast" -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" -#endif -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind -#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead -#endif - -//----------------------------------------------------------------------------- -// [SECTION] Forward declarations and basic types -//----------------------------------------------------------------------------- - -// Forward declarations -struct ImDrawChannel; // Temporary storage to output draw commands out of order, used by ImDrawListSplitter and ImDrawList::ChannelsSplit() -struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call, unless it is a callback) -struct ImDrawData; // All draw command lists required to render the frame + pos/size coordinates to use for the projection matrix. -struct ImDrawList; // A single draw command list (generally one per window, conceptually you may see this as a dynamic "mesh" builder) -struct ImDrawListSharedData; // Data shared among multiple draw lists (typically owned by parent ImGui context, but you may create one yourself) -struct ImDrawListSplitter; // Helper to split a draw list into different layers which can be drawn into out of order, then flattened back. -struct ImDrawVert; // A single vertex (pos + uv + col = 20 bytes by default. Override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) -struct ImFont; // Runtime data for a single font within a parent ImFontAtlas -struct ImFontAtlas; // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF/OTF font loader -struct ImFontBuilderIO; // Opaque interface to a font builder (stb_truetype or FreeType). -struct ImFontConfig; // Configuration data when adding a font or merging fonts -struct ImFontGlyph; // A single font glyph (code point + coordinates within in ImFontAtlas + offset) -struct ImFontGlyphRangesBuilder; // Helper to build glyph ranges from text/string data -struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using) -struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h) -struct ImGuiIO; // Main configuration and I/O between your application and ImGui -struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) -struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions. -struct ImGuiListClipper; // Helper to manually clip large list of items -struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame -struct ImGuiPayload; // User data payload for drag and drop operations -struct ImGuiPlatformImeData; // Platform IME data for io.SetPlatformImeDataFn() function. -struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) -struct ImGuiStorage; // Helper for key->value storage -struct ImGuiStyle; // Runtime data for styling/colors -struct ImGuiTableSortSpecs; // Sorting specifications for a table (often handling sort specs for a single column, occasionally more) -struct ImGuiTableColumnSortSpecs; // Sorting specification for one column of a table -struct ImGuiTextBuffer; // Helper to hold and append into a text buffer (~string builder) -struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]") -struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor - -// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file) -// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! -// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. -typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling -typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions -typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type -typedef int ImGuiDir; // -> enum ImGuiDir_ // Enum: A cardinal direction -typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier -typedef int ImGuiNavInput; // -> enum ImGuiNavInput_ // Enum: An input identifier for navigation -typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle) -typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier -typedef int ImGuiSortDirection; // -> enum ImGuiSortDirection_ // Enum: A sorting direction (ascending or descending) -typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling -typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A color target for TableSetBgColor() -typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions -typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance -typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build -typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags -typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for InvisibleButton() -typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4() etc. -typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags -typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo() -typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for BeginDragDropSource(), AcceptDragDropPayload() -typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() -typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. -typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline() -typedef int ImGuiModFlags; // -> enum ImGuiModFlags_ // Flags: for io.KeyMods (Ctrl/Shift/Alt/Super) -typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() -typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() -typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. -typedef int ImGuiTabBarFlags; // -> enum ImGuiTabBarFlags_ // Flags: for BeginTabBar() -typedef int ImGuiTabItemFlags; // -> enum ImGuiTabItemFlags_ // Flags: for BeginTabItem() -typedef int ImGuiTableFlags; // -> enum ImGuiTableFlags_ // Flags: For BeginTable() -typedef int ImGuiTableColumnFlags; // -> enum ImGuiTableColumnFlags_// Flags: For TableSetupColumn() -typedef int ImGuiTableRowFlags; // -> enum ImGuiTableRowFlags_ // Flags: For TableNextRow() -typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader() -typedef int ImGuiViewportFlags; // -> enum ImGuiViewportFlags_ // Flags: for ImGuiViewport -typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild() - -// ImTexture: user data for renderer backend to identify a texture [Compile-time configurable type] -// - To use something else than an opaque void* pointer: override with e.g. '#define ImTextureID MyTextureType*' in your imconfig.h file. -// - This can be whatever to you want it to be! read the FAQ about ImTextureID for details. -#ifndef ImTextureID -typedef void* ImTextureID; // Default: store a pointer or an integer fitting in a pointer (most renderer backends are ok with that) -#endif - -// ImDrawIdx: vertex index. [Compile-time configurable type] -// - To use 16-bit indices + allow large meshes: backend need to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset (recommended). -// - To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in your imconfig.h file. -#ifndef ImDrawIdx -typedef unsigned short ImDrawIdx; // Default: 16-bit (for maximum compatibility with renderer backends) -#endif - -// Scalar data types -typedef unsigned int ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string) -typedef signed char ImS8; // 8-bit signed integer -typedef unsigned char ImU8; // 8-bit unsigned integer -typedef signed short ImS16; // 16-bit signed integer -typedef unsigned short ImU16; // 16-bit unsigned integer -typedef signed int ImS32; // 32-bit signed integer == int -typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors) -typedef signed long long ImS64; // 64-bit signed integer -typedef unsigned long long ImU64; // 64-bit unsigned integer - -// Character types -// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) -typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. -typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. -#ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16] -typedef ImWchar32 ImWchar; -#else -typedef ImWchar16 ImWchar; -#endif - -// Callback and functions types -typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() -typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() -typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() -typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() - -// ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type] -// This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type. -IM_MSVC_RUNTIME_CHECKS_OFF -struct ImVec2 -{ - float x, y; - constexpr ImVec2() : x(0.0f), y(0.0f) { } - constexpr ImVec2(float _x, float _y) : x(_x), y(_y) { } - float operator[] (size_t idx) const { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. - float& operator[] (size_t idx) { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. -#ifdef IM_VEC2_CLASS_EXTRA - IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2. -#endif -}; - -// ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type] -struct ImVec4 -{ - float x, y, z, w; - constexpr ImVec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) { } - constexpr ImVec4(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) { } -#ifdef IM_VEC4_CLASS_EXTRA - IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4. -#endif -}; -IM_MSVC_RUNTIME_CHECKS_RESTORE - -//----------------------------------------------------------------------------- -// [SECTION] Dear ImGui end-user API functions -// (Note that ImGui:: being a namespace, you can add extra ImGui:: functions in your own separate file. Please don't modify imgui source files!) -//----------------------------------------------------------------------------- - -namespace ImGui -{ - // Context creation and access - // - Each context create its own ImFontAtlas by default. You may instance one yourself and pass it to CreateContext() to share a font atlas between contexts. - // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() - // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for details. - IMGUI_API ImGuiContext* CreateContext(ImFontAtlas* shared_font_atlas = NULL); - IMGUI_API void DestroyContext(ImGuiContext* ctx = NULL); // NULL = destroy current context - IMGUI_API ImGuiContext* GetCurrentContext(); - IMGUI_API void SetCurrentContext(ImGuiContext* ctx); - - // Main - IMGUI_API ImGuiIO& GetIO(); // access the IO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags) - IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleCol(), PushStyleVar() to modify style mid-frame! - IMGUI_API void NewFrame(); // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame(). - IMGUI_API void EndFrame(); // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all! - IMGUI_API void Render(); // ends the Dear ImGui frame, finalize the draw data. You can then get call GetDrawData(). - IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. - - // Demo, Debug, Information - IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! - IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc. - IMGUI_API void ShowDebugLogWindow(bool* p_open = NULL); // create Debug Log window. display a simplified log of important dear imgui events. - IMGUI_API void ShowStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID. - IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. - IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) - IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. - IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. - IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). - IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.80 WIP" (essentially the value for IMGUI_VERSION from the compiled version of imgui.cpp) - - // Styles - IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) - IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // best used with borders and a custom, thicker font - IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style - - // Windows - // - Begin() = push window to the stack and start appending to it. End() = pop window from the stack. - // - Passing 'bool* p_open != NULL' shows a window-closing widget in the upper-right corner of the window, - // which clicking will set the boolean to false when clicked. - // - You may append multiple times to the same window during the same frame by calling Begin()/End() pairs multiple times. - // Some information such as 'flags' or 'p_open' will only be considered by the first call to Begin(). - // - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting - // anything to the window. Always call a matching End() for each Begin() call, regardless of its return value! - // [Important: due to legacy reason, this is inconsistent with most other functions such as BeginMenu/EndMenu, - // BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function - // returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.] - // - Note that the bottom of window stack always contains a window called "Debug". - IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); - IMGUI_API void End(); - - // Child Windows - // - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child. - // - For each independent axis of 'size': ==0.0f: use remaining host window size / >0.0f: fixed size / <0.0f: use remaining window size minus abs(size) / Each axis can use a different mode, e.g. ImVec2(0,400). - // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting anything to the window. - // Always call a matching EndChild() for each BeginChild() call, regardless of its return value. - // [Important: due to legacy reason, this is inconsistent with most other functions such as BeginMenu/EndMenu, - // BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function - // returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.] - IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0); - IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0); - IMGUI_API void EndChild(); - - // Windows Utilities - // - 'current window' = the window we are appending into while inside a Begin()/End() block. 'next window' = next window we will Begin() into. - IMGUI_API bool IsWindowAppearing(); - IMGUI_API bool IsWindowCollapsed(); - IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. - IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! - IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives - IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) - IMGUI_API ImVec2 GetWindowSize(); // get current window size - IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) - IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) - - // Window manipulation - // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). - IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc. - IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() - IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Sizes will be rounded down. Use callback to apply non-trivial programmatic constraints. - IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. set an axis to 0.0f to leave it automatic. call before Begin() - IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin() - IMGUI_API void SetNextWindowFocus(); // set next window to be focused / top-most. call before Begin() - IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground. - IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. - IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. - IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). - IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus(). - IMGUI_API void SetWindowFontScale(float scale); // [OBSOLETE] set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes(). - IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0); // set named window position. - IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis. - IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state - IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / top-most. use NULL to remove focus. - - // Content region - // - Retrieve available space from a given point. GetContentRegionAvail() is frequently useful. - // - Those functions are bound to be redesigned (they are confusing, incomplete and the Min/Max return values are in local window coordinates which increases confusion) - IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() - IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates - IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min for the full window (roughly (0,0)-Scroll), in window coordinates - IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates - - // Windows Scrolling - IMGUI_API float GetScrollX(); // get scrolling amount [0 .. GetScrollMaxX()] - IMGUI_API float GetScrollY(); // get scrolling amount [0 .. GetScrollMaxY()] - IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0 .. GetScrollMaxX()] - IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0 .. GetScrollMaxY()] - IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.x - WindowSize.x - DecorationsSize.x - IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.y - WindowSize.y - DecorationsSize.y - IMGUI_API void SetScrollHereX(float center_x_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_x_ratio=0.0: left, 0.5: center, 1.0: right. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. - IMGUI_API void SetScrollHereY(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. - IMGUI_API void SetScrollFromPosX(float local_x, float center_x_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. - IMGUI_API void SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. - - // Parameters stacks (shared) - IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font - IMGUI_API void PopFont(); - IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); // modify a style color. always use this if you modify the style after NewFrame(). - IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); - IMGUI_API void PopStyleColor(int count = 1); - IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame(). - IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame(). - IMGUI_API void PopStyleVar(int count = 1); - IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets - IMGUI_API void PopAllowKeyboardFocus(); - IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. - IMGUI_API void PopButtonRepeat(); - - // Parameters stacks (current window) - IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). - IMGUI_API void PopItemWidth(); - IMGUI_API void SetNextItemWidth(float item_width); // set width of the _next_ common large "item+label" widget. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side) - IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position. NOT necessarily the width of last item unlike most 'Item' functions. - IMGUI_API void PushTextWrapPos(float wrap_local_pos_x = 0.0f); // push word-wrapping position for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space - IMGUI_API void PopTextWrapPos(); - - // Style read access - // - Use the style editor (ShowStyleEditor() function) to interactively see what the colors are) - IMGUI_API ImFont* GetFont(); // get current font - IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied - IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API - IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList - IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList - IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList - IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in. - - // Cursor / Layout - // - By "cursor" we mean the current output position. - // - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down. - // - You can call SameLine() between widgets to undo the last carriage return and output at the right of the preceding widget. - // - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API: - // Window-local coordinates: SameLine(), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), GetWindowContentRegion*(), PushTextWrapPos() - // Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. - IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator. - IMGUI_API void SameLine(float offset_from_start_x=0.0f, float spacing=-1.0f); // call between widgets or groups to layout them horizontally. X position given in window coordinates. - IMGUI_API void NewLine(); // undo a SameLine() or force a new line when in an horizontal-layout context. - IMGUI_API void Spacing(); // add vertical spacing. - IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size. unlike InvisibleButton(), Dummy() won't take the mouse click or be navigable into. - IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by indent_w, or style.IndentSpacing if indent_w <= 0 - IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by indent_w, or style.IndentSpacing if indent_w <= 0 - IMGUI_API void BeginGroup(); // lock horizontal starting position - IMGUI_API void EndGroup(); // unlock horizontal starting position + capture the whole group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) - IMGUI_API ImVec2 GetCursorPos(); // cursor position in window coordinates (relative to window position) - IMGUI_API float GetCursorPosX(); // (some functions are using window-relative coordinates, such as: GetCursorPos, GetCursorStartPos, GetContentRegionMax, GetWindowContentRegion* etc. - IMGUI_API float GetCursorPosY(); // other functions such as GetCursorScreenPos or everything in ImDrawList:: - IMGUI_API void SetCursorPos(const ImVec2& local_pos); // are using the main, absolute coordinate system. - IMGUI_API void SetCursorPosX(float local_x); // GetWindowPos() + GetCursorPos() == GetCursorScreenPos() etc.) - IMGUI_API void SetCursorPosY(float local_y); // - IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position in window coordinates - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates (useful to work with ImDrawList API). generally top-left == GetMainViewport()->Pos == (0,0) in single viewport mode, and bottom-right == GetMainViewport()->Pos+Size == io.DisplaySize in single-viewport mode. - IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute coordinates - IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item) - IMGUI_API float GetTextLineHeight(); // ~ FontSize - IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) - IMGUI_API float GetFrameHeight(); // ~ FontSize + style.FramePadding.y * 2 - IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) - - // ID stack/scopes - // Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui. - // - Those questions are answered and impacted by understanding of the ID stack system: - // - "Q: Why is my widget not reacting when I click on it?" - // - "Q: How can I have widgets with an empty label?" - // - "Q: How can I have multiple widgets with the same label?" - // - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely - // want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. - // - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others. - // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID, - // whereas "str_id" denote a string that is only used as an ID and not normally displayed. - IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). - IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). - IMGUI_API void PushID(const void* ptr_id); // push pointer into the ID stack (will hash pointer). - IMGUI_API void PushID(int int_id); // push integer into the ID stack (will hash integer). - IMGUI_API void PopID(); // pop from the ID stack. - IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself - IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); - IMGUI_API ImGuiID GetID(const void* ptr_id); - - // Widgets: Text - IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text. - IMGUI_API void Text(const char* fmt, ...) IM_FMTARGS(1); // formatted text - IMGUI_API void TextV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); - IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API void TextDisabled(const char* fmt, ...) IM_FMTARGS(1); // shortcut for PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); Text(fmt, ...); PopStyleColor(); - IMGUI_API void TextDisabledV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void TextWrapped(const char* fmt, ...) IM_FMTARGS(1); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();. Note that this won't work on an auto-resizing window if there's no other widgets to extend the window width, yoy may need to set a size using SetNextWindowSize(). - IMGUI_API void TextWrappedV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void LabelText(const char* label, const char* fmt, ...) IM_FMTARGS(2); // display text+label aligned the same way as value+label widgets - IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API void BulletText(const char* fmt, ...) IM_FMTARGS(1); // shortcut for Bullet()+Text() - IMGUI_API void BulletTextV(const char* fmt, va_list args) IM_FMTLIST(1); - - // Widgets: Main - // - Most widgets return true when the value has been changed or when pressed/selected - // - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state. - IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0, 0)); // button - IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text - IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) - IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape - IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); - IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding - IMGUI_API bool Checkbox(const char* label, bool* v); - IMGUI_API bool CheckboxFlags(const char* label, int* flags, int flags_value); - IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); - IMGUI_API bool RadioButton(const char* label, bool active); // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; } - IMGUI_API bool RadioButton(const char* label, int* v, int v_button); // shortcut to handle the above pattern when value is an integer - IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), const char* overlay = NULL); - IMGUI_API void Bullet(); // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses - - // Widgets: Combo Box - // - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items. - // - The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. This is analogous to how ListBox are created. - IMGUI_API bool BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0); - IMGUI_API void EndCombo(); // only call EndCombo() if BeginCombo() returns true! - IMGUI_API bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1); - IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1); // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0" - IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); - - // Widgets: Drag Sliders - // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. - // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', - // the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x - // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. - // - Format string may also be set to NULL or use the default format ("%f" or "%d"). - // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). - // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used. - // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum. - // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. - // - Legacy: Pre-1.78 there are DragXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. - // If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361 - IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0); // If v_min >= v_max we have no bound - IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0); - IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0); - IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0); - IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", const char* format_max = NULL, ImGuiSliderFlags flags = 0); - IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0); // If v_min >= v_max we have no bound - IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0); - IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0); - IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0); - IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL, ImGuiSliderFlags flags = 0); - IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); - IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); - - // Widgets: Regular Sliders - // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. - // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. - // - Format string may also be set to NULL or use the default format ("%f" or "%d"). - // - Legacy: Pre-1.78 there are SliderXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. - // If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361 - IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. - IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); - IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); - IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); - IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = "%.0f deg", ImGuiSliderFlags flags = 0); - IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0); - IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0); - IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0); - IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0); - IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0); - IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0); - IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); - IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0); - IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0); - - // Widgets: Input with Keyboard - // - If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp. - // - Most of the ImGuiInputTextFlags flags are only useful for InputText() and not for InputFloatX, InputIntX, InputDouble etc. - IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputFloat4(const char* label, float v[4], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = "%.6f", ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); - - // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little color square that can be left-clicked to open a picker, and right-clicked to open an option menu.) - // - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. - // - You can pass the address of a first float element out of a contiguous structure, e.g. &myvector.x - IMGUI_API bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); - IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0); - IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); - IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL); - IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed. - IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls. - - // Widgets: Trees - // - TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents. - IMGUI_API bool TreeNode(const char* label); - IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to easily decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). - IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2); // " - IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API bool TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0); - IMGUI_API bool TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); - IMGUI_API bool TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); - IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); - IMGUI_API bool TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); - IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired. - IMGUI_API void TreePush(const void* ptr_id = NULL); // " - IMGUI_API void TreePop(); // ~ Unindent()+PopId() - IMGUI_API float GetTreeNodeToLabelSpacing(); // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode - IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop(). - IMGUI_API bool CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags = 0); // when 'p_visible != NULL': if '*p_visible==true' display an additional small close button on upper right of the header which will set the bool to false when clicked, if '*p_visible==false' don't display the header. - IMGUI_API void SetNextItemOpen(bool is_open, ImGuiCond cond = 0); // set next TreeNode/CollapsingHeader open state. - - // Widgets: Selectables - // - A selectable highlights when hovered, and can display another color when selected. - // - Neighbors selectable extend their highlight bounds in order to leave no gap between them. This is so a series of selected Selectable appear contiguous. - IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height - IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. - - // Widgets: List Boxes - // - This is essentially a thin wrapper to using BeginChild/EndChild with some stylistic changes. - // - The BeginListBox()/EndListBox() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() or any items. - // - The simplified/old ListBox() api are helpers over BeginListBox()/EndListBox() which are kept available for convenience purpose. This is analoguous to how Combos are created. - // - Choose frame width: size.x > 0.0f: custom / size.x < 0.0f or -FLT_MIN: right-align / size.x = 0.0f (default): use current ItemWidth - // - Choose frame height: size.y > 0.0f: custom / size.y < 0.0f or -FLT_MIN: bottom-align / size.y = 0.0f (default): arbitrary default height which can fit ~7 items - IMGUI_API bool BeginListBox(const char* label, const ImVec2& size = ImVec2(0, 0)); // open a framed scrolling region - IMGUI_API void EndListBox(); // only call EndListBox() if BeginListBox() returned true! - IMGUI_API bool ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1); - IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); - - // Widgets: Data Plotting - // - Consider using ImPlot (https://github.com/epezent/implot) which is much better! - IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); - IMGUI_API void PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); - IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); - IMGUI_API void PlotHistogram(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); - - // Widgets: Value() Helpers. - // - Those are merely shortcut to calling Text() with a format string. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace) - IMGUI_API void Value(const char* prefix, bool b); - IMGUI_API void Value(const char* prefix, int v); - IMGUI_API void Value(const char* prefix, unsigned int v); - IMGUI_API void Value(const char* prefix, float v, const char* float_format = NULL); - - // Widgets: Menus - // - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar. - // - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it. - // - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it. - // - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment. - IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). - IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! - IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. - IMGUI_API void EndMainMenuBar(); // only call EndMainMenuBar() if BeginMainMenuBar() returns true! - IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! - IMGUI_API void EndMenu(); // only call EndMenu() if BeginMenu() returns true! - IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. - IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL - - // Tooltips - // - Tooltip are windows following the mouse. They do not take focus away. - IMGUI_API void BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). - IMGUI_API void EndTooltip(); - IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). - IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); - - // Popups, Modals - // - They block normal mouse hovering detection (and therefore most mouse interactions) behind them. - // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. - // - Their visibility state (~bool) is held internally instead of being held by the programmer as we are used to with regular Begin*() calls. - // - The 3 properties above are related: we need to retain popup visibility state in the library because popups may be closed as any time. - // - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered(). - // - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack. - // This is sometimes leading to confusing mistakes. May rework this in the future. - - // Popups: begin/end functions - // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards. ImGuiWindowFlags are forwarded to the window. - // - BeginPopupModal(): block every interactions behind the window, cannot be closed by user, add a dimming background, has a title bar. - IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. - IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it. - IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! - - // Popups: open/close functions - // - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options. - // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. - // - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually. - // - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options). - // - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup(). - // - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened. - // - IMPORTANT: Notice that for OpenPopupOnItemClick() we exceptionally default flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter - IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!). - IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks - IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) - IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into. - - // Popups: open+begin combined functions helpers - // - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking. - // - They are convenient to easily create context menus, hence the name. - // - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future. - // - IMPORTANT: Notice that we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight. - IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! - IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window. - IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows). - - // Popups: query functions - // - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack. - // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack. - // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel: return true if any popup is open. - IMGUI_API bool IsPopupOpen(const char* str_id, ImGuiPopupFlags flags = 0); // return true if the popup is open. - - // Tables - // - Full-featured replacement for old Columns API. - // - See Demo->Tables for demo code. See top of imgui_tables.cpp for general commentary. - // - See ImGuiTableFlags_ and ImGuiTableColumnFlags_ enums for a description of available flags. - // The typical call flow is: - // - 1. Call BeginTable(), early out if returning false. - // - 2. Optionally call TableSetupColumn() to submit column name/flags/defaults. - // - 3. Optionally call TableSetupScrollFreeze() to request scroll freezing of columns/rows. - // - 4. Optionally call TableHeadersRow() to submit a header row. Names are pulled from TableSetupColumn() data. - // - 5. Populate contents: - // - In most situations you can use TableNextRow() + TableSetColumnIndex(N) to start appending into a column. - // - If you are using tables as a sort of grid, where every columns is holding the same type of contents, - // you may prefer using TableNextColumn() instead of TableNextRow() + TableSetColumnIndex(). - // TableNextColumn() will automatically wrap-around into the next row if needed. - // - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column! - // - Summary of possible call flow: - // -------------------------------------------------------------------------------------------------------- - // TableNextRow() -> TableSetColumnIndex(0) -> Text("Hello 0") -> TableSetColumnIndex(1) -> Text("Hello 1") // OK - // TableNextRow() -> TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK - // TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK: TableNextColumn() automatically gets to next row! - // TableNextRow() -> Text("Hello 0") // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear! - // -------------------------------------------------------------------------------------------------------- - // - 5. Call EndTable() - IMGUI_API bool BeginTable(const char* str_id, int column, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f); - IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true! - IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. - IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible. - IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. - - // Tables: Headers & Columns declaration - // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc. - // - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column. - // Headers are required to perform: reordering, sorting, and opening the context menu. - // The context menu can also be made available in columns body using ImGuiTableFlags_ContextMenuInBody. - // - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in - // some advanced use cases (e.g. adding custom widgets in header row). - // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. - IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0); - IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. - IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu - IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) - - // Tables: Sorting & Miscellaneous functions - // - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. - // When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have - // changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting, - // else you may wastefully sort your data every frame! - // - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index. - IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable(). - IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable) - IMGUI_API int TableGetColumnIndex(); // return current column index. - IMGUI_API int TableGetRowIndex(); // return current row index. - IMGUI_API const char* TableGetColumnName(int column_n = -1); // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column. - IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1); // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column. - IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) - IMGUI_API void TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1); // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details. - - // Legacy Columns API (prefer using Tables!) - // - You can also use SameLine(pos_x) to mimic simplified columns. - IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); - IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished - IMGUI_API int GetColumnIndex(); // get current column index - IMGUI_API float GetColumnWidth(int column_index = -1); // get column width (in pixels). pass -1 to use current column - IMGUI_API void SetColumnWidth(int column_index, float width); // set column width (in pixels). pass -1 to use current column - IMGUI_API float GetColumnOffset(int column_index = -1); // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetColumnsCount() inclusive. column 0 is typically 0.0f - IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column - IMGUI_API int GetColumnsCount(); - - // Tab Bars, Tabs - IMGUI_API bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0); // create and append into a TabBar - IMGUI_API void EndTabBar(); // only call EndTabBar() if BeginTabBar() returns true! - IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0); // create a Tab. Returns true if the Tab is selected. - IMGUI_API void EndTabItem(); // only call EndTabItem() if BeginTabItem() returns true! - IMGUI_API bool TabItemButton(const char* label, ImGuiTabItemFlags flags = 0); // create a Tab behaving like a button. return true when clicked. cannot be selected in the tab bar. - IMGUI_API void SetTabItemClosed(const char* tab_or_docked_window_label); // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name. - - // Logging/Capture - // - All text output from the interface can be captured into tty/file/clipboard. By default, tree nodes are automatically opened during logging. - IMGUI_API void LogToTTY(int auto_open_depth = -1); // start logging to tty (stdout) - IMGUI_API void LogToFile(int auto_open_depth = -1, const char* filename = NULL); // start logging to file - IMGUI_API void LogToClipboard(int auto_open_depth = -1); // start logging to OS clipboard - IMGUI_API void LogFinish(); // stop logging (close file, etc.) - IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard - IMGUI_API void LogText(const char* fmt, ...) IM_FMTARGS(1); // pass text data straight to log (without being displayed) - IMGUI_API void LogTextV(const char* fmt, va_list args) IM_FMTLIST(1); - - // Drag and Drop - // - On source items, call BeginDragDropSource(), if it returns true also call SetDragDropPayload() + EndDragDropSource(). - // - On target candidates, call BeginDragDropTarget(), if it returns true also call AcceptDragDropPayload() + EndDragDropTarget(). - // - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback "..." tooltip, see #1725) - // - An item can be both drag source and drop target. - IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call after submitting an item which may be dragged. when this return true, you can call SetDragDropPayload() + EndDragDropSource() - IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. Return true when payload has been accepted. - IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! - IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() - IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. - IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! - IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. - - // Disabling [BETA API] - // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) - // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) - // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. - IMGUI_API void BeginDisabled(bool disabled = true); - IMGUI_API void EndDisabled(); - - // Clipping - // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only. - IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); - IMGUI_API void PopClipRect(); - - // Focus, Activation - // - Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHereY()" when applicable to signify "this is the default item" - IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. - IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. - - // Item/Widgets Utilities and Query Functions - // - Most of the functions are referring to the previous Item that has been submitted. - // - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions. - IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options. - IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false) - IMGUI_API bool IsItemFocused(); // is the last item focused for keyboard/gamepad navigation? - IMGUI_API bool IsItemClicked(ImGuiMouseButton mouse_button = 0); // is the last item hovered and mouse clicked on? (**) == IsMouseClicked(mouse_button) && IsItemHovered()Important. (**) this it NOT equivalent to the behavior of e.g. Button(). Read comments in function definition. - IMGUI_API bool IsItemVisible(); // is the last item visible? (items may be out of sight because of clipping/scrolling) - IMGUI_API bool IsItemEdited(); // did the last item modify its underlying value this frame? or was pressed? This is generally the same as the "bool" return value of many widgets. - IMGUI_API bool IsItemActivated(); // was the last item just made active (item was previously inactive). - IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that requires continuous editing. - IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that requires continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). - IMGUI_API bool IsItemToggledOpen(); // was the last item open state toggled? set by TreeNode(). - IMGUI_API bool IsAnyItemHovered(); // is any item hovered? - IMGUI_API bool IsAnyItemActive(); // is any item active? - IMGUI_API bool IsAnyItemFocused(); // is any item focused? - IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) - IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) - IMGUI_API ImVec2 GetItemRectSize(); // get size of last item - IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. - - // Viewports - // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. - // - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports. - // - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. - IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. This can never be NULL. - - // Background/Foreground Draw Lists - IMGUI_API ImDrawList* GetBackgroundDrawList(); // this draw list will be the first rendered one. Useful to quickly draw shapes/text behind dear imgui contents. - IMGUI_API ImDrawList* GetForegroundDrawList(); // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. - - // Miscellaneous Utilities - IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. - IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. - IMGUI_API double GetTime(); // get global imgui time. incremented by io.DeltaTime every frame. - IMGUI_API int GetFrameCount(); // get global imgui frame count. incremented by 1 every frame. - IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances. - IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.). - IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) - IMGUI_API ImGuiStorage* GetStateStorage(); - IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame - IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) - - // Text Utilities - IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); - - // Color Utilities - IMGUI_API ImVec4 ColorConvertU32ToFloat4(ImU32 in); - IMGUI_API ImU32 ColorConvertFloat4ToU32(const ImVec4& in); - IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); - IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); - - // Inputs Utilities: Keyboard - // Without IMGUI_DISABLE_OBSOLETE_KEYIO: (legacy support) - // - For 'ImGuiKey key' you can still use your legacy native/user indices according to how your backend/engine stored them in io.KeysDown[]. - // With IMGUI_DISABLE_OBSOLETE_KEYIO: (this is the way forward) - // - Any use of 'ImGuiKey' will assert when key < 512 will be passed, previously reserved as native/user keys indices - // - GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined) - IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held. - IMGUI_API bool IsKeyPressed(ImGuiKey key, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate - IMGUI_API bool IsKeyReleased(ImGuiKey key); // was key released (went from Down to !Down)? - IMGUI_API int GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate - IMGUI_API const char* GetKeyName(ImGuiKey key); // [DEBUG] returns English name of the key. Those names a provided for debugging purpose and are not meant to be saved persistently not compared. - IMGUI_API void SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard); // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard"; after the next NewFrame() call. - - // Inputs Utilities: Mouse - // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right. - // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle. - // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold') - IMGUI_API bool IsMouseDown(ImGuiMouseButton button); // is mouse button held? - IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1. - IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) - IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true) - IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0). - IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. - IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available - IMGUI_API bool IsAnyMouseDown(); // [WILL OBSOLETE] is any mouse button held? This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid. - IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls - IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve mouse position at the time of opening popup we have BeginPopup() into (helper to avoid user backing that value themselves) - IMGUI_API bool IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f); // is mouse dragging? (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold) - IMGUI_API ImVec2 GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold) - IMGUI_API void ResetMouseDragDelta(ImGuiMouseButton button = 0); // - IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you - IMGUI_API void SetMouseCursor(ImGuiMouseCursor cursor_type); // set desired cursor type - IMGUI_API void SetNextFrameWantCaptureMouse(bool want_capture_mouse); // Override io.WantCaptureMouse flag next frame (said flag is left for your application to handle, typical when true it instucts your app to ignore inputs). This is equivalent to setting "io.WantCaptureMouse = want_capture_mouse;" after the next NewFrame() call. - - // Clipboard Utilities - // - Also see the LogToClipboard() function to capture GUI into clipboard, or easily output text data to the clipboard. - IMGUI_API const char* GetClipboardText(); - IMGUI_API void SetClipboardText(const char* text); - - // Settings/.Ini Utilities - // - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini"). - // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually. - // - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables). - IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename). - IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source. - IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext). - IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. - - // Debug Utilities - IMGUI_API void DebugTextEncoding(const char* text); - IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro. - - // Memory Allocators - // - Those functions are not reliant on the current context. - // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() - // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. - IMGUI_API void SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data = NULL); - IMGUI_API void GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data); - IMGUI_API void* MemAlloc(size_t size); - IMGUI_API void MemFree(void* ptr); - -} // namespace ImGui - -//----------------------------------------------------------------------------- -// [SECTION] Flags & Enumerations -//----------------------------------------------------------------------------- - -// Flags for ImGui::Begin() -enum ImGuiWindowFlags_ -{ - ImGuiWindowFlags_None = 0, - ImGuiWindowFlags_NoTitleBar = 1 << 0, // Disable title-bar - ImGuiWindowFlags_NoResize = 1 << 1, // Disable user resizing with the lower-right grip - ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window - ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programmatically) - ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. - ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node). - ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame - ImGuiWindowFlags_NoBackground = 1 << 7, // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f). - ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file - ImGuiWindowFlags_NoMouseInputs = 1 << 9, // Disable catching mouse, hovering test with pass through. - ImGuiWindowFlags_MenuBar = 1 << 10, // Has a menu-bar - ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. - ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, // Disable taking focus when transitioning from hidden to visible state - ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus) - ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) - ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) - ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) - ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window - ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) - ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. - ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, - ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, - ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, - - // [Internal] - ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] On child window: allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. - ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() - ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() - ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() - ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() - ImGuiWindowFlags_ChildMenu = 1 << 28 // Don't use! For internal use by BeginMenu() - //ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // [Obsolete] --> Set io.ConfigWindowsResizeFromEdges=true and make sure mouse cursors are supported by backend (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) -}; - -// Flags for ImGui::InputText() -enum ImGuiInputTextFlags_ -{ - ImGuiInputTextFlags_None = 0, - ImGuiInputTextFlags_CharsDecimal = 1 << 0, // Allow 0123456789.+-*/ - ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, // Allow 0123456789ABCDEFabcdef - ImGuiInputTextFlags_CharsUppercase = 1 << 2, // Turn a..z into A..Z - ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs - ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus - ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider looking at the IsItemDeactivatedAfterEdit() function. - ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Callback on pressing TAB (for completion handling) - ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Callback on pressing Up/Down arrows (for history handling) - ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Callback on each iteration. User code may query cursor position, modify text buffer. - ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. - ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field - ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is opposite: unfocus with Ctrl+Enter, add line with Enter). - ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally - ImGuiInputTextFlags_AlwaysOverwrite = 1 << 13, // Overwrite mode - ImGuiInputTextFlags_ReadOnly = 1 << 14, // Read-only mode - ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' - ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). - ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) - ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) - ImGuiInputTextFlags_CallbackEdit = 1 << 19 // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) - - // Obsolete names (will be removed soon) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior -#endif -}; - -// Flags for ImGui::TreeNodeEx(), ImGui::CollapsingHeader*() -enum ImGuiTreeNodeFlags_ -{ - ImGuiTreeNodeFlags_None = 0, - ImGuiTreeNodeFlags_Selected = 1 << 0, // Draw as selected - ImGuiTreeNodeFlags_Framed = 1 << 1, // Draw frame with background (e.g. for CollapsingHeader) - ImGuiTreeNodeFlags_AllowItemOverlap = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one - ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack - ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) - ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open - ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Need double-click to open node - ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open. - ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). - ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow - ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding(). - ImGuiTreeNodeFlags_SpanAvailWidth = 1 << 11, // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line. In the future we may refactor the hit system to be front-to-back, allowing natural overlaps and then this can become the default. - ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12, // Extend hit box to the left-most and right-most edges (bypass the indented area). - ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 13, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop) - //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 14, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible - ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog -}; - -// Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions. -// - To be backward compatible with older API which took an 'int mouse_button = 1' argument, we need to treat -// small flags values as a mouse button index, so we encode the mouse button in the first few bits of the flags. -// It is therefore guaranteed to be legal to pass a mouse button index in ImGuiPopupFlags. -// - For the same reason, we exceptionally default the ImGuiPopupFlags argument of BeginPopupContextXXX functions to 1 instead of 0. -// IMPORTANT: because the default parameter is 1 (==ImGuiPopupFlags_MouseButtonRight), if you rely on the default parameter -// and want to another another flag, you need to pass in the ImGuiPopupFlags_MouseButtonRight flag. -// - Multiple buttons currently cannot be combined/or-ed in those functions (we could allow it later). -enum ImGuiPopupFlags_ -{ - ImGuiPopupFlags_None = 0, - ImGuiPopupFlags_MouseButtonLeft = 0, // For BeginPopupContext*(): open on Left Mouse release. Guaranteed to always be == 0 (same as ImGuiMouseButton_Left) - ImGuiPopupFlags_MouseButtonRight = 1, // For BeginPopupContext*(): open on Right Mouse release. Guaranteed to always be == 1 (same as ImGuiMouseButton_Right) - ImGuiPopupFlags_MouseButtonMiddle = 2, // For BeginPopupContext*(): open on Middle Mouse release. Guaranteed to always be == 2 (same as ImGuiMouseButton_Middle) - ImGuiPopupFlags_MouseButtonMask_ = 0x1F, - ImGuiPopupFlags_MouseButtonDefault_ = 1, - ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 5, // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack - ImGuiPopupFlags_NoOpenOverItems = 1 << 6, // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space - ImGuiPopupFlags_AnyPopupId = 1 << 7, // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup. - ImGuiPopupFlags_AnyPopupLevel = 1 << 8, // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level) - ImGuiPopupFlags_AnyPopup = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel -}; - -// Flags for ImGui::Selectable() -enum ImGuiSelectableFlags_ -{ - ImGuiSelectableFlags_None = 0, - ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this don't close parent popup window - ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Selectable frame can span all columns (text will still fit in current column) - ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, // Generate press events on double clicks too - ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text - ImGuiSelectableFlags_AllowItemOverlap = 1 << 4 // (WIP) Hit testing to allow subsequent widgets to overlap this one -}; - -// Flags for ImGui::BeginCombo() -enum ImGuiComboFlags_ -{ - ImGuiComboFlags_None = 0, - ImGuiComboFlags_PopupAlignLeft = 1 << 0, // Align the popup toward the left by default - ImGuiComboFlags_HeightSmall = 1 << 1, // Max ~4 items visible. Tip: If you want your combo popup to be a specific size you can use SetNextWindowSizeConstraints() prior to calling BeginCombo() - ImGuiComboFlags_HeightRegular = 1 << 2, // Max ~8 items visible (default) - ImGuiComboFlags_HeightLarge = 1 << 3, // Max ~20 items visible - ImGuiComboFlags_HeightLargest = 1 << 4, // As many fitting items as possible - ImGuiComboFlags_NoArrowButton = 1 << 5, // Display on the preview box without the square arrow button - ImGuiComboFlags_NoPreview = 1 << 6, // Display only a square arrow button - ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest -}; - -// Flags for ImGui::BeginTabBar() -enum ImGuiTabBarFlags_ -{ - ImGuiTabBarFlags_None = 0, - ImGuiTabBarFlags_Reorderable = 1 << 0, // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list - ImGuiTabBarFlags_AutoSelectNewTabs = 1 << 1, // Automatically select new tabs when they appear - ImGuiTabBarFlags_TabListPopupButton = 1 << 2, // Disable buttons to open the tab list popup - ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 3, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. - ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4, // Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll) - ImGuiTabBarFlags_NoTooltip = 1 << 5, // Disable tooltips when hovering a tab - ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 6, // Resize tabs when they don't fit - ImGuiTabBarFlags_FittingPolicyScroll = 1 << 7, // Add scroll buttons when tabs don't fit - ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll, - ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown -}; - -// Flags for ImGui::BeginTabItem() -enum ImGuiTabItemFlags_ -{ - ImGuiTabItemFlags_None = 0, - ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Display a dot next to the title + tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. - ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem() - ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. - ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() - ImGuiTabItemFlags_NoTooltip = 1 << 4, // Disable tooltip for the given tab - ImGuiTabItemFlags_NoReorder = 1 << 5, // Disable reordering this tab or having another tab cross over this tab - ImGuiTabItemFlags_Leading = 1 << 6, // Enforce the tab position to the left of the tab bar (after the tab list popup button) - ImGuiTabItemFlags_Trailing = 1 << 7 // Enforce the tab position to the right of the tab bar (before the scrolling buttons) -}; - -// Flags for ImGui::BeginTable() -// - Important! Sizing policies have complex and subtle side effects, much more so than you would expect. -// Read comments/demos carefully + experiment with live demos to get acquainted with them. -// - The DEFAULT sizing policies are: -// - Default to ImGuiTableFlags_SizingFixedFit if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize. -// - Default to ImGuiTableFlags_SizingStretchSame if ScrollX is off. -// - When ScrollX is off: -// - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight. -// - Columns sizing policy allowed: Stretch (default), Fixed/Auto. -// - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all). -// - Stretch Columns will share the remaining width according to their respective weight. -// - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors. -// The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. -// (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing). -// - When ScrollX is on: -// - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed -// - Columns sizing policy allowed: Fixed/Auto mostly. -// - Fixed Columns can be enlarged as needed. Table will show an horizontal scrollbar if needed. -// - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop. -// - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable(). -// If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again. -// - Read on documentation at the top of imgui_tables.cpp for details. -enum ImGuiTableFlags_ -{ - // Features - ImGuiTableFlags_None = 0, - ImGuiTableFlags_Resizable = 1 << 0, // Enable resizing columns. - ImGuiTableFlags_Reorderable = 1 << 1, // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers) - ImGuiTableFlags_Hideable = 1 << 2, // Enable hiding/disabling columns in context menu. - ImGuiTableFlags_Sortable = 1 << 3, // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate. - ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width and sort settings in the .ini file. - ImGuiTableFlags_ContextMenuInBody = 1 << 5, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow(). - // Decorations - ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) - ImGuiTableFlags_BordersInnerH = 1 << 7, // Draw horizontal borders between rows. - ImGuiTableFlags_BordersOuterH = 1 << 8, // Draw horizontal borders at the top and bottom. - ImGuiTableFlags_BordersInnerV = 1 << 9, // Draw vertical borders between columns. - ImGuiTableFlags_BordersOuterV = 1 << 10, // Draw vertical borders on the left and right sides. - ImGuiTableFlags_BordersH = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders. - ImGuiTableFlags_BordersV = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders. - ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders. - ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders. - ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, // Draw all borders. - ImGuiTableFlags_NoBordersInBody = 1 << 11, // [ALPHA] Disable vertical borders in columns Body (borders will always appears in Headers). -> May move to style - ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12, // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers). -> May move to style - // Sizing Policy (read above for defaults) - ImGuiTableFlags_SizingFixedFit = 1 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width. - ImGuiTableFlags_SizingFixedSame = 2 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible. - ImGuiTableFlags_SizingStretchProp = 3 << 13, // Columns default to _WidthStretch with default weights proportional to each columns contents widths. - ImGuiTableFlags_SizingStretchSame = 4 << 13, // Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn(). - // Sizing Extra Options - ImGuiTableFlags_NoHostExtendX = 1 << 16, // Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. - ImGuiTableFlags_NoHostExtendY = 1 << 17, // Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible. - ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable. - ImGuiTableFlags_PreciseWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. - // Clipping - ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). - // Padding - ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outer-most padding. Generally desirable if you have headers. - ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outer-most padding. - ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). - // Scrolling - ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. - ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. - // Sorting - ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). - ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). - - // [Internal] Combinations and masks - ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame - - // Obsolete names (will be removed soon) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //, ImGuiTableFlags_ColumnsWidthFixed = ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_ColumnsWidthStretch = ImGuiTableFlags_SizingStretchSame // WIP Tables 2020/12 - //, ImGuiTableFlags_SizingPolicyFixed = ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingPolicyStretch = ImGuiTableFlags_SizingStretchSame // WIP Tables 2021/01 -#endif -}; - -// Flags for ImGui::TableSetupColumn() -enum ImGuiTableColumnFlags_ -{ - // Input configuration flags - ImGuiTableColumnFlags_None = 0, - ImGuiTableColumnFlags_Disabled = 1 << 0, // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state) - ImGuiTableColumnFlags_DefaultHide = 1 << 1, // Default as a hidden/disabled column. - ImGuiTableColumnFlags_DefaultSort = 1 << 2, // Default as a sorting column. - ImGuiTableColumnFlags_WidthStretch = 1 << 3, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). - ImGuiTableColumnFlags_WidthFixed = 1 << 4, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). - ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing. - ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. - ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column. - ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command). - ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). - ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. - ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. - ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit label for this column. Convenient for some small columns. Name will still appear in context menu. - ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. - ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). - ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. - ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0). - ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. - - // Output status flags, read-only via TableGetColumnFlags() - ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. - ImGuiTableColumnFlags_IsVisible = 1 << 25, // Status: is visible == is enabled AND not clipped by scrolling. - ImGuiTableColumnFlags_IsSorted = 1 << 26, // Status: is currently part of the sort specs - ImGuiTableColumnFlags_IsHovered = 1 << 27, // Status: is hovered by mouse - - // [Internal] Combinations and masks - ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed, - ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable, - ImGuiTableColumnFlags_StatusMask_ = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered, - ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30 // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge) - - // Obsolete names (will be removed soon) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //ImGuiTableColumnFlags_WidthAuto = ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, // Column will not stretch and keep resizing based on submitted contents. -#endif -}; - -// Flags for ImGui::TableNextRow() -enum ImGuiTableRowFlags_ -{ - ImGuiTableRowFlags_None = 0, - ImGuiTableRowFlags_Headers = 1 << 0 // Identify header row (set default background color + width of its contents accounted differently for auto column width) -}; - -// Enum for ImGui::TableSetBgColor() -// Background colors are rendering in 3 layers: -// - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set. -// - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set. -// - Layer 2: draw with CellBg color if set. -// The purpose of the two row/columns layers is to let you decide if a background color changes should override or blend with the existing color. -// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows. -// If you set the color of RowBg0 target, your color will override the existing RowBg0 color. -// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color. -enum ImGuiTableBgTarget_ -{ - ImGuiTableBgTarget_None = 0, - ImGuiTableBgTarget_RowBg0 = 1, // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used) - ImGuiTableBgTarget_RowBg1 = 2, // Set row background color 1 (generally used for selection marking) - ImGuiTableBgTarget_CellBg = 3 // Set cell background color (top-most color) -}; - -// Flags for ImGui::IsWindowFocused() -enum ImGuiFocusedFlags_ -{ - ImGuiFocusedFlags_None = 0, - ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused - ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) - ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! - ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) - //ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) - ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows -}; - -// Flags for ImGui::IsItemHovered(), ImGui::IsWindowHovered() -// Note: if you are trying to check whether your mouse should be dispatched to Dear ImGui or to your app, you should use 'io.WantCaptureMouse' instead! Please read the FAQ! -// Note: windows with the ImGuiWindowFlags_NoInputs flag are ignored by IsWindowHovered() calls. -enum ImGuiHoveredFlags_ -{ - ImGuiHoveredFlags_None = 0, // Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them. - ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered - ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) - ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) - //ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window - //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. - ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled - ImGuiHoveredFlags_NoNavOverride = 1 << 10, // Disable using gamepad/keyboard navigation state when active, always query mouse. - ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, - ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows -}; - -// Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() -enum ImGuiDragDropFlags_ -{ - ImGuiDragDropFlags_None = 0, - // BeginDragDropSource() flags - ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0, // By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disable this behavior. - ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1, // By default, when dragging we clear data so that IsItemHovered() will return false, to avoid subsequent user code submitting tooltips. This flag disable this behavior so you can still call IsItemHovered() on the source item. - ImGuiDragDropFlags_SourceNoHoldToOpenOthers = 1 << 2, // Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item. - ImGuiDragDropFlags_SourceAllowNullID = 1 << 3, // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit. - ImGuiDragDropFlags_SourceExtern = 1 << 4, // External source (from outside of dear imgui), won't attempt to read current item/window info. Will always return true. Only one Extern source can be active simultaneously. - ImGuiDragDropFlags_SourceAutoExpirePayload = 1 << 5, // Automatically expire the payload if the source cease to be submitted (otherwise payloads are persisting while being dragged) - // AcceptDragDropPayload() flags - ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered. - ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, // Do not draw the default highlight rectangle when hovering over target. - ImGuiDragDropFlags_AcceptNoPreviewTooltip = 1 << 12, // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site. - ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect // For peeking ahead and inspecting the payload before delivery. -}; - -// Standard Drag and Drop payload types. You can define you own payload types using short strings. Types starting with '_' are defined by Dear ImGui. -#define IMGUI_PAYLOAD_TYPE_COLOR_3F "_COL3F" // float[3]: Standard type for colors, without alpha. User code may use this type. -#define IMGUI_PAYLOAD_TYPE_COLOR_4F "_COL4F" // float[4]: Standard type for colors. User code may use this type. - -// A primary data type -enum ImGuiDataType_ -{ - ImGuiDataType_S8, // signed char / char (with sensible compilers) - ImGuiDataType_U8, // unsigned char - ImGuiDataType_S16, // short - ImGuiDataType_U16, // unsigned short - ImGuiDataType_S32, // int - ImGuiDataType_U32, // unsigned int - ImGuiDataType_S64, // long long / __int64 - ImGuiDataType_U64, // unsigned long long / unsigned __int64 - ImGuiDataType_Float, // float - ImGuiDataType_Double, // double - ImGuiDataType_COUNT -}; - -// A cardinal direction -enum ImGuiDir_ -{ - ImGuiDir_None = -1, - ImGuiDir_Left = 0, - ImGuiDir_Right = 1, - ImGuiDir_Up = 2, - ImGuiDir_Down = 3, - ImGuiDir_COUNT -}; - -// A sorting direction -enum ImGuiSortDirection_ -{ - ImGuiSortDirection_None = 0, - ImGuiSortDirection_Ascending = 1, // Ascending = 0->9, A->Z etc. - ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc. -}; - -// Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87) -// Keys value >= 512 are named keys (>= 1.87) -enum ImGuiKey_ -{ - // Keyboard - ImGuiKey_None = 0, - ImGuiKey_Tab = 512, // == ImGuiKey_NamedKey_BEGIN - ImGuiKey_LeftArrow, - ImGuiKey_RightArrow, - ImGuiKey_UpArrow, - ImGuiKey_DownArrow, - ImGuiKey_PageUp, - ImGuiKey_PageDown, - ImGuiKey_Home, - ImGuiKey_End, - ImGuiKey_Insert, - ImGuiKey_Delete, - ImGuiKey_Backspace, - ImGuiKey_Space, - ImGuiKey_Enter, - ImGuiKey_Escape, - ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper, - ImGuiKey_RightCtrl, ImGuiKey_RightShift, ImGuiKey_RightAlt, ImGuiKey_RightSuper, - ImGuiKey_Menu, - ImGuiKey_0, ImGuiKey_1, ImGuiKey_2, ImGuiKey_3, ImGuiKey_4, ImGuiKey_5, ImGuiKey_6, ImGuiKey_7, ImGuiKey_8, ImGuiKey_9, - ImGuiKey_A, ImGuiKey_B, ImGuiKey_C, ImGuiKey_D, ImGuiKey_E, ImGuiKey_F, ImGuiKey_G, ImGuiKey_H, ImGuiKey_I, ImGuiKey_J, - ImGuiKey_K, ImGuiKey_L, ImGuiKey_M, ImGuiKey_N, ImGuiKey_O, ImGuiKey_P, ImGuiKey_Q, ImGuiKey_R, ImGuiKey_S, ImGuiKey_T, - ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z, - ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6, - ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12, - ImGuiKey_Apostrophe, // ' - ImGuiKey_Comma, // , - ImGuiKey_Minus, // - - ImGuiKey_Period, // . - ImGuiKey_Slash, // / - ImGuiKey_Semicolon, // ; - ImGuiKey_Equal, // = - ImGuiKey_LeftBracket, // [ - ImGuiKey_Backslash, // \ (this text inhibit multiline comment caused by backslash) - ImGuiKey_RightBracket, // ] - ImGuiKey_GraveAccent, // ` - ImGuiKey_CapsLock, - ImGuiKey_ScrollLock, - ImGuiKey_NumLock, - ImGuiKey_PrintScreen, - ImGuiKey_Pause, - ImGuiKey_Keypad0, ImGuiKey_Keypad1, ImGuiKey_Keypad2, ImGuiKey_Keypad3, ImGuiKey_Keypad4, - ImGuiKey_Keypad5, ImGuiKey_Keypad6, ImGuiKey_Keypad7, ImGuiKey_Keypad8, ImGuiKey_Keypad9, - ImGuiKey_KeypadDecimal, - ImGuiKey_KeypadDivide, - ImGuiKey_KeypadMultiply, - ImGuiKey_KeypadSubtract, - ImGuiKey_KeypadAdd, - ImGuiKey_KeypadEnter, - ImGuiKey_KeypadEqual, - - // Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION action - ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) // -- - ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) // -- - ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // -> ImGuiNavInput_Input - ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // -> ImGuiNavInput_Activate - ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // -> ImGuiNavInput_Menu - ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // -> ImGuiNavInput_Cancel - ImGuiKey_GamepadDpadUp, // D-pad Up // -> ImGuiNavInput_DpadUp - ImGuiKey_GamepadDpadDown, // D-pad Down // -> ImGuiNavInput_DpadDown - ImGuiKey_GamepadDpadLeft, // D-pad Left // -> ImGuiNavInput_DpadLeft - ImGuiKey_GamepadDpadRight, // D-pad Right // -> ImGuiNavInput_DpadRight - ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // -> ImGuiNavInput_FocusPrev + ImGuiNavInput_TweakSlow - ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // -> ImGuiNavInput_FocusNext + ImGuiNavInput_TweakFast - ImGuiKey_GamepadL2, // L Trigger (Xbox) ZL (Switch) L2 (PS) [Analog] - ImGuiKey_GamepadR2, // R Trigger (Xbox) ZR (Switch) R2 (PS) [Analog] - ImGuiKey_GamepadL3, // L Thumbstick (Xbox) L3 (Switch) L3 (PS) - ImGuiKey_GamepadR3, // R Thumbstick (Xbox) R3 (Switch) R3 (PS) - ImGuiKey_GamepadLStickUp, // [Analog] // -> ImGuiNavInput_LStickUp - ImGuiKey_GamepadLStickDown, // [Analog] // -> ImGuiNavInput_LStickDown - ImGuiKey_GamepadLStickLeft, // [Analog] // -> ImGuiNavInput_LStickLeft - ImGuiKey_GamepadLStickRight, // [Analog] // -> ImGuiNavInput_LStickRight - ImGuiKey_GamepadRStickUp, // [Analog] - ImGuiKey_GamepadRStickDown, // [Analog] - ImGuiKey_GamepadRStickLeft, // [Analog] - ImGuiKey_GamepadRStickRight, // [Analog] - - // Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls) - // - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing - // them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc. - // - Code polling every keys (e.g. an interface to detect a key press for input mapping) might want to ignore those - // and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiKey_ModCtrl). - // - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys. - // In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and - // backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user... - ImGuiKey_ModCtrl, ImGuiKey_ModShift, ImGuiKey_ModAlt, ImGuiKey_ModSuper, - - // End of list - ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value - - // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + a io.KeyMap[] array. - // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) - ImGuiKey_NamedKey_BEGIN = 512, - ImGuiKey_NamedKey_END = ImGuiKey_COUNT, - ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, -#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys - ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in io.KeysData[0]. Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET). -#else - ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys - ImGuiKey_KeysData_OFFSET = 0 // First key stored in io.KeysData[0]. Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET). -#endif - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter // Renamed in 1.87 -#endif -}; - -// Helper "flags" version of key-mods to store and compare multiple key-mods easily. Sometimes used for storage (e.g. io.KeyMods) but otherwise not much used in public API. -enum ImGuiModFlags_ -{ - ImGuiModFlags_None = 0, - ImGuiModFlags_Ctrl = 1 << 0, - ImGuiModFlags_Shift = 1 << 1, - ImGuiModFlags_Alt = 1 << 2, // Menu - ImGuiModFlags_Super = 1 << 3 // Cmd/Super/Windows key -}; - -// Gamepad/Keyboard navigation -// Since >= 1.87 backends you generally don't need to care about this enum since io.NavInputs[] is setup automatically. This might become private/internal some day. -// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. -// Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). -// Read instructions in imgui.cpp for more details. Download PNG/PSD at http://dearimgui.org/controls_sheets. -enum ImGuiNavInput_ -{ - // Gamepad Mapping - ImGuiNavInput_Activate, // Activate / Open / Toggle / Tweak value // e.g. Cross (PS4), A (Xbox), A (Switch), Space (Keyboard) - ImGuiNavInput_Cancel, // Cancel / Close / Exit // e.g. Circle (PS4), B (Xbox), B (Switch), Escape (Keyboard) - ImGuiNavInput_Input, // Text input / On-Screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard) - ImGuiNavInput_Menu, // Tap: Toggle menu / Hold: Focus, Move, Resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard) - ImGuiNavInput_DpadLeft, // Move / Tweak / Resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard) - ImGuiNavInput_DpadRight, // - ImGuiNavInput_DpadUp, // - ImGuiNavInput_DpadDown, // - ImGuiNavInput_LStickLeft, // Scroll / Move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down - ImGuiNavInput_LStickRight, // - ImGuiNavInput_LStickUp, // - ImGuiNavInput_LStickDown, // - ImGuiNavInput_FocusPrev, // Focus Next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_FocusNext, // Focus Prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) - ImGuiNavInput_TweakSlow, // Slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_TweakFast, // Faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) - - // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. - // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from keyboard keys instead of io.NavInputs[]. - ImGuiNavInput_KeyLeft_, // Move left // = Arrow keys - ImGuiNavInput_KeyRight_, // Move right - ImGuiNavInput_KeyUp_, // Move up - ImGuiNavInput_KeyDown_, // Move down - ImGuiNavInput_COUNT -}; - -// Configuration flags stored in io.ConfigFlags. Set by user/application. -enum ImGuiConfigFlags_ -{ - ImGuiConfigFlags_None = 0, - ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.AddKeyEvent() calls - ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui backend to fill io.NavInputs[]. Backend also needs to set ImGuiBackendFlags_HasGamepad. - ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your backend, otherwise ImGui will react as if the mouse is jumping around back and forth. - ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set. - ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information set by the backend. - ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. - - // User storage (to allow your backend/engine to communicate to code that may be shared between multiple projects. Those flags are NOT used by core Dear ImGui) - ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. - ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse. -}; - -// Backend capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom backend. -enum ImGuiBackendFlags_ -{ - ImGuiBackendFlags_None = 0, - ImGuiBackendFlags_HasGamepad = 1 << 0, // Backend Platform supports gamepad and currently has one connected. - ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape. - ImGuiBackendFlags_HasSetMousePos = 1 << 2, // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set). - ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3 // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices. -}; - -// Enumeration for PushStyleColor() / PopStyleColor() -enum ImGuiCol_ -{ - ImGuiCol_Text, - ImGuiCol_TextDisabled, - ImGuiCol_WindowBg, // Background of normal windows - ImGuiCol_ChildBg, // Background of child windows - ImGuiCol_PopupBg, // Background of popups, menus, tooltips windows - ImGuiCol_Border, - ImGuiCol_BorderShadow, - ImGuiCol_FrameBg, // Background of checkbox, radio button, plot, slider, text input - ImGuiCol_FrameBgHovered, - ImGuiCol_FrameBgActive, - ImGuiCol_TitleBg, - ImGuiCol_TitleBgActive, - ImGuiCol_TitleBgCollapsed, - ImGuiCol_MenuBarBg, - ImGuiCol_ScrollbarBg, - ImGuiCol_ScrollbarGrab, - ImGuiCol_ScrollbarGrabHovered, - ImGuiCol_ScrollbarGrabActive, - ImGuiCol_CheckMark, - ImGuiCol_SliderGrab, - ImGuiCol_SliderGrabActive, - ImGuiCol_Button, - ImGuiCol_ButtonHovered, - ImGuiCol_ButtonActive, - ImGuiCol_Header, // Header* colors are used for CollapsingHeader, TreeNode, Selectable, MenuItem - ImGuiCol_HeaderHovered, - ImGuiCol_HeaderActive, - ImGuiCol_Separator, - ImGuiCol_SeparatorHovered, - ImGuiCol_SeparatorActive, - ImGuiCol_ResizeGrip, // Resize grip in lower-right and lower-left corners of windows. - ImGuiCol_ResizeGripHovered, - ImGuiCol_ResizeGripActive, - ImGuiCol_Tab, // TabItem in a TabBar - ImGuiCol_TabHovered, - ImGuiCol_TabActive, - ImGuiCol_TabUnfocused, - ImGuiCol_TabUnfocusedActive, - ImGuiCol_PlotLines, - ImGuiCol_PlotLinesHovered, - ImGuiCol_PlotHistogram, - ImGuiCol_PlotHistogramHovered, - ImGuiCol_TableHeaderBg, // Table header background - ImGuiCol_TableBorderStrong, // Table outer and header borders (prefer using Alpha=1.0 here) - ImGuiCol_TableBorderLight, // Table inner borders (prefer using Alpha=1.0 here) - ImGuiCol_TableRowBg, // Table row background (even rows) - ImGuiCol_TableRowBgAlt, // Table row background (odd rows) - ImGuiCol_TextSelectedBg, - ImGuiCol_DragDropTarget, // Rectangle highlighting a drop target - ImGuiCol_NavHighlight, // Gamepad/keyboard: current highlighted item - ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB - ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active - ImGuiCol_ModalWindowDimBg, // Darken/colorize entire screen behind a modal window, when one is active - ImGuiCol_COUNT -}; - -// Enumeration for PushStyleVar() / PopStyleVar() to temporarily modify the ImGuiStyle structure. -// - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code. -// During initialization or between frames, feel free to just poke into ImGuiStyle directly. -// - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description. -// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. -// - When changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type. -enum ImGuiStyleVar_ -{ - // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions) - ImGuiStyleVar_Alpha, // float Alpha - ImGuiStyleVar_DisabledAlpha, // float DisabledAlpha - ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding - ImGuiStyleVar_WindowRounding, // float WindowRounding - ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize - ImGuiStyleVar_WindowMinSize, // ImVec2 WindowMinSize - ImGuiStyleVar_WindowTitleAlign, // ImVec2 WindowTitleAlign - ImGuiStyleVar_ChildRounding, // float ChildRounding - ImGuiStyleVar_ChildBorderSize, // float ChildBorderSize - ImGuiStyleVar_PopupRounding, // float PopupRounding - ImGuiStyleVar_PopupBorderSize, // float PopupBorderSize - ImGuiStyleVar_FramePadding, // ImVec2 FramePadding - ImGuiStyleVar_FrameRounding, // float FrameRounding - ImGuiStyleVar_FrameBorderSize, // float FrameBorderSize - ImGuiStyleVar_ItemSpacing, // ImVec2 ItemSpacing - ImGuiStyleVar_ItemInnerSpacing, // ImVec2 ItemInnerSpacing - ImGuiStyleVar_IndentSpacing, // float IndentSpacing - ImGuiStyleVar_CellPadding, // ImVec2 CellPadding - ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize - ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding - ImGuiStyleVar_GrabMinSize, // float GrabMinSize - ImGuiStyleVar_GrabRounding, // float GrabRounding - ImGuiStyleVar_TabRounding, // float TabRounding - ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign - ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign - ImGuiStyleVar_COUNT -}; - -// Flags for InvisibleButton() [extended in imgui_internal.h] -enum ImGuiButtonFlags_ -{ - ImGuiButtonFlags_None = 0, - ImGuiButtonFlags_MouseButtonLeft = 1 << 0, // React on left mouse button (default) - ImGuiButtonFlags_MouseButtonRight = 1 << 1, // React on right mouse button - ImGuiButtonFlags_MouseButtonMiddle = 1 << 2, // React on center mouse button - - // [Internal] - ImGuiButtonFlags_MouseButtonMask_ = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle, - ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft -}; - -// Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton() -enum ImGuiColorEditFlags_ -{ - ImGuiColorEditFlags_None = 0, - ImGuiColorEditFlags_NoAlpha = 1 << 1, // // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (will only read 3 components from the input pointer). - ImGuiColorEditFlags_NoPicker = 1 << 2, // // ColorEdit: disable picker when clicking on color square. - ImGuiColorEditFlags_NoOptions = 1 << 3, // // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview. - ImGuiColorEditFlags_NoSmallPreview = 1 << 4, // // ColorEdit, ColorPicker: disable color square preview next to the inputs. (e.g. to show only the inputs) - ImGuiColorEditFlags_NoInputs = 1 << 5, // // ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview color square). - ImGuiColorEditFlags_NoTooltip = 1 << 6, // // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview. - ImGuiColorEditFlags_NoLabel = 1 << 7, // // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker). - ImGuiColorEditFlags_NoSidePreview = 1 << 8, // // ColorPicker: disable bigger color preview on right side of the picker, use small color square preview instead. - ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source. - ImGuiColorEditFlags_NoBorder = 1 << 10, // // ColorButton: disable border (which is enforced by default) - - // User Options (right-click on widget to change some of them). - ImGuiColorEditFlags_AlphaBar = 1 << 16, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. - ImGuiColorEditFlags_AlphaPreview = 1 << 17, // // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque. - ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 18, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque. - ImGuiColorEditFlags_HDR = 1 << 19, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). - ImGuiColorEditFlags_DisplayRGB = 1 << 20, // [Display] // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex. - ImGuiColorEditFlags_DisplayHSV = 1 << 21, // [Display] // " - ImGuiColorEditFlags_DisplayHex = 1 << 22, // [Display] // " - ImGuiColorEditFlags_Uint8 = 1 << 23, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255. - ImGuiColorEditFlags_Float = 1 << 24, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers. - ImGuiColorEditFlags_PickerHueBar = 1 << 25, // [Picker] // ColorPicker: bar for Hue, rectangle for Sat/Value. - ImGuiColorEditFlags_PickerHueWheel = 1 << 26, // [Picker] // ColorPicker: wheel for Hue, triangle for Sat/Value. - ImGuiColorEditFlags_InputRGB = 1 << 27, // [Input] // ColorEdit, ColorPicker: input and output data in RGB format. - ImGuiColorEditFlags_InputHSV = 1 << 28, // [Input] // ColorEdit, ColorPicker: input and output data in HSV format. - - // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to - // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup. - ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, - - // [Internal] Masks - ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, - ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, - ImGuiColorEditFlags_PickerMask_ = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, - ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV - - // Obsolete names (will be removed) - // ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] -}; - -// Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. -// We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. -enum ImGuiSliderFlags_ -{ - ImGuiSliderFlags_None = 0, - ImGuiSliderFlags_AlwaysClamp = 1 << 4, // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds. - ImGuiSliderFlags_Logarithmic = 1 << 5, // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits. - ImGuiSliderFlags_NoRoundToFormat = 1 << 6, // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits) - ImGuiSliderFlags_NoInput = 1 << 7, // Disable CTRL+Click or Enter key allowing to input text directly into the widget - ImGuiSliderFlags_InvalidMask_ = 0x7000000F // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed. - - // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiSliderFlags_ClampOnInput = ImGuiSliderFlags_AlwaysClamp // [renamed in 1.79] -#endif -}; - -// Identify a mouse button. -// Those values are guaranteed to be stable and we frequently use 0/1 directly. Named enums provided for convenience. -enum ImGuiMouseButton_ -{ - ImGuiMouseButton_Left = 0, - ImGuiMouseButton_Right = 1, - ImGuiMouseButton_Middle = 2, - ImGuiMouseButton_COUNT = 5 -}; - -// Enumeration for GetMouseCursor() -// User code may request backend to display given cursor by calling SetMouseCursor(), which is why we have some cursors that are marked unused here -enum ImGuiMouseCursor_ -{ - ImGuiMouseCursor_None = -1, - ImGuiMouseCursor_Arrow = 0, - ImGuiMouseCursor_TextInput, // When hovering over InputText, etc. - ImGuiMouseCursor_ResizeAll, // (Unused by Dear ImGui functions) - ImGuiMouseCursor_ResizeNS, // When hovering over an horizontal border - ImGuiMouseCursor_ResizeEW, // When hovering over a vertical border or a column - ImGuiMouseCursor_ResizeNESW, // When hovering over the bottom-left corner of a window - ImGuiMouseCursor_ResizeNWSE, // When hovering over the bottom-right corner of a window - ImGuiMouseCursor_Hand, // (Unused by Dear ImGui functions. Use for e.g. hyperlinks) - ImGuiMouseCursor_NotAllowed, // When hovering something with disallowed interaction. Usually a crossed circle. - ImGuiMouseCursor_COUNT -}; - -// Enumeration for ImGui::SetWindow***(), SetNextWindow***(), SetNextItem***() functions -// Represent a condition. -// Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond_Always. -enum ImGuiCond_ -{ - ImGuiCond_None = 0, // No condition (always set the variable), same as _Always - ImGuiCond_Always = 1 << 0, // No condition (always set the variable) - ImGuiCond_Once = 1 << 1, // Set the variable once per runtime session (only the first call will succeed) - ImGuiCond_FirstUseEver = 1 << 2, // Set the variable if the object/window has no persistently saved data (no entry in .ini file) - ImGuiCond_Appearing = 1 << 3 // Set the variable if the object/window is appearing after being hidden/inactive (or the first time) -}; - -//----------------------------------------------------------------------------- -// [SECTION] Helpers: Memory allocations macros, ImVector<> -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// IM_MALLOC(), IM_FREE(), IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE() -// We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. -// Defining a custom placement new() with a custom parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. -//----------------------------------------------------------------------------- - -struct ImNewWrapper {}; -inline void* operator new(size_t, ImNewWrapper, void* ptr) { return ptr; } -inline void operator delete(void*, ImNewWrapper, void*) {} // This is only required so we can use the symmetrical new() -#define IM_ALLOC(_SIZE) ImGui::MemAlloc(_SIZE) -#define IM_FREE(_PTR) ImGui::MemFree(_PTR) -#define IM_PLACEMENT_NEW(_PTR) new(ImNewWrapper(), _PTR) -#define IM_NEW(_TYPE) new(ImNewWrapper(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE -template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } } - -//----------------------------------------------------------------------------- -// ImVector<> -// Lightweight std::vector<>-like class to avoid dragging dependencies (also, some implementations of STL with debug enabled are absurdly slow, we bypass it so our code runs fast in debug). -//----------------------------------------------------------------------------- -// - You generally do NOT need to care or use this ever. But we need to make it available in imgui.h because some of our public structures are relying on it. -// - We use std-like naming convention here, which is a little unusual for this codebase. -// - Important: clear() frees memory, resize(0) keep the allocated buffer. We use resize(0) a lot to intentionally recycle allocated buffers across frames and amortize our costs. -// - Important: our implementation does NOT call C++ constructors/destructors, we treat everything as raw data! This is intentional but be extra mindful of that, -// Do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset. -//----------------------------------------------------------------------------- - -IM_MSVC_RUNTIME_CHECKS_OFF -template -struct ImVector -{ - int Size; - int Capacity; - T* Data; - - // Provide standard typedefs but we don't use them ourselves. - typedef T value_type; - typedef value_type* iterator; - typedef const value_type* const_iterator; - - // Constructors, destructor - inline ImVector() { Size = Capacity = 0; Data = NULL; } - inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } - inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } - inline ~ImVector() { if (Data) IM_FREE(Data); } // Important: does not destruct anything - - inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } // Important: does not destruct anything - inline void clear_delete() { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); } // Important: never called automatically! always explicit. - inline void clear_destruct() { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); } // Important: never called automatically! always explicit. - - inline bool empty() const { return Size == 0; } - inline int size() const { return Size; } - inline int size_in_bytes() const { return Size * (int)sizeof(T); } - inline int max_size() const { return 0x7FFFFFFF / (int)sizeof(T); } - inline int capacity() const { return Capacity; } - inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } - inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } - - inline T* begin() { return Data; } - inline const T* begin() const { return Data; } - inline T* end() { return Data + Size; } - inline const T* end() const { return Data + Size; } - inline T& front() { IM_ASSERT(Size > 0); return Data[0]; } - inline const T& front() const { IM_ASSERT(Size > 0); return Data[0]; } - inline T& back() { IM_ASSERT(Size > 0); return Data[Size - 1]; } - inline const T& back() const { IM_ASSERT(Size > 0); return Data[Size - 1]; } - inline void swap(ImVector& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; T* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } - - inline int _grow_capacity(int sz) const { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; } - inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; } - inline void resize(int new_size, const T& v) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; } - inline void shrink(int new_size) { IM_ASSERT(new_size <= Size); Size = new_size; } // Resize a vector to a smaller size, guaranteed not to cause a reallocation - inline void reserve(int new_capacity) { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; } - inline void reserve_discard(int new_capacity) { if (new_capacity <= Capacity) return; if (Data) IM_FREE(Data); Data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); Capacity = new_capacity; } - - // NB: It is illegal to call push_back/push_front/insert with a reference pointing inside the ImVector data itself! e.g. v.push_back(v[10]) is forbidden. - inline void push_back(const T& v) { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; } - inline void pop_back() { IM_ASSERT(Size > 0); Size--; } - inline void push_front(const T& v) { if (Size == 0) push_back(v); else insert(Data, v); } - inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; } - inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last >= it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; } - inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; } - inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; } - inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } - inline T* find(const T& v) { T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; } - inline const T* find(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; } - inline bool find_erase(const T& v) { const T* it = find(v); if (it < Data + Size) { erase(it); return true; } return false; } - inline bool find_erase_unsorted(const T& v) { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; } - inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; } -}; -IM_MSVC_RUNTIME_CHECKS_RESTORE - -//----------------------------------------------------------------------------- -// [SECTION] ImGuiStyle -//----------------------------------------------------------------------------- -// You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame(). -// During the frame, use ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values, -// and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors. -//----------------------------------------------------------------------------- - -struct ImGuiStyle -{ - float Alpha; // Global alpha applies to everything in Dear ImGui. - float DisabledAlpha; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. - ImVec2 WindowPadding; // Padding within a window. - float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. - float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - ImVec2 WindowMinSize; // Minimum window size. This is a global setting. If you want to constraint individual windows, use SetNextWindowSizeConstraints(). - ImVec2 WindowTitleAlign; // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered. - ImGuiDir WindowMenuButtonPosition; // Side of the collapsing/docking button in the title bar (None/Left/Right). Defaults to ImGuiDir_Left. - float ChildRounding; // Radius of child window corners rounding. Set to 0.0f to have rectangular windows. - float ChildBorderSize; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - float PopupRounding; // Radius of popup window corners rounding. (Note that tooltip windows use WindowRounding) - float PopupBorderSize; // Thickness of border around popup/tooltip windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - ImVec2 FramePadding; // Padding within a framed rectangle (used by most widgets). - float FrameRounding; // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets). - float FrameBorderSize; // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines. - ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label). - ImVec2 CellPadding; // Padding within a table cell - ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! - float IndentSpacing; // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). - float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). - float ScrollbarSize; // Width of the vertical scrollbar, Height of the horizontal scrollbar. - float ScrollbarRounding; // Radius of grab corners for scrollbar. - float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. - float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. - float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. - float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. - float TabBorderSize; // Thickness of border around tabs. - float TabMinWidthForCloseButton; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. - ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. - ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). - ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. - ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. - ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! - float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. - bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). - bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList). - bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). - float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. - float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. - ImVec4 Colors[ImGuiCol_COUNT]; - - IMGUI_API ImGuiStyle(); - IMGUI_API void ScaleAllSizes(float scale_factor); -}; - -//----------------------------------------------------------------------------- -// [SECTION] ImGuiIO -//----------------------------------------------------------------------------- -// Communicate most settings and inputs/outputs to Dear ImGui using this structure. -// Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage. -//----------------------------------------------------------------------------- - -// [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions. -// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and not io.KeysData[key]->DownDuration. -struct ImGuiKeyData -{ - bool Down; // True for if key is down - float DownDuration; // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held) - float DownDurationPrev; // Last frame duration the key has been down - float AnalogValue; // 0.0f..1.0f for gamepad values -}; - -struct ImGuiIO -{ - //------------------------------------------------------------------ - // Configuration // Default value - //------------------------------------------------------------------ - - ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc. - ImGuiBackendFlags BackendFlags; // = 0 // See ImGuiBackendFlags_ enum. Set by backend (imgui_impl_xxx files or custom backend) to communicate features supported by the backend. - ImVec2 DisplaySize; // // Main display size, in pixels (generally == GetMainViewport()->Size). May change every frame. - float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. May change every frame. - float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. - const char* IniFilename; // = "imgui.ini" // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions. - const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). - float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. - float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. - float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. - float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). - float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - void* UserData; // = NULL // Store your own data for retrieval by callbacks. - - ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture. - float FontGlobalScale; // = 1.0f // Global scale all fonts - bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. - ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. - ImVec2 DisplayFramebufferScale; // = (1, 1) // For retina display or other situations where window coordinates are different from framebuffer coordinates. This generally ends up in ImDrawData::FramebufferScale. - - // Miscellaneous options - bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations. - bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. - bool ConfigInputTrickleEventQueue; // = true // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates. - bool ConfigInputTextCursorBlink; // = true // Enable blinking cursor (optional as some users consider it to be distracting). - bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. - bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) - bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. - float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable. - - //------------------------------------------------------------------ - // Platform Functions - // (the imgui_impl_xxxx backend files are setting those up for you) - //------------------------------------------------------------------ - - // Optional: Platform/Renderer backend name (informational only! will be displayed in About Window) + User data for backend/wrappers to store their own stuff. - const char* BackendPlatformName; // = NULL - const char* BackendRendererName; // = NULL - void* BackendPlatformUserData; // = NULL // User data for platform backend - void* BackendRendererUserData; // = NULL // User data for renderer backend - void* BackendLanguageUserData; // = NULL // User data for non C++ programming language backend - - // Optional: Access OS clipboard - // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) - const char* (*GetClipboardTextFn)(void* user_data); - void (*SetClipboardTextFn)(void* user_data, const char* text); - void* ClipboardUserData; - - // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) - // (default to use native imm32 api on Windows) - void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - void* ImeWindowHandle; // = NULL // [Obsolete] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. -#else - void* _UnusedPadding; // Unused field to keep data structure the same size. -#endif - - //------------------------------------------------------------------ - // Input - Call before calling NewFrame() - //------------------------------------------------------------------ - - // Input Functions - IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) - IMGUI_API void AddKeyAnalogEvent(ImGuiKey key, bool down, float v); // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend. - IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) - IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change - IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update - IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) - IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input - IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate - IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string - - IMGUI_API void SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode. - IMGUI_API void SetAppAcceptingEvents(bool accepting_events); // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen. - IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually - IMGUI_API void ClearInputKeys(); // [Internal] Release all keys - - //------------------------------------------------------------------ - // Output - Updated by NewFrame() or EndFrame()/Render() - // (when reading from the io.WantCaptureMouse, io.WantCaptureKeyboard flags to dispatch your inputs, it is - // generally easier and more correct to use their state BEFORE calling NewFrame(). See FAQ for details!) - //------------------------------------------------------------------ - - bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). - bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). - bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). - bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. - bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! - bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. - bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. - int MetricsRenderVertices; // Vertices output during last call to Render() - int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 - int MetricsRenderWindows; // Number of visible windows - int MetricsActiveWindows; // Number of active windows - int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts. - ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. - - // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame. - // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent(). -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512. - bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow. -#endif - - //------------------------------------------------------------------ - // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed! - //------------------------------------------------------------------ - - // Main Input State - // (this block used to be written by backend, since 1.87 it is best to NOT write to those directly, call the AddXXX functions above instead) - // (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere) - ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.) - bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. - float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. - float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. - bool KeyCtrl; // Keyboard modifier down: Control - bool KeyShift; // Keyboard modifier down: Shift - bool KeyAlt; // Keyboard modifier down: Alt - bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows - float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). - - // Other state maintained from data above + IO function calls - ImGuiModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() - ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this. - bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. - ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) - ImVec2 MouseClickedPos[5]; // Position at time of clicking - double MouseClickedTime[5]; // Time of last click (used to figure out double-click) - bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0) - bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2) - ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down - ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. - bool MouseReleased[5]; // Mouse button went from Down to !Down - bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. - bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window. - float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) - float MouseDownDurationPrev[5]; // Previous time the mouse button has been down - float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds) - float NavInputsDownDuration[ImGuiNavInput_COUNT]; - float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; - float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. - bool AppFocusLost; // Only modify via AddFocusEvent() - bool AppAcceptingEvents; // Only modify via SetAppAcceptingEvents() - ImS8 BackendUsingLegacyKeyArrays; // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[] - bool BackendUsingLegacyNavInputArray; // 0: using AddKeyAnalogEvent(), 1: writing to legacy io.NavInputs[] directly - ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16() - ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. - - IMGUI_API ImGuiIO(); -}; - -//----------------------------------------------------------------------------- -// [SECTION] Misc data structures -//----------------------------------------------------------------------------- - -// Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used. -// The callback function should return 0 by default. -// Callbacks (follow a flag name and see comments in ImGuiInputTextFlags_ declarations for more details) -// - ImGuiInputTextFlags_CallbackEdit: Callback on buffer edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) -// - ImGuiInputTextFlags_CallbackAlways: Callback on each iteration -// - ImGuiInputTextFlags_CallbackCompletion: Callback on pressing TAB -// - ImGuiInputTextFlags_CallbackHistory: Callback on pressing Up/Down arrows -// - ImGuiInputTextFlags_CallbackCharFilter: Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. -// - ImGuiInputTextFlags_CallbackResize: Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. -struct ImGuiInputTextCallbackData -{ - ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only - ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only - void* UserData; // What user passed to InputText() // Read-only - - // Arguments for the different callback events - // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary. - // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state. - ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0; - ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] - char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! - int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length() - int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 - bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] - int CursorPos; // // Read-write // [Completion,History,Always] - int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection) - int SelectionEnd; // // Read-write // [Completion,History,Always] - - // Helper functions for text manipulation. - // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection. - IMGUI_API ImGuiInputTextCallbackData(); - IMGUI_API void DeleteChars(int pos, int bytes_count); - IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); - void SelectAll() { SelectionStart = 0; SelectionEnd = BufTextLen; } - void ClearSelection() { SelectionStart = SelectionEnd = BufTextLen; } - bool HasSelection() const { return SelectionStart != SelectionEnd; } -}; - -// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). -// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough. -struct ImGuiSizeCallbackData -{ - void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints() - ImVec2 Pos; // Read-only. Window position, for reference. - ImVec2 CurrentSize; // Read-only. Current window size. - ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. -}; - -// Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() -struct ImGuiPayload -{ - // Members - void* Data; // Data (copied and owned by dear imgui) - int DataSize; // Data size - - // [Internal] - ImGuiID SourceId; // Source item id - ImGuiID SourceParentId; // Source parent id (if available) - int DataFrameCount; // Data timestamp - char DataType[32 + 1]; // Data type tag (short user-supplied string, 32 characters max) - bool Preview; // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets) - bool Delivery; // Set when AcceptDragDropPayload() was called and mouse button is released over the target item. - - ImGuiPayload() { Clear(); } - void Clear() { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; } - bool IsDataType(const char* type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; } - bool IsPreview() const { return Preview; } - bool IsDelivery() const { return Delivery; } -}; - -// Sorting specification for one column of a table (sizeof == 12 bytes) -struct ImGuiTableColumnSortSpecs -{ - ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call) - ImS16 ColumnIndex; // Index of the column - ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here) - ImGuiSortDirection SortDirection : 8; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending (you can use this or SortSign, whichever is more convenient for your sort function) - - ImGuiTableColumnSortSpecs() { memset(this, 0, sizeof(*this)); } -}; - -// Sorting specifications for a table (often handling sort specs for a single column, occasionally more) -// Obtained by calling TableGetSortSpecs(). -// When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time. -// Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame! -struct ImGuiTableSortSpecs -{ - const ImGuiTableColumnSortSpecs* Specs; // Pointer to sort spec array. - int SpecsCount; // Sort spec count. Most often 1. May be > 1 when ImGuiTableFlags_SortMulti is enabled. May be == 0 when ImGuiTableFlags_SortTristate is enabled. - bool SpecsDirty; // Set to true when specs have changed since last time! Use this to sort again, then clear the flag. - - ImGuiTableSortSpecs() { memset(this, 0, sizeof(*this)); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) -//----------------------------------------------------------------------------- - -// Helper: Unicode defines -#define IM_UNICODE_CODEPOINT_INVALID 0xFFFD // Invalid Unicode code point (standard value). -#ifdef IMGUI_USE_WCHAR32 -#define IM_UNICODE_CODEPOINT_MAX 0x10FFFF // Maximum Unicode code point supported by this build. -#else -#define IM_UNICODE_CODEPOINT_MAX 0xFFFF // Maximum Unicode code point supported by this build. -#endif - -// Helper: Execute a block of code at maximum once a frame. Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame. -// Usage: static ImGuiOnceUponAFrame oaf; if (oaf) ImGui::Text("This will be called only once per frame"); -struct ImGuiOnceUponAFrame -{ - ImGuiOnceUponAFrame() { RefFrame = -1; } - mutable int RefFrame; - operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; } -}; - -// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" -struct ImGuiTextFilter -{ - IMGUI_API ImGuiTextFilter(const char* default_filter = ""); - IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build - IMGUI_API bool PassFilter(const char* text, const char* text_end = NULL) const; - IMGUI_API void Build(); - void Clear() { InputBuf[0] = 0; Build(); } - bool IsActive() const { return !Filters.empty(); } - - // [Internal] - struct ImGuiTextRange - { - const char* b; - const char* e; - - ImGuiTextRange() { b = e = NULL; } - ImGuiTextRange(const char* _b, const char* _e) { b = _b; e = _e; } - bool empty() const { return b == e; } - IMGUI_API void split(char separator, ImVector* out) const; - }; - char InputBuf[256]; - ImVectorFilters; - int CountGrep; -}; - -// Helper: Growable text buffer for logging/accumulating text -// (this could be called 'ImGuiTextBuilder' / 'ImGuiStringBuilder') -struct ImGuiTextBuffer -{ - ImVector Buf; - IMGUI_API static char EmptyString[1]; - - ImGuiTextBuffer() { } - inline char operator[](int i) const { IM_ASSERT(Buf.Data != NULL); return Buf.Data[i]; } - const char* begin() const { return Buf.Data ? &Buf.front() : EmptyString; } - const char* end() const { return Buf.Data ? &Buf.back() : EmptyString; } // Buf is zero-terminated, so end() will point on the zero-terminator - int size() const { return Buf.Size ? Buf.Size - 1 : 0; } - bool empty() const { return Buf.Size <= 1; } - void clear() { Buf.clear(); } - void reserve(int capacity) { Buf.reserve(capacity); } - const char* c_str() const { return Buf.Data ? Buf.Data : EmptyString; } - IMGUI_API void append(const char* str, const char* str_end = NULL); - IMGUI_API void appendf(const char* fmt, ...) IM_FMTARGS(2); - IMGUI_API void appendfv(const char* fmt, va_list args) IM_FMTLIST(2); -}; - -// Helper: Key->Value storage -// Typically you don't have to worry about this since a storage is held within each Window. -// We use it to e.g. store collapse state for a tree (Int 0/1) -// This is optimized for efficient lookup (dichotomy into a contiguous buffer) and rare insertion (typically tied to user interactions aka max once a frame) -// You can use it as custom user storage for temporary values. Declare your own storage if, for example: -// - You want to manipulate the open/close state of a particular sub-tree in your interface (tree node uses Int 0/1 to store their state). -// - You want to store custom debug data easily without adding or editing structures in your code (probably not efficient, but convenient) -// Types are NOT stored, so it is up to you to make sure your Key don't collide with different types. -struct ImGuiStorage -{ - // [Internal] - struct ImGuiStoragePair - { - ImGuiID key; - union { int val_i; float val_f; void* val_p; }; - ImGuiStoragePair(ImGuiID _key, int _val_i) { key = _key; val_i = _val_i; } - ImGuiStoragePair(ImGuiID _key, float _val_f) { key = _key; val_f = _val_f; } - ImGuiStoragePair(ImGuiID _key, void* _val_p) { key = _key; val_p = _val_p; } - }; - - ImVector Data; - - // - Get***() functions find pair, never add/allocate. Pairs are sorted so a query is O(log N) - // - Set***() functions find pair, insertion on demand if missing. - // - Sorted insertion is costly, paid once. A typical frame shouldn't need to insert any new pair. - void Clear() { Data.clear(); } - IMGUI_API int GetInt(ImGuiID key, int default_val = 0) const; - IMGUI_API void SetInt(ImGuiID key, int val); - IMGUI_API bool GetBool(ImGuiID key, bool default_val = false) const; - IMGUI_API void SetBool(ImGuiID key, bool val); - IMGUI_API float GetFloat(ImGuiID key, float default_val = 0.0f) const; - IMGUI_API void SetFloat(ImGuiID key, float val); - IMGUI_API void* GetVoidPtr(ImGuiID key) const; // default_val is NULL - IMGUI_API void SetVoidPtr(ImGuiID key, void* val); - - // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set. - // - References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. - // - A typical use case where this is convenient for quick hacking (e.g. add storage during a live Edit&Continue session if you can't modify existing struct) - // float* pvar = ImGui::GetFloatRef(key); ImGui::SliderFloat("var", pvar, 0, 100.0f); some_var += *pvar; - IMGUI_API int* GetIntRef(ImGuiID key, int default_val = 0); - IMGUI_API bool* GetBoolRef(ImGuiID key, bool default_val = false); - IMGUI_API float* GetFloatRef(ImGuiID key, float default_val = 0.0f); - IMGUI_API void** GetVoidPtrRef(ImGuiID key, void* default_val = NULL); - - // Use on your own storage if you know only integer are being stored (open/close all tree nodes) - IMGUI_API void SetAllInt(int val); - - // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. - IMGUI_API void BuildSortByKey(); -}; - -// Helper: Manually clip large list of items. -// If you have lots evenly spaced items and you have a random access to the list, you can perform coarse -// clipping based on visibility to only submit items that are in view. -// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. -// (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally -// fetching/submitting your own data incurs additional cost. Coarse clipping using ImGuiListClipper allows you to easily -// scale using lists with tens of thousands of items without a problem) -// Usage: -// ImGuiListClipper clipper; -// clipper.Begin(1000); // We have 1000 elements, evenly spaced. -// while (clipper.Step()) -// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) -// ImGui::Text("line number %d", i); -// Generally what happens is: -// - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not. -// - User code submit that one element. -// - Clipper can measure the height of the first element -// - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element. -// - User code submit visible elements. -// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc. -struct ImGuiListClipper -{ - int DisplayStart; // First item to display, updated by each call to Step() - int DisplayEnd; // End of items to display (exclusive) - int ItemsCount; // [Internal] Number of items - float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it - float StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed - void* TempData; // [Internal] Internal data - - // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step) - // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). - IMGUI_API ImGuiListClipper(); - IMGUI_API ~ImGuiListClipper(); - IMGUI_API void Begin(int items_count, float items_height = -1.0f); - IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. - IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. - - // Call ForceDisplayRangeByIndices() before first call to Step() if you need a range of items to be displayed regardless of visibility. - IMGUI_API void ForceDisplayRangeByIndices(int item_min, int item_max); // item_max is exclusive e.g. use (42, 42+1) to make item 42 always visible BUT due to alignment/padding of certain items it is likely that an extra item may be included on either end of the display range. - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] -#endif -}; - -// Helpers macros to generate 32-bit encoded colors -// User can declare their own format by #defining the 5 _SHIFT/_MASK macros in their imconfig file. -#ifndef IM_COL32_R_SHIFT -#ifdef IMGUI_USE_BGRA_PACKED_COLOR -#define IM_COL32_R_SHIFT 16 -#define IM_COL32_G_SHIFT 8 -#define IM_COL32_B_SHIFT 0 -#define IM_COL32_A_SHIFT 24 -#define IM_COL32_A_MASK 0xFF000000 -#else -#define IM_COL32_R_SHIFT 0 -#define IM_COL32_G_SHIFT 8 -#define IM_COL32_B_SHIFT 16 -#define IM_COL32_A_SHIFT 24 -#define IM_COL32_A_MASK 0xFF000000 -#endif -#endif -#define IM_COL32(R,G,B,A) (((ImU32)(A)<> IM_COL32_R_SHIFT) & 0xFF) * sc; Value.y = (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * sc; Value.z = (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * sc; Value.w = (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * sc; } - inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } - inline operator ImVec4() const { return Value; } - - // FIXME-OBSOLETE: May need to obsolete/cleanup those helpers. - inline void SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; } - static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r, g, b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r, g, b, a); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) -// Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. -//----------------------------------------------------------------------------- - -// The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoBakedLines to disable baking. -#ifndef IM_DRAWLIST_TEX_LINES_WIDTH_MAX -#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX (63) -#endif - -// ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h] -// NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering, -// you can poke into the draw list for that! Draw callback may be useful for example to: -// A) Change your GPU render state, -// B) render a complex 3D scene inside a UI element without an intermediate texture/render target, etc. -// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) { cmd.UserCallback(parent_list, cmd); } else { RenderTriangles() }' -// If you want to override the signature of ImDrawCallback, you can simply use e.g. '#define ImDrawCallback MyDrawCallback' (in imconfig.h) + update rendering backend accordingly. -#ifndef ImDrawCallback -typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); -#endif - -// Special Draw callback value to request renderer backend to reset the graphics/render state. -// The renderer backend needs to handle this special value, otherwise it will crash trying to call a function at this address. -// This is useful for example if you submitted callbacks which you know have altered the render state and you want it to be restored. -// It is not done by default because they are many perfectly useful way of altering render state for imgui contents (e.g. changing shader/blending settings before an Image call). -#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-1) - -// Typically, 1 command = 1 GPU draw call (unless command is a callback) -// - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled, -// this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices. -// Backends made for <1.71. will typically ignore the VtxOffset fields. -// - The ClipRect/TextureId/VtxOffset fields must be contiguous as we memcmp() them together (this is asserted for). -struct ImDrawCmd -{ - ImVec4 ClipRect; // 4*4 // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates - ImTextureID TextureId; // 4-8 // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. - unsigned int VtxOffset; // 4 // Start offset in vertex buffer. ImGuiBackendFlags_RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices. - unsigned int IdxOffset; // 4 // Start offset in index buffer. - unsigned int ElemCount; // 4 // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. - ImDrawCallback UserCallback; // 4-8 // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. - void* UserCallbackData; // 4-8 // The draw callback code can access this. - - ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed - - // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature) - inline ImTextureID GetTexID() const { return TextureId; } -}; - -// Vertex layout -#ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT -struct ImDrawVert -{ - ImVec2 pos; - ImVec2 uv; - ImU32 col; -}; -#else -// You can override the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h -// The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine. -// The type has to be described within the macro (you can either declare the struct or use a typedef). This is because ImVec2/ImU32 are likely not declared a the time you'd want to set your type up. -// NOTE: IMGUI DOESN'T CLEAR THE STRUCTURE AND DOESN'T CALL A CONSTRUCTOR SO ANY CUSTOM FIELD WILL BE UNINITIALIZED. IF YOU ADD EXTRA FIELDS (SUCH AS A 'Z' COORDINATES) YOU WILL NEED TO CLEAR THEM DURING RENDER OR TO IGNORE THEM. -IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; -#endif - -// [Internal] For use by ImDrawList -struct ImDrawCmdHeader -{ - ImVec4 ClipRect; - ImTextureID TextureId; - unsigned int VtxOffset; -}; - -// [Internal] For use by ImDrawListSplitter -struct ImDrawChannel -{ - ImVector _CmdBuffer; - ImVector _IdxBuffer; -}; - - -// Split/Merge functions are used to split the draw list into different layers which can be drawn into out of order. -// This is used by the Columns/Tables API, so items of each column can be batched together in a same draw call. -struct ImDrawListSplitter -{ - int _Current; // Current channel number (0) - int _Count; // Number of active channels (1+) - ImVector _Channels; // Draw channels (not resized down so _Count might be < Channels.Size) - - inline ImDrawListSplitter() { memset(this, 0, sizeof(*this)); } - inline ~ImDrawListSplitter() { ClearFreeMemory(); } - inline void Clear() { _Current = 0; _Count = 1; } // Do not clear Channels[] so our allocations are reused next frame - IMGUI_API void ClearFreeMemory(); - IMGUI_API void Split(ImDrawList* draw_list, int count); - IMGUI_API void Merge(ImDrawList* draw_list); - IMGUI_API void SetCurrentChannel(ImDrawList* draw_list, int channel_idx); -}; - -// Flags for ImDrawList functions -// (Legacy: bit 0 must always correspond to ImDrawFlags_Closed to be backward compatible with old API using a bool. Bits 1..3 must be unused) -enum ImDrawFlags_ -{ - ImDrawFlags_None = 0, - ImDrawFlags_Closed = 1 << 0, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason) - ImDrawFlags_RoundCornersTopLeft = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01. - ImDrawFlags_RoundCornersTopRight = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02. - ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04. - ImDrawFlags_RoundCornersBottomRight = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08. - ImDrawFlags_RoundCornersNone = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag! - ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight, - ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, - ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft, - ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight, - ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, - ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified. - ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone -}; - -// Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. -// It is however possible to temporarily alter flags between calls to ImDrawList:: functions. -enum ImDrawListFlags_ -{ - ImDrawListFlags_None = 0, - ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles) - ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). - ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). - ImDrawListFlags_AllowVtxOffset = 1 << 3 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. -}; - -// Draw command list -// This is the low-level list of polygons that ImGui:: functions are filling. At the end of the frame, -// all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. -// Each dear imgui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to -// access the current window draw list and draw custom primitives. -// You can interleave normal ImGui:: calls and adding primitives to the current draw list. -// In single viewport mode, top-left is == GetMainViewport()->Pos (generally 0,0), bottom-right is == GetMainViewport()->Pos+Size (generally io.DisplaySize). -// You are totally free to apply whatever transformation matrix to want to the data (depending on the use of the transformation you may want to apply it to ClipRect as well!) -// Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects. -struct ImDrawList -{ - // This is what you have to render - ImVector CmdBuffer; // Draw commands. Typically 1 command = 1 GPU draw call, unless the command is a callback. - ImVector IdxBuffer; // Index buffer. Each command consume ImDrawCmd::ElemCount of those - ImVector VtxBuffer; // Vertex buffer. - ImDrawListFlags Flags; // Flags, you may poke into these to adjust anti-aliasing settings per-primitive. - - // [Internal, used while building lists] - unsigned int _VtxCurrentIdx; // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0. - const ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context) - const char* _OwnerName; // Pointer to owner window's name for debugging - ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) - ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) - ImVector _ClipRectStack; // [Internal] - ImVector _TextureIdStack; // [Internal] - ImVector _Path; // [Internal] current path building - ImDrawCmdHeader _CmdHeader; // [Internal] template of active commands. Fields should match those of CmdBuffer.back(). - ImDrawListSplitter _Splitter; // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!) - float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content - - // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui) - ImDrawList(const ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; } - - ~ImDrawList() { _ClearFreeMemory(); } - IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) - IMGUI_API void PushClipRectFullScreen(); - IMGUI_API void PopClipRect(); - IMGUI_API void PushTextureID(ImTextureID texture_id); - IMGUI_API void PopTextureID(); - inline ImVec2 GetClipRectMin() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.x, cr.y); } - inline ImVec2 GetClipRectMax() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.z, cr.w); } - - // Primitives - // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. - // - For rectangular primitives, "p_min" and "p_max" represent the upper-left and lower-right corners. - // - For circle primitives, use "num_segments == 0" to automatically calculate tessellation (preferred). - // In older versions (until Dear ImGui 1.77) the AddCircle functions defaulted to num_segments == 12. - // In future versions we will use textures to provide cheaper and higher-quality circles. - // Use AddNgon() and AddNgonFilled() functions if you need to guaranteed a specific number of sides. - IMGUI_API void AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); // a: upper-left, b: lower-right (== upper-left + size) - IMGUI_API void AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size) - IMGUI_API void AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); - IMGUI_API void AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col); - IMGUI_API void AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col); - IMGUI_API void AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments = 0, float thickness = 1.0f); - IMGUI_API void AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 0); - IMGUI_API void AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f); - IMGUI_API void AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments); - IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); - IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); - IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); - IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); - IMGUI_API void AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) - IMGUI_API void AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quadratic Bezier (3 control points) - - // Image primitives - // - Read FAQ to understand what ImTextureID is. - // - "p_min" and "p_max" represent the upper-left and lower-right corners of the rectangle. - // - "uv_min" and "uv_max" represent the normalized texture coordinates to use for those corners. Using (0,0)->(1,1) texture coordinates will generally display the entire texture. - IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min = ImVec2(0, 0), const ImVec2& uv_max = ImVec2(1, 1), ImU32 col = IM_COL32_WHITE); - IMGUI_API void AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1 = ImVec2(0, 0), const ImVec2& uv2 = ImVec2(1, 0), const ImVec2& uv3 = ImVec2(1, 1), const ImVec2& uv4 = ImVec2(0, 1), ImU32 col = IM_COL32_WHITE); - IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0); - - // Stateful path API, add points then finish with PathFillConvex() or PathStroke() - // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. - inline void PathClear() { _Path.Size = 0; } - inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); } - inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); } - inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } - inline void PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; } - IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0); - IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle - IMGUI_API void PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) - IMGUI_API void PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points) - IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawFlags flags = 0); - - // Advanced - IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. - IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible - IMGUI_API ImDrawList* CloneOutput() const; // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer. - - // Advanced: Channels - // - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives) - // - Use to minimize draw calls (e.g. if going back-and-forth between multiple clipping rectangles, prefer to append into separate channels then merge at the end) - // - FIXME-OBSOLETE: This API shouldn't have been in ImDrawList in the first place! - // Prefer using your own persistent instance of ImDrawListSplitter as you can stack them. - // Using the ImDrawList::ChannelsXXXX you cannot stack a split over another. - inline void ChannelsSplit(int count) { _Splitter.Split(this, count); } - inline void ChannelsMerge() { _Splitter.Merge(this); } - inline void ChannelsSetCurrent(int n) { _Splitter.SetCurrentChannel(this, n); } - - // Advanced: Primitives allocations - // - We render triangles (three vertices) - // - All primitives needs to be reserved via PrimReserve() beforehand. - IMGUI_API void PrimReserve(int idx_count, int vtx_count); - IMGUI_API void PrimUnreserve(int idx_count, int vtx_count); - IMGUI_API void PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col); // Axis aligned rectangle (composed of two triangles) - IMGUI_API void PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col); - IMGUI_API void PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col); - inline void PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; } - inline void PrimWriteIdx(ImDrawIdx idx) { *_IdxWritePtr = idx; _IdxWritePtr++; } - inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - inline void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } // OBSOLETED in 1.80 (Jan 2021) - inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } // OBSOLETED in 1.80 (Jan 2021) -#endif - - // [Internal helpers] - IMGUI_API void _ResetForNewFrame(); - IMGUI_API void _ClearFreeMemory(); - IMGUI_API void _PopUnusedDrawCmd(); - IMGUI_API void _TryMergeDrawCmds(); - IMGUI_API void _OnChangedClipRect(); - IMGUI_API void _OnChangedTextureID(); - IMGUI_API void _OnChangedVtxOffset(); - IMGUI_API int _CalcCircleAutoSegmentCount(float radius) const; - IMGUI_API void _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step); - IMGUI_API void _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments); -}; - -// All draw data to render a Dear ImGui frame -// (NB: the style and the naming convention here is a little inconsistent, we currently preserve them for backward compatibility purpose, -// as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList) -struct ImDrawData -{ - bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - int CmdListsCount; // Number of ImDrawList* to render - int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size - int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size - ImDrawList** CmdLists; // Array of ImDrawList* to render. The ImDrawList are owned by ImGuiContext and only pointed to from here. - ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications) - ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) - ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. - - // Functions - ImDrawData() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); } // The ImDrawList are owned by ImGuiContext! - IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! - IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. -}; - -//----------------------------------------------------------------------------- -// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont) -//----------------------------------------------------------------------------- - -struct ImFontConfig -{ - void* FontData; // // TTF/OTF data - int FontDataSize; // // TTF/OTF data size - bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). - int FontNo; // 0 // Index of font within TTF/OTF file - float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). - int OversampleH; // 3 // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal so you can reduce this to 2 to save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. - int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. This is not really useful as we don't use sub-pixel positions on the Y axis. - bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. - ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now. - ImVec2 GlyphOffset; // 0, 0 // Offset all glyphs from this font input. - const ImWchar* GlyphRanges; // NULL // Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. - float GlyphMinAdvanceX; // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font - float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs - bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. - unsigned int FontBuilderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure. - float RasterizerMultiply; // 1.0f // Brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. - ImWchar EllipsisChar; // -1 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. - - // [Internal] - char Name[40]; // Name (strictly to ease debugging) - ImFont* DstFont; - - IMGUI_API ImFontConfig(); -}; - -// Hold rendering data for one glyph. -// (Note: some language parsers may fail to convert the 31+1 bitfield members, in this case maybe drop store a single u32 or we can rework this) -struct ImFontGlyph -{ - unsigned int Colored : 1; // Flag to indicate glyph is colored and should generally ignore tinting (make it usable with no shift on little-endian as this is used in loops) - unsigned int Visible : 1; // Flag to indicate glyph has no visible pixels (e.g. space). Allow early out when rendering. - unsigned int Codepoint : 30; // 0x0000..0x10FFFF - float AdvanceX; // Distance to next character (= data from font + ImFontConfig::GlyphExtraSpacing.x baked in) - float X0, Y0, X1, Y1; // Glyph corners - float U0, V0, U1, V1; // Texture coordinates -}; - -// Helper to build glyph ranges from text/string data. Feed your application strings/characters to it then call BuildRanges(). -// This is essentially a tightly packed of vector of 64k booleans = 8KB storage. -struct ImFontGlyphRangesBuilder -{ - ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) - - ImFontGlyphRangesBuilder() { Clear(); } - inline void Clear() { int size_in_bytes = (IM_UNICODE_CODEPOINT_MAX + 1) / 8; UsedChars.resize(size_in_bytes / (int)sizeof(ImU32)); memset(UsedChars.Data, 0, (size_t)size_in_bytes); } - inline bool GetBit(size_t n) const { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); return (UsedChars[off] & mask) != 0; } // Get bit n in the array - inline void SetBit(size_t n) { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); UsedChars[off] |= mask; } // Set bit n in the array - inline void AddChar(ImWchar c) { SetBit(c); } // Add character - IMGUI_API void AddText(const char* text, const char* text_end = NULL); // Add string (each character of the UTF-8 string are added) - IMGUI_API void AddRanges(const ImWchar* ranges); // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext - IMGUI_API void BuildRanges(ImVector* out_ranges); // Output new ranges -}; - -// See ImFontAtlas::AddCustomRectXXX functions. -struct ImFontAtlasCustomRect -{ - unsigned short Width, Height; // Input // Desired rectangle dimension - unsigned short X, Y; // Output // Packed position in Atlas - unsigned int GlyphID; // Input // For custom font glyphs only (ID < 0x110000) - float GlyphAdvanceX; // Input // For custom font glyphs only: glyph xadvance - ImVec2 GlyphOffset; // Input // For custom font glyphs only: glyph display offset - ImFont* Font; // Input // For custom font glyphs only: target font - ImFontAtlasCustomRect() { Width = Height = 0; X = Y = 0xFFFF; GlyphID = 0; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0, 0); Font = NULL; } - bool IsPacked() const { return X != 0xFFFF; } -}; - -// Flags for ImFontAtlas build -enum ImFontAtlasFlags_ -{ - ImFontAtlasFlags_None = 0, - ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two - ImFontAtlasFlags_NoMouseCursors = 1 << 1, // Don't build software mouse cursors into the atlas (save a little texture memory) - ImFontAtlasFlags_NoBakedLines = 1 << 2 // Don't build thick line textures into the atlas (save a little texture memory, allow support for point/nearest filtering). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU). -}; - -// Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: -// - One or more fonts. -// - Custom graphics data needed to render the shapes needed by Dear ImGui. -// - Mouse cursor shapes for software cursor rendering (unless setting 'Flags |= ImFontAtlasFlags_NoMouseCursors' in the font atlas). -// It is the user-code responsibility to setup/build the atlas, then upload the pixel data into a texture accessible by your graphics api. -// - Optionally, call any of the AddFont*** functions. If you don't call any, the default font embedded in the code will be loaded for you. -// - Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data. -// - Upload the pixels data into a texture within your graphics system (see imgui_impl_xxxx.cpp examples) -// - Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture in a format natural to your graphics API. -// This value will be passed back to you during rendering to identify the texture. Read FAQ entry about ImTextureID for more details. -// Common pitfalls: -// - If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the -// atlas is build (when calling GetTexData*** or Build()). We only copy the pointer, not the data. -// - Important: By default, AddFontFromMemoryTTF() takes ownership of the data. Even though we are not writing to it, we will free the pointer on destruction. -// You can set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed, -// - Even though many functions are suffixed with "TTF", OTF data is supported just as well. -// - This is an old API and it is currently awkward for those and and various other reasons! We will address them in the future! -struct ImFontAtlas -{ - IMGUI_API ImFontAtlas(); - IMGUI_API ~ImFontAtlas(); - IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg); - IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); - IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); - IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed. - IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. - IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. - IMGUI_API void ClearInputData(); // Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. - IMGUI_API void ClearTexData(); // Clear output texture data (CPU side). Saves RAM once the texture has been copied to graphics memory. - IMGUI_API void ClearFonts(); // Clear output font data (glyphs storage, UV coordinates). - IMGUI_API void Clear(); // Clear all input and output. - - // Build atlas, retrieve pixel data. - // User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID(). - // The pitch is always = Width * BytesPerPixels (1 or 4) - // Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into - // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste. - IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. - IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel - IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel - bool IsBuilt() const { return Fonts.Size > 0 && TexReady; } // Bit ambiguous: used to detect when user didn't built texture but effectively we should check TexID != 0 except that would be backend dependent... - void SetTexID(ImTextureID id) { TexID = id; } - - //------------------------------------------- - // Glyph Ranges - //------------------------------------------- - - // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list) - // NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details. - // NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data. - IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin - IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters - IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs - IMGUI_API const ImWchar* GetGlyphRangesChineseFull(); // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs - IMGUI_API const ImWchar* GetGlyphRangesChineseSimplifiedCommon();// Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese - IMGUI_API const ImWchar* GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters - IMGUI_API const ImWchar* GetGlyphRangesThai(); // Default + Thai characters - IMGUI_API const ImWchar* GetGlyphRangesVietnamese(); // Default + Vietnamese characters - - //------------------------------------------- - // [BETA] Custom Rectangles/Glyphs API - //------------------------------------------- - - // You can request arbitrary rectangles to be packed into the atlas, for your own purposes. - // - After calling Build(), you can query the rectangle position and render your pixels. - // - If you render colored output, set 'atlas->TexPixelsUseColors = true' as this may help some backends decide of prefered texture format. - // - You can also request your rectangles to be mapped as font glyph (given a font + Unicode point), - // so you can render e.g. custom colorful icons and use them as regular glyphs. - // - Read docs/FONTS.md for more details about using colorful icons. - // - Note: this API may be redesigned later in order to support multi-monitor varying DPI settings. - IMGUI_API int AddCustomRectRegular(int width, int height); - IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0, 0)); - ImFontAtlasCustomRect* GetCustomRectByIndex(int index) { IM_ASSERT(index >= 0); return &CustomRects[index]; } - - // [Internal] - IMGUI_API void CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const; - IMGUI_API bool GetMouseCursorTexData(ImGuiMouseCursor cursor, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]); - - //------------------------------------------- - // Members - //------------------------------------------- - - ImFontAtlasFlags Flags; // Build flags (see ImFontAtlasFlags_) - ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. - int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. - int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false). - bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. - - // [Internal] - // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. - bool TexReady; // Set when texture was built matching current font input - bool TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format. - unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight - unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 - int TexWidth; // Texture width calculated during Build(). - int TexHeight; // Texture height calculated during Build(). - ImVec2 TexUvScale; // = (1.0f/TexWidth, 1.0f/TexHeight) - ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel - ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. - ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. - ImVector ConfigData; // Configuration data - ImVec4 TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1]; // UVs for baked anti-aliased lines - - // [Internal] Font builder - const ImFontBuilderIO* FontBuilderIO; // Opaque interface to a font builder (default to stb_truetype, can be changed to use FreeType by defining IMGUI_ENABLE_FREETYPE). - unsigned int FontBuilderFlags; // Shared flags (for all fonts) for custom font builder. THIS IS BUILD IMPLEMENTATION DEPENDENT. Per-font override is also available in ImFontConfig. - - // [Internal] Packing data - int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors - int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines - - // [Obsolete] - //typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ - //typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ -}; - -// Font runtime data and rendering -// ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32(). -struct ImFont -{ - // Members: Hot ~20/24 bytes (for CalcTextSize) - ImVector IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this this info, and are often bottleneck in large UI). - float FallbackAdvanceX; // 4 // out // = FallbackGlyph->AdvanceX - float FontSize; // 4 // in // // Height of characters/line, set during loading (don't change after loading) - - // Members: Hot ~28/40 bytes (for CalcTextSize + render loop) - ImVector IndexLookup; // 12-16 // out // // Sparse. Index glyphs by Unicode code-point. - ImVector Glyphs; // 12-16 // out // // All glyphs. - const ImFontGlyph* FallbackGlyph; // 4-8 // out // = FindGlyph(FontFallbackChar) - - // Members: Cold ~32/40 bytes - ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into - const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData - short ConfigDataCount; // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. - ImWchar FallbackChar; // 2 // out // = FFFD/'?' // Character used if a glyph isn't found. - ImWchar EllipsisChar; // 2 // out // = '...' // Character used for ellipsis rendering. - ImWchar DotChar; // 2 // out // = '.' // Character used for ellipsis rendering (if a single '...' character isn't found) - bool DirtyLookupTables; // 1 // out // - float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() - float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] - int MetricsTotalSurface;// 4 // out // // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) - ImU8 Used4kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/4096/8]; // 2 bytes if ImWchar=ImWchar16, 34 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints. - - // Methods - IMGUI_API ImFont(); - IMGUI_API ~ImFont(); - IMGUI_API const ImFontGlyph*FindGlyph(ImWchar c) const; - IMGUI_API const ImFontGlyph*FindGlyphNoFallback(ImWchar c) const; - float GetCharAdvance(ImWchar c) const { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; } - bool IsLoaded() const { return ContainerAtlas != NULL; } - const char* GetDebugName() const { return ConfigData ? ConfigData->Name : ""; } - - // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. - // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. - IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8 - IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const; - IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const; - IMGUI_API void RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; - - // [Internal] Don't use! - IMGUI_API void BuildLookupTable(); - IMGUI_API void ClearOutputData(); - IMGUI_API void GrowIndex(int new_size); - IMGUI_API void AddGlyph(const ImFontConfig* src_cfg, ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); - IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. - IMGUI_API void SetGlyphVisible(ImWchar c, bool visible); - IMGUI_API bool IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last); -}; - -//----------------------------------------------------------------------------- -// [SECTION] Viewports -//----------------------------------------------------------------------------- - -// Flags stored in ImGuiViewport::Flags, giving indications to the platform backends. -enum ImGuiViewportFlags_ -{ - ImGuiViewportFlags_None = 0, - ImGuiViewportFlags_IsPlatformWindow = 1 << 0, // Represent a Platform Window - ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, // Represent a Platform Monitor (unused yet) - ImGuiViewportFlags_OwnedByApp = 1 << 2 // Platform Window: is created/managed by the application (rather than a dear imgui backend) -}; - -// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. -// - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports. -// - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. -// - About Main Area vs Work Area: -// - Main Area = entire viewport. -// - Work Area = entire viewport minus sections used by main menu bars (for platform windows), or by task bar (for platform monitor). -// - Windows are generally trying to stay within the Work Area of their host viewport. -struct ImGuiViewport -{ - ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ - ImVec2 Pos; // Main Area: Position of the viewport (Dear ImGui coordinates are the same as OS desktop/native coordinates) - ImVec2 Size; // Main Area: Size of the viewport. - ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos) - ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size) - - // Platform/Backend Dependent Data - void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (under Win32 this is expected to be a HWND, unused for other platforms) - - ImGuiViewport() { memset(this, 0, sizeof(*this)); } - - // Helpers - ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } - ImVec2 GetWorkCenter() const { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Platform Dependent Interfaces -//----------------------------------------------------------------------------- - -// (Optional) Support for IME (Input Method Editor) via the io.SetPlatformImeDataFn() function. -struct ImGuiPlatformImeData -{ - bool WantVisible; // A widget wants the IME to be visible - ImVec2 InputPos; // Position of the input cursor - float InputLineHeight; // Line height - - ImGuiPlatformImeData() { memset(this, 0, sizeof(*this)); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Obsolete functions and types -// (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) -// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead. -//----------------------------------------------------------------------------- - -namespace ImGui -{ -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key] -#else - static inline int GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; } -#endif -} - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -namespace ImGui -{ - // OBSOLETED in 1.88 (from May 2022) - static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. - static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. - // OBSOLETED in 1.86 (from November 2021) - IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Calculate coarse clipping for large list of evenly sized items. Prefer using ImGuiListClipper. - // OBSOLETED in 1.85 (from August 2021) - static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } - // OBSOLETED in 1.81 (from February 2021) - IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // Helper to calculate size from items_count and height_in_items - static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); } - static inline void ListBoxFooter() { EndListBox(); } - // OBSOLETED in 1.79 (from August 2020) - static inline void OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1) { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry! - // OBSOLETED in 1.78 (from June 2020) - // Old drag/sliders functions that took a 'float power = 1.0' argument instead of flags. - // For shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. - IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power); - IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power); - static inline bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); } - static inline bool DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); } - static inline bool DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); } - static inline bool DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); } - IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power); - IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format, float power); - static inline bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power) { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); } - static inline bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); } - static inline bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); } - static inline bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); } - // OBSOLETED in 1.77 (from June 2020) - static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } - - // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) - //static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); } // OBSOLETED in 1.72 (from July 2019) - //static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } // OBSOLETED in 1.71 (from June 2019) - //static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } // OBSOLETED in 1.70 (from May 2019) - //static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019) - //static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018) - //static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018) - //static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018) - //static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) - //static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) -} - -// OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect() -typedef ImDrawFlags ImDrawCornerFlags; -enum ImDrawCornerFlags_ -{ - ImDrawCornerFlags_None = ImDrawFlags_RoundCornersNone, // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit - ImDrawCornerFlags_TopLeft = ImDrawFlags_RoundCornersTopLeft, // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally). - ImDrawCornerFlags_TopRight = ImDrawFlags_RoundCornersTopRight, // Was == 0x02 (1 << 1) prior to 1.82. - ImDrawCornerFlags_BotLeft = ImDrawFlags_RoundCornersBottomLeft, // Was == 0x04 (1 << 2) prior to 1.82. - ImDrawCornerFlags_BotRight = ImDrawFlags_RoundCornersBottomRight, // Was == 0x08 (1 << 3) prior to 1.82. - ImDrawCornerFlags_All = ImDrawFlags_RoundCornersAll, // Was == 0x0F prior to 1.82 - ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight, - ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight, - ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft, - ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight -}; - -// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022) -typedef int ImGuiKeyModFlags; -enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = ImGuiModFlags_None, ImGuiKeyModFlags_Ctrl = ImGuiModFlags_Ctrl, ImGuiKeyModFlags_Shift = ImGuiModFlags_Shift, ImGuiKeyModFlags_Alt = ImGuiModFlags_Alt, ImGuiKeyModFlags_Super = ImGuiModFlags_Super }; - -#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -// RENAMED IMGUI_DISABLE_METRICS_WINDOW > IMGUI_DISABLE_DEBUG_TOOLS in 1.88 (from June 2022) -#if defined(IMGUI_DISABLE_METRICS_WINDOW) && !defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && !defined(IMGUI_DISABLE_DEBUG_TOOLS) -#define IMGUI_DISABLE_DEBUG_TOOLS -#endif -#if defined(IMGUI_DISABLE_METRICS_WINDOW) && defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) -#error IMGUI_DISABLE_METRICS_WINDOW was renamed to IMGUI_DISABLE_DEBUG_TOOLS, please use new name. -#endif - -//----------------------------------------------------------------------------- - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - -#ifdef _MSC_VER -#pragma warning (pop) -#endif - -// Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h) -#ifdef IMGUI_INCLUDE_IMGUI_USER_H -#include "imgui_user.h" -#endif - -#endif // #ifndef IMGUI_DISABLE diff --git a/libs/zgui/libs/imgui/imgui_demo.cpp b/libs/zgui/libs/imgui/imgui_demo.cpp deleted file mode 100644 index 4a12dd4df..000000000 --- a/libs/zgui/libs/imgui/imgui_demo.cpp +++ /dev/null @@ -1,7969 +0,0 @@ -// dear imgui, v1.88 -// (demo code) - -// Help: -// - Read FAQ at http://dearimgui.org/faq -// - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. -// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. -// Read imgui.cpp for more details, documentation and comments. -// Get the latest version at https://github.com/ocornut/imgui - -// Message to the person tempted to delete this file when integrating Dear ImGui into their codebase: -// Do NOT remove this file from your project! Think again! It is the most useful reference code that you and other -// coders will want to refer to and call. Have the ImGui::ShowDemoWindow() function wired in an always-available -// debug menu of your game/app! Removing this file from your project is hindering access to documentation for everyone -// in your team, likely leading you to poorer usage of the library. -// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow(). -// If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be -// linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty. -// In another situation, whenever you have Dear ImGui available you probably want this to be available for reference. -// Thank you, -// -Your beloved friend, imgui_demo.cpp (which you won't delete) - -// Message to beginner C/C++ programmers about the meaning of the 'static' keyword: -// In this demo code, we frequently use 'static' variables inside functions. A static variable persists across calls, -// so it is essentially like a global variable but declared inside the scope of the function. We do this as a way to -// gather code and data in the same place, to make the demo source code faster to read, faster to write, and smaller -// in size. It also happens to be a convenient way of storing simple UI related information as long as your function -// doesn't need to be reentrant or used in multiple threads. This might be a pattern you will want to use in your code, -// but most of the real data you would be editing is likely going to be stored outside your functions. - -// The Demo code in this file is designed to be easy to copy-and-paste into your application! -// Because of this: -// - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace. -// - We try to declare static variables in the local scope, as close as possible to the code using them. -// - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API. -// - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided -// by imgui_internal.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional -// and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h. -// Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp. - -// Navigating this file: -// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. - -/* - -Index of this file: - -// [SECTION] Forward Declarations, Helpers -// [SECTION] Demo Window / ShowDemoWindow() -// - sub section: ShowDemoWindowWidgets() -// - sub section: ShowDemoWindowLayout() -// - sub section: ShowDemoWindowPopups() -// - sub section: ShowDemoWindowTables() -// - sub section: ShowDemoWindowMisc() -// [SECTION] About Window / ShowAboutWindow() -// [SECTION] Style Editor / ShowStyleEditor() -// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() -// [SECTION] Example App: Debug Console / ShowExampleAppConsole() -// [SECTION] Example App: Debug Log / ShowExampleAppLog() -// [SECTION] Example App: Simple Layout / ShowExampleAppLayout() -// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() -// [SECTION] Example App: Long Text / ShowExampleAppLongText() -// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() -// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize() -// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay() -// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen() -// [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles() -// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() -// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() - -*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "imgui.h" -#ifndef IMGUI_DISABLE - -// System includes -#include // toupper -#include // INT_MIN, INT_MAX -#include // sqrtf, powf, cosf, sinf, floorf, ceilf -#include // vsnprintf, sscanf, printf -#include // NULL, malloc, free, atoi -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else -#include // intptr_t -#endif - -// Visual Studio warnings -#ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). -#endif - -// Clang/GCC warnings with -Weverything -#if defined(__clang__) -#if __has_warning("-Wunknown-warning-option") -#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! -#endif -#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' -#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. -#pragma clang diagnostic ignored "-Wdeprecated-declarations" // warning: 'xx' is deprecated: The POSIX name for this.. // for strdup used in demo code (so user can copy & paste the code) -#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type -#pragma clang diagnostic ignored "-Wformat-security" // warning: format string is not a string literal -#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning: declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. -#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used. -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 -#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. -#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier -#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision -#elif defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind -#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size -#pragma GCC diagnostic ignored "-Wformat-security" // warning: format string is not a string literal (potentially insecure) -#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function -#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value -#pragma GCC diagnostic ignored "-Wmisleading-indentation" // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement // GCC 6.0+ only. See #883 on GitHub. -#endif - -// Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!) -#ifdef _WIN32 -#define IM_NEWLINE "\r\n" -#else -#define IM_NEWLINE "\n" -#endif - -// Helpers -#if defined(_MSC_VER) && !defined(snprintf) -#define snprintf _snprintf -#endif -#if defined(_MSC_VER) && !defined(vsnprintf) -#define vsnprintf _vsnprintf -#endif - -// Format specifiers, printing 64-bit hasn't been decently standardized... -// In a real application you should be using PRId64 and PRIu64 from (non-windows) and on Windows define them yourself. -#ifdef _MSC_VER -#define IM_PRId64 "I64d" -#define IM_PRIu64 "I64u" -#else -#define IM_PRId64 "lld" -#define IM_PRIu64 "llu" -#endif - -// Helpers macros -// We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste, -// but making an exception here as those are largely simplifying code... -// In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo. -#define IM_MIN(A, B) (((A) < (B)) ? (A) : (B)) -#define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B)) -#define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V)) - -// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall -#ifndef IMGUI_CDECL -#ifdef _MSC_VER -#define IMGUI_CDECL __cdecl -#else -#define IMGUI_CDECL -#endif -#endif - -//----------------------------------------------------------------------------- -// [SECTION] Forward Declarations, Helpers -//----------------------------------------------------------------------------- - -#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) - -// Forward Declarations -static void ShowExampleAppDocuments(bool* p_open); -static void ShowExampleAppMainMenuBar(); -static void ShowExampleAppConsole(bool* p_open); -static void ShowExampleAppLog(bool* p_open); -static void ShowExampleAppLayout(bool* p_open); -static void ShowExampleAppPropertyEditor(bool* p_open); -static void ShowExampleAppLongText(bool* p_open); -static void ShowExampleAppAutoResize(bool* p_open); -static void ShowExampleAppConstrainedResize(bool* p_open); -static void ShowExampleAppSimpleOverlay(bool* p_open); -static void ShowExampleAppFullscreen(bool* p_open); -static void ShowExampleAppWindowTitles(bool* p_open); -static void ShowExampleAppCustomRendering(bool* p_open); -static void ShowExampleMenuFile(); - -// Helper to display a little (?) mark which shows a tooltip when hovered. -// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md) -static void HelpMarker(const char* desc) -{ - ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(desc); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } -} - -// Helper to wire demo markers located in code to a interactive browser -typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data); -extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback; -extern void* GImGuiDemoMarkerCallbackUserData; -ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL; -void* GImGuiDemoMarkerCallbackUserData = NULL; -#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0) - -// Helper to display basic user controls. -void ImGui::ShowUserGuide() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGui::BulletText("Double-click on title bar to collapse window."); - ImGui::BulletText( - "Click and drag on lower corner to resize window\n" - "(double-click to auto fit window to its contents)."); - ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); - ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); - ImGui::BulletText("CTRL+Tab to select a window."); - if (io.FontAllowUserScaling) - ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents."); - ImGui::BulletText("While inputing text:\n"); - ImGui::Indent(); - ImGui::BulletText("CTRL+Left/Right to word jump."); - ImGui::BulletText("CTRL+A or double-click to select all."); - ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste."); - ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo."); - ImGui::BulletText("ESCAPE to revert."); - ImGui::Unindent(); - ImGui::BulletText("With keyboard navigation enabled:"); - ImGui::Indent(); - ImGui::BulletText("Arrow keys to navigate."); - ImGui::BulletText("Space to activate a widget."); - ImGui::BulletText("Return to input text into a widget."); - ImGui::BulletText("Escape to deactivate a widget, close popup, exit child window."); - ImGui::BulletText("Alt to jump to the menu layer of a window."); - ImGui::Unindent(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Demo Window / ShowDemoWindow() -//----------------------------------------------------------------------------- -// - ShowDemoWindowWidgets() -// - ShowDemoWindowLayout() -// - ShowDemoWindowPopups() -// - ShowDemoWindowTables() -// - ShowDemoWindowColumns() -// - ShowDemoWindowMisc() -//----------------------------------------------------------------------------- - -// We split the contents of the big ShowDemoWindow() function into smaller functions -// (because the link time of very large functions grow non-linearly) -static void ShowDemoWindowWidgets(); -static void ShowDemoWindowLayout(); -static void ShowDemoWindowPopups(); -static void ShowDemoWindowTables(); -static void ShowDemoWindowColumns(); -static void ShowDemoWindowMisc(); - -// Demonstrate most Dear ImGui features (this is big function!) -// You may execute this function to experiment with the UI and understand what it does. -// You may then search for keywords in the code when you are interested by a specific feature. -void ImGui::ShowDemoWindow(bool* p_open) -{ - // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup - // Most ImGui functions would normally just crash if the context is missing. - IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing dear imgui context. Refer to examples app!"); - - // Examples Apps (accessible from the "Examples" menu) - static bool show_app_main_menu_bar = false; - static bool show_app_documents = false; - - static bool show_app_console = false; - static bool show_app_log = false; - static bool show_app_layout = false; - static bool show_app_property_editor = false; - static bool show_app_long_text = false; - static bool show_app_auto_resize = false; - static bool show_app_constrained_resize = false; - static bool show_app_simple_overlay = false; - static bool show_app_fullscreen = false; - static bool show_app_window_titles = false; - static bool show_app_custom_rendering = false; - - if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); - if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); - - if (show_app_console) ShowExampleAppConsole(&show_app_console); - if (show_app_log) ShowExampleAppLog(&show_app_log); - if (show_app_layout) ShowExampleAppLayout(&show_app_layout); - if (show_app_property_editor) ShowExampleAppPropertyEditor(&show_app_property_editor); - if (show_app_long_text) ShowExampleAppLongText(&show_app_long_text); - if (show_app_auto_resize) ShowExampleAppAutoResize(&show_app_auto_resize); - if (show_app_constrained_resize) ShowExampleAppConstrainedResize(&show_app_constrained_resize); - if (show_app_simple_overlay) ShowExampleAppSimpleOverlay(&show_app_simple_overlay); - if (show_app_fullscreen) ShowExampleAppFullscreen(&show_app_fullscreen); - if (show_app_window_titles) ShowExampleAppWindowTitles(&show_app_window_titles); - if (show_app_custom_rendering) ShowExampleAppCustomRendering(&show_app_custom_rendering); - - // Dear ImGui Apps (accessible from the "Tools" menu) - static bool show_app_metrics = false; - static bool show_app_debug_log = false; - static bool show_app_stack_tool = false; - static bool show_app_about = false; - static bool show_app_style_editor = false; - - if (show_app_metrics) - ImGui::ShowMetricsWindow(&show_app_metrics); - if (show_app_debug_log) - ImGui::ShowDebugLogWindow(&show_app_debug_log); - if (show_app_stack_tool) - ImGui::ShowStackToolWindow(&show_app_stack_tool); - if (show_app_about) - ImGui::ShowAboutWindow(&show_app_about); - if (show_app_style_editor) - { - ImGui::Begin("Dear ImGui Style Editor", &show_app_style_editor); - ImGui::ShowStyleEditor(); - ImGui::End(); - } - - // Demonstrate the various window flags. Typically you would just use the default! - static bool no_titlebar = false; - static bool no_scrollbar = false; - static bool no_menu = false; - static bool no_move = false; - static bool no_resize = false; - static bool no_collapse = false; - static bool no_close = false; - static bool no_nav = false; - static bool no_background = false; - static bool no_bring_to_front = false; - static bool unsaved_document = false; - - ImGuiWindowFlags window_flags = 0; - if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; - if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; - if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; - if (no_move) window_flags |= ImGuiWindowFlags_NoMove; - if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; - if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; - if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; - if (no_background) window_flags |= ImGuiWindowFlags_NoBackground; - if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus; - if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument; - if (no_close) p_open = NULL; // Don't pass our bool* to Begin - - // We specify a default position/size in case there's no data in the .ini file. - // We only do it to make the demo applications a little more welcoming, but typically this isn't required. - const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); - - // Main body of the Demo window starts here. - if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags)) - { - // Early out if the window is collapsed, as an optimization. - ImGui::End(); - return; - } - - // Most "big" widgets share a common width settings by default. See 'Demo->Layout->Widgets Width' for details. - - // e.g. Use 2/3 of the space for widgets and 1/3 for labels (right align) - //ImGui::PushItemWidth(-ImGui::GetWindowWidth() * 0.35f); - - // e.g. Leave a fixed amount of width for labels (by passing a negative value), the rest goes to widgets. - ImGui::PushItemWidth(ImGui::GetFontSize() * -12); - - // Menu Bar - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("Menu")) - { - IMGUI_DEMO_MARKER("Menu/File"); - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Examples")) - { - IMGUI_DEMO_MARKER("Menu/Examples"); - ImGui::MenuItem("Main menu bar", NULL, &show_app_main_menu_bar); - ImGui::MenuItem("Console", NULL, &show_app_console); - ImGui::MenuItem("Log", NULL, &show_app_log); - ImGui::MenuItem("Simple layout", NULL, &show_app_layout); - ImGui::MenuItem("Property editor", NULL, &show_app_property_editor); - ImGui::MenuItem("Long text display", NULL, &show_app_long_text); - ImGui::MenuItem("Auto-resizing window", NULL, &show_app_auto_resize); - ImGui::MenuItem("Constrained-resizing window", NULL, &show_app_constrained_resize); - ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); - ImGui::MenuItem("Fullscreen window", NULL, &show_app_fullscreen); - ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); - ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); - ImGui::MenuItem("Documents", NULL, &show_app_documents); - ImGui::EndMenu(); - } - //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! - if (ImGui::BeginMenu("Tools")) - { - IMGUI_DEMO_MARKER("Menu/Tools"); -#ifndef IMGUI_DISABLE_DEBUG_TOOLS - const bool has_debug_tools = true; -#else - const bool has_debug_tools = false; -#endif - ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics, has_debug_tools); - ImGui::MenuItem("Debug Log", NULL, &show_app_debug_log, has_debug_tools); - ImGui::MenuItem("Stack Tool", NULL, &show_app_stack_tool, has_debug_tools); - ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor); - ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - - ImGui::Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); - ImGui::Spacing(); - - IMGUI_DEMO_MARKER("Help"); - if (ImGui::CollapsingHeader("Help")) - { - ImGui::Text("ABOUT THIS DEMO:"); - ImGui::BulletText("Sections below are demonstrating many aspects of the library."); - ImGui::BulletText("The \"Examples\" menu above leads to more demo contents."); - ImGui::BulletText("The \"Tools\" menu above gives access to: About Box, Style Editor,\n" - "and Metrics/Debugger (general purpose Dear ImGui debugging tool)."); - ImGui::Separator(); - - ImGui::Text("PROGRAMMER GUIDE:"); - ImGui::BulletText("See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!"); - ImGui::BulletText("See comments in imgui.cpp."); - ImGui::BulletText("See example applications in the examples/ folder."); - ImGui::BulletText("Read the FAQ at http://www.dearimgui.org/faq/"); - ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls."); - ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls."); - ImGui::Separator(); - - ImGui::Text("USER GUIDE:"); - ImGui::ShowUserGuide(); - } - - IMGUI_DEMO_MARKER("Configuration"); - if (ImGui::CollapsingHeader("Configuration")) - { - ImGuiIO& io = ImGui::GetIO(); - - if (ImGui::TreeNode("Configuration##2")) - { - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); - ImGui::SameLine(); HelpMarker("Enable keyboard controls."); - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); - ImGui::SameLine(); HelpMarker("Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", &io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos); - ImGui::SameLine(); HelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); - ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", &io.ConfigFlags, ImGuiConfigFlags_NoMouse); - if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) - { - // The "NoMouse" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it: - if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f) - { - ImGui::SameLine(); - ImGui::Text("<>"); - } - if (ImGui::IsKeyPressed(ImGuiKey_Space)) - io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; - } - ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); - ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility."); - ImGui::Checkbox("io.ConfigInputTrickleEventQueue", &io.ConfigInputTrickleEventQueue); - ImGui::SameLine(); HelpMarker("Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates."); - ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); - ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting)."); - ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText); - ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving)."); - ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); - ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); - ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); - ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); - ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); - ImGui::Text("Also see Style->Rendering for rendering options."); - ImGui::TreePop(); - ImGui::Separator(); - } - - IMGUI_DEMO_MARKER("Configuration/Backend Flags"); - if (ImGui::TreeNode("Backend Flags")) - { - HelpMarker( - "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n" - "Here we expose them as read-only fields to avoid breaking interactions with your backend."); - - // Make a local copy to avoid modifying actual backend flags. - // FIXME: We don't use BeginDisabled() to keep label bright, maybe we need a BeginReadonly() equivalent.. - ImGuiBackendFlags backend_flags = io.BackendFlags; - ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &backend_flags, ImGuiBackendFlags_HasGamepad); - ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &backend_flags, ImGuiBackendFlags_HasMouseCursors); - ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &backend_flags, ImGuiBackendFlags_HasSetMousePos); - ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &backend_flags, ImGuiBackendFlags_RendererHasVtxOffset); - ImGui::TreePop(); - ImGui::Separator(); - } - - IMGUI_DEMO_MARKER("Configuration/Style"); - if (ImGui::TreeNode("Style")) - { - HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function."); - ImGui::ShowStyleEditor(); - ImGui::TreePop(); - ImGui::Separator(); - } - - IMGUI_DEMO_MARKER("Configuration/Capture, Logging"); - if (ImGui::TreeNode("Capture/Logging")) - { - HelpMarker( - "The logging API redirects all text output so you can easily capture the content of " - "a window or a block. Tree nodes can be automatically expanded.\n" - "Try opening any of the contents below in this window and then click one of the \"Log To\" button."); - ImGui::LogButtons(); - - HelpMarker("You can also call ImGui::LogText() to output directly to the log without a visual output."); - if (ImGui::Button("Copy \"Hello, world!\" to clipboard")) - { - ImGui::LogToClipboard(); - ImGui::LogText("Hello, world!"); - ImGui::LogFinish(); - } - ImGui::TreePop(); - } - } - - IMGUI_DEMO_MARKER("Window options"); - if (ImGui::CollapsingHeader("Window options")) - { - if (ImGui::BeginTable("split", 3)) - { - ImGui::TableNextColumn(); ImGui::Checkbox("No titlebar", &no_titlebar); - ImGui::TableNextColumn(); ImGui::Checkbox("No scrollbar", &no_scrollbar); - ImGui::TableNextColumn(); ImGui::Checkbox("No menu", &no_menu); - ImGui::TableNextColumn(); ImGui::Checkbox("No move", &no_move); - ImGui::TableNextColumn(); ImGui::Checkbox("No resize", &no_resize); - ImGui::TableNextColumn(); ImGui::Checkbox("No collapse", &no_collapse); - ImGui::TableNextColumn(); ImGui::Checkbox("No close", &no_close); - ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav); - ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background); - ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front); - ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document); - ImGui::EndTable(); - } - } - - // All demo contents - ShowDemoWindowWidgets(); - ShowDemoWindowLayout(); - ShowDemoWindowPopups(); - ShowDemoWindowTables(); - ShowDemoWindowMisc(); - - // End of ShowDemoWindow() - ImGui::PopItemWidth(); - ImGui::End(); -} - -static void ShowDemoWindowWidgets() -{ - IMGUI_DEMO_MARKER("Widgets"); - if (!ImGui::CollapsingHeader("Widgets")) - return; - - static bool disable_all = false; // The Checkbox for that is inside the "Disabled" section at the bottom - if (disable_all) - ImGui::BeginDisabled(); - - IMGUI_DEMO_MARKER("Widgets/Basic"); - if (ImGui::TreeNode("Basic")) - { - IMGUI_DEMO_MARKER("Widgets/Basic/Button"); - static int clicked = 0; - if (ImGui::Button("Button")) - clicked++; - if (clicked & 1) - { - ImGui::SameLine(); - ImGui::Text("Thanks for clicking me!"); - } - - IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox"); - static bool check = true; - ImGui::Checkbox("checkbox", &check); - - IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton"); - static int e = 0; - ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); - ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); - ImGui::RadioButton("radio c", &e, 2); - - // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. - IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)"); - for (int i = 0; i < 7; i++) - { - if (i > 0) - ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); - ImGui::Button("Click"); - ImGui::PopStyleColor(3); - ImGui::PopID(); - } - - // Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements - // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!) - // See 'Demo->Layout->Text Baseline Alignment' for details. - ImGui::AlignTextToFramePadding(); - ImGui::Text("Hold to repeat:"); - ImGui::SameLine(); - - // Arrow buttons with Repeater - IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)"); - static int counter = 0; - float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - ImGui::PushButtonRepeat(true); - if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; } - ImGui::SameLine(0.0f, spacing); - if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; } - ImGui::PopButtonRepeat(); - ImGui::SameLine(); - ImGui::Text("%d", counter); - - IMGUI_DEMO_MARKER("Widgets/Basic/Tooltips"); - ImGui::Text("Hover over me"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip"); - - ImGui::SameLine(); - ImGui::Text("- or me"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::Text("I am a fancy tooltip"); - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); - ImGui::EndTooltip(); - } - - ImGui::Separator(); - ImGui::LabelText("label", "Value"); - - { - // Using the _simplified_ one-liner Combo() api here - // See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api. - IMGUI_DEMO_MARKER("Widgets/Basic/Combo"); - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" }; - static int item_current = 0; - ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); - ImGui::SameLine(); HelpMarker( - "Using the simplified one-liner Combo API here.\nRefer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API."); - } - - { - // To wire InputText() with std::string or any other custom string type, - // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. - IMGUI_DEMO_MARKER("Widgets/Basic/InputText"); - static char str0[128] = "Hello, world!"; - ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); - ImGui::SameLine(); HelpMarker( - "USER:\n" - "Hold SHIFT or use mouse to select text.\n" - "CTRL+Left/Right to word jump.\n" - "CTRL+A or double-click to select all.\n" - "CTRL+X,CTRL+C,CTRL+V clipboard.\n" - "CTRL+Z,CTRL+Y undo/redo.\n" - "ESCAPE to revert.\n\n" - "PROGRAMMER:\n" - "You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() " - "to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated " - "in imgui_demo.cpp)."); - - static char str1[128] = ""; - ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); - - IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat"); - static int i0 = 123; - ImGui::InputInt("input int", &i0); - - static float f0 = 0.001f; - ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f"); - - static double d0 = 999999.00000001; - ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f"); - - static float f1 = 1.e10f; - ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e"); - ImGui::SameLine(); HelpMarker( - "You can input value using the scientific notation,\n" - " e.g. \"1e+8\" becomes \"100000000\"."); - - static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; - ImGui::InputFloat3("input float3", vec4a); - } - - { - IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat"); - static int i1 = 50, i2 = 42; - ImGui::DragInt("drag int", &i1, 1); - ImGui::SameLine(); HelpMarker( - "Click and drag to edit value.\n" - "Hold SHIFT/ALT for faster/slower edit.\n" - "Double-click or CTRL+click to input value."); - - ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp); - - static float f1 = 1.00f, f2 = 0.0067f; - ImGui::DragFloat("drag float", &f1, 0.005f); - ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns"); - } - - { - IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat"); - static int i1 = 0; - ImGui::SliderInt("slider int", &i1, -1, 3); - ImGui::SameLine(); HelpMarker("CTRL+click to input value."); - - static float f1 = 0.123f, f2 = 0.0f; - ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); - ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic); - - IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle"); - static float angle = 0.0f; - ImGui::SliderAngle("slider angle", &angle); - - // Using the format string to display a name instead of an integer. - // Here we completely omit '%d' from the format string, so it'll only display a name. - // This technique can also be used with DragInt(). - IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)"); - enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT }; - static int elem = Element_Fire; - const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" }; - const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown"; - ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); - ImGui::SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer."); - } - - { - IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4"); - static float col1[3] = { 1.0f, 0.0f, 0.2f }; - static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; - ImGui::ColorEdit3("color 1", col1); - ImGui::SameLine(); HelpMarker( - "Click on the color square to open a color picker.\n" - "Click and hold to use drag and drop.\n" - "Right-click on the color square to show options.\n" - "CTRL+click on individual component to input value.\n"); - - ImGui::ColorEdit4("color 2", col2); - } - - { - // Using the _simplified_ one-liner ListBox() api here - // See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api. - IMGUI_DEMO_MARKER("Widgets/Basic/ListBox"); - const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; - static int item_current = 1; - ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4); - ImGui::SameLine(); HelpMarker( - "Using the simplified one-liner ListBox API here.\nRefer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API."); - } - - ImGui::TreePop(); - } - - // Testing ImGuiOnceUponAFrame helper. - //static ImGuiOnceUponAFrame once; - //for (int i = 0; i < 5; i++) - // if (once) - // ImGui::Text("This will be displayed only once."); - - IMGUI_DEMO_MARKER("Widgets/Trees"); - if (ImGui::TreeNode("Trees")) - { - IMGUI_DEMO_MARKER("Widgets/Trees/Basic trees"); - if (ImGui::TreeNode("Basic trees")) - { - for (int i = 0; i < 5; i++) - { - // Use SetNextItemOpen() so set the default state of a node to be open. We could - // also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing! - if (i == 0) - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - - if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i)) - { - ImGui::Text("blah blah"); - ImGui::SameLine(); - if (ImGui::SmallButton("button")) {} - ImGui::TreePop(); - } - } - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Trees/Advanced, with Selectable nodes"); - if (ImGui::TreeNode("Advanced, with Selectable nodes")) - { - HelpMarker( - "This is a more typical looking tree with selectable nodes.\n" - "Click to select, CTRL+Click to toggle, click on arrows or double-click to open."); - static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; - static bool align_label_with_current_x_position = false; - static bool test_drag_and_drop = false; - ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow); - ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick); - ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node."); - ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &base_flags, ImGuiTreeNodeFlags_SpanFullWidth); - ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position); - ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop); - ImGui::Text("Hello!"); - if (align_label_with_current_x_position) - ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - - // 'selection_mask' is dumb representation of what may be user-side selection state. - // You may retain selection state inside or outside your objects in whatever format you see fit. - // 'node_clicked' is temporary storage of what node we have clicked to process selection at the end - /// of the loop. May be a pointer to your own node type, etc. - static int selection_mask = (1 << 2); - int node_clicked = -1; - for (int i = 0; i < 6; i++) - { - // Disable the default "open on single-click behavior" + set Selected flag according to our selection. - // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection. - ImGuiTreeNodeFlags node_flags = base_flags; - const bool is_selected = (selection_mask & (1 << i)) != 0; - if (is_selected) - node_flags |= ImGuiTreeNodeFlags_Selected; - if (i < 3) - { - // Items 0..2 are Tree Node - bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); - if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) - node_clicked = i; - if (test_drag_and_drop && ImGui::BeginDragDropSource()) - { - ImGui::SetDragDropPayload("_TREENODE", NULL, 0); - ImGui::Text("This is a drag and drop source"); - ImGui::EndDragDropSource(); - } - if (node_open) - { - ImGui::BulletText("Blah blah\nBlah Blah"); - ImGui::TreePop(); - } - } - else - { - // Items 3..5 are Tree Leaves - // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can - // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text(). - node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet - ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); - if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) - node_clicked = i; - if (test_drag_and_drop && ImGui::BeginDragDropSource()) - { - ImGui::SetDragDropPayload("_TREENODE", NULL, 0); - ImGui::Text("This is a drag and drop source"); - ImGui::EndDragDropSource(); - } - } - } - if (node_clicked != -1) - { - // Update selection state - // (process outside of tree loop to avoid visual inconsistencies during the clicking frame) - if (ImGui::GetIO().KeyCtrl) - selection_mask ^= (1 << node_clicked); // CTRL+click to toggle - else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection - selection_mask = (1 << node_clicked); // Click to single-select - } - if (align_label_with_current_x_position) - ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Collapsing Headers"); - if (ImGui::TreeNode("Collapsing Headers")) - { - static bool closable_group = true; - ImGui::Checkbox("Show 2nd header", &closable_group); - if (ImGui::CollapsingHeader("Header", ImGuiTreeNodeFlags_None)) - { - ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); - for (int i = 0; i < 5; i++) - ImGui::Text("Some content %d", i); - } - if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) - { - ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); - for (int i = 0; i < 5; i++) - ImGui::Text("More content %d", i); - } - /* - if (ImGui::CollapsingHeader("Header with a bullet", ImGuiTreeNodeFlags_Bullet)) - ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); - */ - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Bullets"); - if (ImGui::TreeNode("Bullets")) - { - ImGui::BulletText("Bullet point 1"); - ImGui::BulletText("Bullet point 2\nOn multiple lines"); - if (ImGui::TreeNode("Tree node")) - { - ImGui::BulletText("Another bullet point"); - ImGui::TreePop(); - } - ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); - ImGui::Bullet(); ImGui::SmallButton("Button"); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Text"); - if (ImGui::TreeNode("Text")) - { - IMGUI_DEMO_MARKER("Widgets/Text/Colored Text"); - if (ImGui::TreeNode("Colorful Text")) - { - // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. - ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), "Pink"); - ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Yellow"); - ImGui::TextDisabled("Disabled"); - ImGui::SameLine(); HelpMarker("The TextDisabled color is stored in ImGuiStyle."); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping"); - if (ImGui::TreeNode("Word Wrapping")) - { - // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. - ImGui::TextWrapped( - "This text should automatically wrap on the edge of the window. The current implementation " - "for text wrapping follows simple rules suitable for English and possibly other languages."); - ImGui::Spacing(); - - static float wrap_width = 200.0f; - ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f"); - - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - for (int n = 0; n < 2; n++) - { - ImGui::Text("Test paragraph %d:", n); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y); - ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()); - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); - if (n == 0) - ImGui::Text("The lazy dog is a good dog. This paragraph should fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width); - else - ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh"); - - // Draw actual text bounding box, following by marker of our expected limit (should not overlap!) - draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255)); - draw_list->AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255)); - ImGui::PopTextWrapPos(); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text"); - if (ImGui::TreeNode("UTF-8 Text")) - { - // UTF-8 test with Japanese characters - // (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md for details.) - // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8 - // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you - // can save your source files as 'UTF-8 without signature'). - // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8 - // CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants. - // Don't do this in your application! Please use u8"text in any language" in your application! - // Note that characters values are preserved even by InputText() if the font cannot be displayed, - // so you can safely copy & paste garbled characters into another application. - ImGui::TextWrapped( - "CJK text will only appears if the font was loaded with the appropriate CJK character ranges. " - "Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. " - "Read docs/FONTS.md for details."); - ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); // Normally we would use u8"blah blah" with the proper characters directly in the string. - ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); - static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; - //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis - ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf)); - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Images"); - if (ImGui::TreeNode("Images")) - { - ImGuiIO& io = ImGui::GetIO(); - ImGui::TextWrapped( - "Below we are displaying the font texture (which is the only texture we have access to in this demo). " - "Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. " - "Hover the texture for a zoomed view!"); - - // Below we are displaying the font texture because it is the only texture we have access to inside the demo! - // Remember that ImTextureID is just storage for whatever you want it to be. It is essentially a value that - // will be passed to the rendering backend via the ImDrawCmd structure. - // If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top - // of their respective source file to specify what they expect to be stored in ImTextureID, for example: - // - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer - // - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc. - // More: - // - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers - // to ImGui::Image(), and gather width/height through your own functions, etc. - // - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer, - // it will help you debug issues if you are confused about it. - // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage(). - // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md - // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples - ImTextureID my_tex_id = io.Fonts->TexID; - float my_tex_w = (float)io.Fonts->TexWidth; - float my_tex_h = (float)io.Fonts->TexHeight; - { - ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left - ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right - ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint - ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); // 50% opaque white - ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, tint_col, border_col); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - float region_sz = 32.0f; - float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; - float region_y = io.MousePos.y - pos.y - region_sz * 0.5f; - float zoom = 4.0f; - if (region_x < 0.0f) { region_x = 0.0f; } - else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; } - if (region_y < 0.0f) { region_y = 0.0f; } - else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; } - ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y); - ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz); - ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h); - ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h); - ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, tint_col, border_col); - ImGui::EndTooltip(); - } - } - - IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons"); - ImGui::TextWrapped("And now some textured buttons.."); - static int pressed_count = 0; - for (int i = 0; i < 8; i++) - { - ImGui::PushID(i); - int frame_padding = -1 + i; // -1 == uses default padding (style.FramePadding) - ImVec2 size = ImVec2(32.0f, 32.0f); // Size of the image we want to make visible - ImVec2 uv0 = ImVec2(0.0f, 0.0f); // UV coordinates for lower-left - ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h);// UV coordinates for (32,32) in our texture - ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // Black background - ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint - if (ImGui::ImageButton(my_tex_id, size, uv0, uv1, frame_padding, bg_col, tint_col)) - pressed_count += 1; - ImGui::PopID(); - ImGui::SameLine(); - } - ImGui::NewLine(); - ImGui::Text("Pressed %d times.", pressed_count); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Combo"); - if (ImGui::TreeNode("Combo")) - { - // Expose flags as checkbox for the demo - static ImGuiComboFlags flags = 0; - ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft); - ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo"); - if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, ImGuiComboFlags_NoArrowButton)) - flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both - if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, ImGuiComboFlags_NoPreview)) - flags &= ~ImGuiComboFlags_NoArrowButton; // Clear the other flag, as we cannot combine both - - // Using the generic BeginCombo() API, you have full control over how to display the combo contents. - // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively - // stored in the object itself, etc.) - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; - static int item_current_idx = 0; // Here we store our selection data as an index. - const char* combo_preview_value = items[item_current_idx]; // Pass in the preview value visible before opening the combo (it could be anything) - if (ImGui::BeginCombo("combo 1", combo_preview_value, flags)) - { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) - { - const bool is_selected = (item_current_idx == n); - if (ImGui::Selectable(items[n], is_selected)) - item_current_idx = n; - - // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) - if (is_selected) - ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } - - // Simplified one-liner Combo() API, using values packed in a single constant string - // This is a convenience for when the selection set is small and known at compile-time. - static int item_current_2 = 0; - ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); - - // Simplified one-liner Combo() using an array of const char* - // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control. - static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview - ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); - - // Simplified one-liner Combo() using an accessor function - struct Funcs { static bool ItemGetter(void* data, int n, const char** out_str) { *out_str = ((const char**)data)[n]; return true; } }; - static int item_current_4 = 0; - ImGui::Combo("combo 4 (function)", &item_current_4, &Funcs::ItemGetter, items, IM_ARRAYSIZE(items)); - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/List Boxes"); - if (ImGui::TreeNode("List boxes")) - { - // Using the generic BeginListBox() API, you have full control over how to display the combo contents. - // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively - // stored in the object itself, etc.) - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; - static int item_current_idx = 0; // Here we store our selection data as an index. - if (ImGui::BeginListBox("listbox 1")) - { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) - { - const bool is_selected = (item_current_idx == n); - if (ImGui::Selectable(items[n], is_selected)) - item_current_idx = n; - - // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) - if (is_selected) - ImGui::SetItemDefaultFocus(); - } - ImGui::EndListBox(); - } - - // Custom size: use all width, 5 items tall - ImGui::Text("Full-width:"); - if (ImGui::BeginListBox("##listbox 2", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing()))) - { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) - { - const bool is_selected = (item_current_idx == n); - if (ImGui::Selectable(items[n], is_selected)) - item_current_idx = n; - - // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) - if (is_selected) - ImGui::SetItemDefaultFocus(); - } - ImGui::EndListBox(); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Selectables"); - if (ImGui::TreeNode("Selectables")) - { - // Selectable() has 2 overloads: - // - The one taking "bool selected" as a read-only selection information. - // When Selectable() has been clicked it returns true and you can alter selection state accordingly. - // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases) - // The earlier is more flexible, as in real application your selection may be stored in many different ways - // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc). - IMGUI_DEMO_MARKER("Widgets/Selectables/Basic"); - if (ImGui::TreeNode("Basic")) - { - static bool selection[5] = { false, true, false, false, false }; - ImGui::Selectable("1. I am selectable", &selection[0]); - ImGui::Selectable("2. I am selectable", &selection[1]); - ImGui::Text("(I am not selectable)"); - ImGui::Selectable("4. I am selectable", &selection[3]); - if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) - if (ImGui::IsMouseDoubleClicked(0)) - selection[4] = !selection[4]; - ImGui::TreePop(); - } - IMGUI_DEMO_MARKER("Widgets/Selectables/Single Selection"); - if (ImGui::TreeNode("Selection State: Single Selection")) - { - static int selected = -1; - for (int n = 0; n < 5; n++) - { - char buf[32]; - sprintf(buf, "Object %d", n); - if (ImGui::Selectable(buf, selected == n)) - selected = n; - } - ImGui::TreePop(); - } - IMGUI_DEMO_MARKER("Widgets/Selectables/Multiple Selection"); - if (ImGui::TreeNode("Selection State: Multiple Selection")) - { - HelpMarker("Hold CTRL and click to select multiple items."); - static bool selection[5] = { false, false, false, false, false }; - for (int n = 0; n < 5; n++) - { - char buf[32]; - sprintf(buf, "Object %d", n); - if (ImGui::Selectable(buf, selection[n])) - { - if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held - memset(selection, 0, sizeof(selection)); - selection[n] ^= 1; - } - } - ImGui::TreePop(); - } - IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more text into the same line"); - if (ImGui::TreeNode("Rendering more text into the same line")) - { - // Using the Selectable() override that takes "bool* p_selected" parameter, - // this function toggle your bool value automatically. - static bool selected[3] = { false, false, false }; - ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); - ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes"); - ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); - ImGui::TreePop(); - } - IMGUI_DEMO_MARKER("Widgets/Selectables/In columns"); - if (ImGui::TreeNode("In columns")) - { - static bool selected[10] = {}; - - if (ImGui::BeginTable("split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) - { - for (int i = 0; i < 10; i++) - { - char label[32]; - sprintf(label, "Item %d", i); - ImGui::TableNextColumn(); - ImGui::Selectable(label, &selected[i]); // FIXME-TABLE: Selection overlap - } - ImGui::EndTable(); - } - ImGui::Spacing(); - if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) - { - for (int i = 0; i < 10; i++) - { - char label[32]; - sprintf(label, "Item %d", i); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Selectable(label, &selected[i], ImGuiSelectableFlags_SpanAllColumns); - ImGui::TableNextColumn(); - ImGui::Text("Some other contents"); - ImGui::TableNextColumn(); - ImGui::Text("123456"); - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - IMGUI_DEMO_MARKER("Widgets/Selectables/Grid"); - if (ImGui::TreeNode("Grid")) - { - static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; - - // Add in a bit of silly fun... - const float time = (float)ImGui::GetTime(); - const bool winning_state = memchr(selected, 0, sizeof(selected)) == NULL; // If all cells are selected... - if (winning_state) - ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f))); - - for (int y = 0; y < 4; y++) - for (int x = 0; x < 4; x++) - { - if (x > 0) - ImGui::SameLine(); - ImGui::PushID(y * 4 + x); - if (ImGui::Selectable("Sailor", selected[y][x] != 0, 0, ImVec2(50, 50))) - { - // Toggle clicked cell + toggle neighbors - selected[y][x] ^= 1; - if (x > 0) { selected[y][x - 1] ^= 1; } - if (x < 3) { selected[y][x + 1] ^= 1; } - if (y > 0) { selected[y - 1][x] ^= 1; } - if (y < 3) { selected[y + 1][x] ^= 1; } - } - ImGui::PopID(); - } - - if (winning_state) - ImGui::PopStyleVar(); - ImGui::TreePop(); - } - IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment"); - if (ImGui::TreeNode("Alignment")) - { - HelpMarker( - "By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item " - "basis using PushStyleVar(). You'll probably want to always keep your default situation to " - "left-align otherwise it becomes difficult to layout multiple items on a same line"); - static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true }; - for (int y = 0; y < 3; y++) - { - for (int x = 0; x < 3; x++) - { - ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f); - char name[32]; - sprintf(name, "(%.1f,%.1f)", alignment.x, alignment.y); - if (x > 0) ImGui::SameLine(); - ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment); - ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(80, 80)); - ImGui::PopStyleVar(); - } - } - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - // To wire InputText() with std::string or any other custom string type, - // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. - IMGUI_DEMO_MARKER("Widgets/Text Input"); - if (ImGui::TreeNode("Text Input")) - { - IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input"); - if (ImGui::TreeNode("Multi-line Text Input")) - { - // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize - // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings. - static char text[1024 * 16] = - "/*\n" - " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" - " the hexadecimal encoding of one offending instruction,\n" - " more formally, the invalid operand with locked CMPXCHG8B\n" - " instruction bug, is a design flaw in the majority of\n" - " Intel Pentium, Pentium MMX, and Pentium OverDrive\n" - " processors (all in the P5 microarchitecture).\n" - "*/\n\n" - "label:\n" - "\tlock cmpxchg8b eax\n"; - - static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput; - HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include in here)"); - ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); - ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput); - ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine); - ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input"); - if (ImGui::TreeNode("Filtered Text Input")) - { - struct TextFilters - { - // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i' - static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) - { - if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) - return 0; - return 1; - } - }; - - static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); - static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); - static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); - static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); - static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); - static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Text Input/Password input"); - if (ImGui::TreeNode("Password Input")) - { - static char password[64] = "password123"; - ImGui::InputText("password", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password); - ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); - ImGui::InputTextWithHint("password (w/ hint)", "", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password); - ImGui::InputText("password (clear)", password, IM_ARRAYSIZE(password)); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Completion, History, Edit Callbacks")) - { - struct Funcs - { - static int MyCallback(ImGuiInputTextCallbackData* data) - { - if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion) - { - data->InsertChars(data->CursorPos, ".."); - } - else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory) - { - if (data->EventKey == ImGuiKey_UpArrow) - { - data->DeleteChars(0, data->BufTextLen); - data->InsertChars(0, "Pressed Up!"); - data->SelectAll(); - } - else if (data->EventKey == ImGuiKey_DownArrow) - { - data->DeleteChars(0, data->BufTextLen); - data->InsertChars(0, "Pressed Down!"); - data->SelectAll(); - } - } - else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit) - { - // Toggle casing of first character - char c = data->Buf[0]; - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32; - data->BufDirty = true; - - // Increment a counter - int* p_int = (int*)data->UserData; - *p_int = *p_int + 1; - } - return 0; - } - }; - static char buf1[64]; - ImGui::InputText("Completion", buf1, 64, ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback); - ImGui::SameLine(); HelpMarker("Here we append \"..\" each time Tab is pressed. See 'Examples>Console' for a more meaningful demonstration of using this callback."); - - static char buf2[64]; - ImGui::InputText("History", buf2, 64, ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback); - ImGui::SameLine(); HelpMarker("Here we replace and select text each time Up/Down are pressed. See 'Examples>Console' for a more meaningful demonstration of using this callback."); - - static char buf3[64]; - static int edit_count = 0; - ImGui::InputText("Edit", buf3, 64, ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count); - ImGui::SameLine(); HelpMarker("Here we toggle the casing of the first character on every edits + count edits."); - ImGui::SameLine(); ImGui::Text("(%d)", edit_count); - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback"); - if (ImGui::TreeNode("Resize Callback")) - { - // To wire InputText() with std::string or any other custom string type, - // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper - // using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string. - HelpMarker( - "Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n" - "See misc/cpp/imgui_stdlib.h for an implementation of this for std::string."); - struct Funcs - { - static int MyResizeCallback(ImGuiInputTextCallbackData* data) - { - if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) - { - ImVector* my_str = (ImVector*)data->UserData; - IM_ASSERT(my_str->begin() == data->Buf); - my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1 - data->Buf = my_str->begin(); - } - return 0; - } - - // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace. - // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)' - static bool MyInputTextMultiline(const char* label, ImVector* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0) - { - IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); - return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str); - } - }; - - // For this demo we are using ImVector as a string container. - // Note that because we need to store a terminating zero character, our size/capacity are 1 more - // than usually reported by a typical string class. - static ImVector my_str; - if (my_str.empty()) - my_str.push_back(0); - Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16)); - ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity()); - ImGui::TreePop(); - } - - ImGui::TreePop(); - } - - // Tabs - IMGUI_DEMO_MARKER("Widgets/Tabs"); - if (ImGui::TreeNode("Tabs")) - { - IMGUI_DEMO_MARKER("Widgets/Tabs/Basic"); - if (ImGui::TreeNode("Basic")) - { - ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; - if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) - { - if (ImGui::BeginTabItem("Avocado")) - { - ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah"); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Broccoli")) - { - ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah"); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Cucumber")) - { - ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah"); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } - ImGui::Separator(); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button"); - if (ImGui::TreeNode("Advanced & Close Button")) - { - // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0). - static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable; - ImGui::CheckboxFlags("ImGuiTabBarFlags_Reorderable", &tab_bar_flags, ImGuiTabBarFlags_Reorderable); - ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", &tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs); - ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton); - ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton); - if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) - tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_; - if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown)) - tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown); - if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll)) - tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); - - // Tab Bar - const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" }; - static bool opened[4] = { true, true, true, true }; // Persistent user state - for (int n = 0; n < IM_ARRAYSIZE(opened); n++) - { - if (n > 0) { ImGui::SameLine(); } - ImGui::Checkbox(names[n], &opened[n]); - } - - // Passing a bool* to BeginTabItem() is similar to passing one to Begin(): - // the underlying bool will be set to false when the tab is closed. - if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) - { - for (int n = 0; n < IM_ARRAYSIZE(opened); n++) - if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None)) - { - ImGui::Text("This is the %s tab!", names[n]); - if (n & 1) - ImGui::Text("I am an odd tab."); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } - ImGui::Separator(); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags"); - if (ImGui::TreeNode("TabItemButton & Leading/Trailing flags")) - { - static ImVector active_tabs; - static int next_tab_id = 0; - if (next_tab_id == 0) // Initialize with some default tabs - for (int i = 0; i < 3; i++) - active_tabs.push_back(next_tab_id++); - - // TabItemButton() and Leading/Trailing flags are distinct features which we will demo together. - // (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags... - // but they tend to make more sense together) - static bool show_leading_button = true; - static bool show_trailing_button = true; - ImGui::Checkbox("Show Leading TabItemButton()", &show_leading_button); - ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button); - - // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs - static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyResizeDown; - ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton); - if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown)) - tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown); - if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll)) - tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); - - if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) - { - // Demo a Leading TabItemButton(): click the "?" button to open a menu - if (show_leading_button) - if (ImGui::TabItemButton("?", ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip)) - ImGui::OpenPopup("MyHelpMenu"); - if (ImGui::BeginPopup("MyHelpMenu")) - { - ImGui::Selectable("Hello!"); - ImGui::EndPopup(); - } - - // Demo Trailing Tabs: click the "+" button to add a new tab (in your app you may want to use a font icon instead of the "+") - // Note that we submit it before the regular tabs, but because of the ImGuiTabItemFlags_Trailing flag it will always appear at the end. - if (show_trailing_button) - if (ImGui::TabItemButton("+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) - active_tabs.push_back(next_tab_id++); // Add new tab - - // Submit our regular tabs - for (int n = 0; n < active_tabs.Size; ) - { - bool open = true; - char name[16]; - snprintf(name, IM_ARRAYSIZE(name), "%04d", active_tabs[n]); - if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None)) - { - ImGui::Text("This is the %s tab!", name); - ImGui::EndTabItem(); - } - - if (!open) - active_tabs.erase(active_tabs.Data + n); - else - n++; - } - - ImGui::EndTabBar(); - } - ImGui::Separator(); - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - // Plot/Graph widgets are not very good. - // Consider using a third-party library such as ImPlot: https://github.com/epezent/implot - // (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions) - IMGUI_DEMO_MARKER("Widgets/Plotting"); - if (ImGui::TreeNode("Plotting")) - { - static bool animate = true; - ImGui::Checkbox("Animate", &animate); - - // Plot as lines and plot as histogram - IMGUI_DEMO_MARKER("Widgets/Plotting/PlotLines, PlotHistogram"); - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); - ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); - - // Fill an array of contiguous float values to plot - // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float - // and the sizeof() of your structure in the "stride" parameter. - static float values[90] = {}; - static int values_offset = 0; - static double refresh_time = 0.0; - if (!animate || refresh_time == 0.0) - refresh_time = ImGui::GetTime(); - while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo - { - static float phase = 0.0f; - values[values_offset] = cosf(phase); - values_offset = (values_offset + 1) % IM_ARRAYSIZE(values); - phase += 0.10f * values_offset; - refresh_time += 1.0f / 60.0f; - } - - // Plots can display overlay texts - // (in this example, we will display an average value) - { - float average = 0.0f; - for (int n = 0; n < IM_ARRAYSIZE(values); n++) - average += values[n]; - average /= (float)IM_ARRAYSIZE(values); - char overlay[32]; - sprintf(overlay, "avg %f", average); - ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f)); - } - - // Use functions to generate output - // FIXME: This is rather awkward because current plot API only pass in indices. - // We probably want an API passing floats and user provide sample rate/count. - struct Funcs - { - static float Sin(void*, int i) { return sinf(i * 0.1f); } - static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; } - }; - static int func_type = 0, display_count = 70; - ImGui::Separator(); - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); - ImGui::Combo("func", &func_type, "Sin\0Saw\0"); - ImGui::SameLine(); - ImGui::SliderInt("Sample count", &display_count, 1, 400); - float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; - ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); - ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); - ImGui::Separator(); - - // Animate a simple progress bar - IMGUI_DEMO_MARKER("Widgets/Plotting/ProgressBar"); - static float progress = 0.0f, progress_dir = 1.0f; - if (animate) - { - progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; - if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } - if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } - } - - // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width, - // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. - ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f)); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::Text("Progress Bar"); - - float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f); - char buf[32]; - sprintf(buf, "%d/%d", (int)(progress_saturated * 1753), 1753); - ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Color"); - if (ImGui::TreeNode("Color/Picker Widgets")) - { - static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f); - - static bool alpha_preview = true; - static bool alpha_half_preview = false; - static bool drag_and_drop = true; - static bool options_menu = true; - static bool hdr = false; - ImGui::Checkbox("With Alpha Preview", &alpha_preview); - ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); - ImGui::Checkbox("With Drag and Drop", &drag_and_drop); - ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); - ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); - ImGuiColorEditFlags misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); - - IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit"); - ImGui::Text("Color widget:"); - ImGui::SameLine(); HelpMarker( - "Click on the color square to open a color picker.\n" - "CTRL+click on individual component to input value.\n"); - ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); - - IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)"); - ImGui::Text("Color widget HSV with Alpha:"); - ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags); - - IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)"); - ImGui::Text("Color widget with Float Display:"); - ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); - - IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)"); - ImGui::Text("Color button with Picker:"); - ImGui::SameLine(); HelpMarker( - "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n" - "With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only " - "be used for the tooltip and picker popup."); - ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); - - IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)"); - ImGui::Text("Color button with Custom Picker Popup:"); - - // Generate a default palette. The palette will persist and can be edited. - static bool saved_palette_init = true; - static ImVec4 saved_palette[32] = {}; - if (saved_palette_init) - { - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) - { - ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, - saved_palette[n].x, saved_palette[n].y, saved_palette[n].z); - saved_palette[n].w = 1.0f; // Alpha - } - saved_palette_init = false; - } - - static ImVec4 backup_color; - bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags); - ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x); - open_popup |= ImGui::Button("Palette"); - if (open_popup) - { - ImGui::OpenPopup("mypicker"); - backup_color = color; - } - if (ImGui::BeginPopup("mypicker")) - { - ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); - ImGui::Separator(); - ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); - ImGui::SameLine(); - - ImGui::BeginGroup(); // Lock X position - ImGui::Text("Current"); - ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)); - ImGui::Text("Previous"); - if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40))) - color = backup_color; - ImGui::Separator(); - ImGui::Text("Palette"); - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) - { - ImGui::PushID(n); - if ((n % 8) != 0) - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); - - ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip; - if (ImGui::ColorButton("##palette", saved_palette[n], palette_button_flags, ImVec2(20, 20))) - color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha! - - // Allow user to drop colors into each palette entry. Note that ColorButton() is already a - // drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag. - if (ImGui::BeginDragDropTarget()) - { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) - memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3); - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) - memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4); - ImGui::EndDragDropTarget(); - } - - ImGui::PopID(); - } - ImGui::EndGroup(); - ImGui::EndPopup(); - } - - IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)"); - ImGui::Text("Color button only:"); - static bool no_border = false; - ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border); - ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80)); - - IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker"); - ImGui::Text("Color picker:"); - static bool alpha = true; - static bool alpha_bar = true; - static bool side_preview = true; - static bool ref_color = false; - static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f); - static int display_mode = 0; - static int picker_mode = 0; - ImGui::Checkbox("With Alpha", &alpha); - ImGui::Checkbox("With Alpha Bar", &alpha_bar); - ImGui::Checkbox("With Side Preview", &side_preview); - if (side_preview) - { - ImGui::SameLine(); - ImGui::Checkbox("With Ref Color", &ref_color); - if (ref_color) - { - ImGui::SameLine(); - ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); - } - } - ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0None\0RGB Only\0HSV Only\0Hex Only\0"); - ImGui::SameLine(); HelpMarker( - "ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, " - "but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex " - "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions()."); - ImGui::SameLine(); HelpMarker("When not specified explicitly (Auto/Current mode), user can right-click the picker to change mode."); - ImGuiColorEditFlags flags = misc_flags; - if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() - if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; - if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; - if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; - if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; - if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays - if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB; // Override display mode - if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV; - if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex; - ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL); - - ImGui::Text("Set defaults in code:"); - ImGui::SameLine(); HelpMarker( - "SetColorEditOptions() is designed to allow you to set boot-time default.\n" - "We don't have Push/Pop functions because you can force options on a per-widget basis if needed," - "and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid" - "encouraging you to persistently save values that aren't forward-compatible."); - if (ImGui::Button("Default: Uint8 + HSV + Hue Bar")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar); - if (ImGui::Button("Default: Float + HDR + Hue Wheel")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); - - // Always both a small version of both types of pickers (to make it more visible in the demo to people who are skimming quickly through it) - ImGui::Text("Both types:"); - float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f; - ImGui::SetNextItemWidth(w); - ImGui::ColorPicker3("##MyColor##5", (float*)&color, ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha); - ImGui::SameLine(); - ImGui::SetNextItemWidth(w); - ImGui::ColorPicker3("##MyColor##6", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha); - - // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0) - static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV! - ImGui::Spacing(); - ImGui::Text("HSV encoded colors"); - ImGui::SameLine(); HelpMarker( - "By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV" - "allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the" - "added benefit that you can manipulate hue values with the picker even when saturation or value are zero."); - ImGui::Text("Color widget with InputHSV:"); - ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); - ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); - ImGui::DragFloat4("Raw HSV values", (float*)&color_hsv, 0.01f, 0.0f, 1.0f); - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags"); - if (ImGui::TreeNode("Drag/Slider Flags")) - { - // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same! - static ImGuiSliderFlags flags = ImGuiSliderFlags_None; - ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp); - ImGui::SameLine(); HelpMarker("Always clamp value to min/max bounds (if any) when input manually with CTRL+Click."); - ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic); - ImGui::SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values)."); - ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat); - ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits)."); - ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput); - ImGui::SameLine(); HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget."); - - // Drags - static float drag_f = 0.5f; - static int drag_i = 50; - ImGui::Text("Underlying float value: %f", drag_f); - ImGui::DragFloat("DragFloat (0 -> 1)", &drag_f, 0.005f, 0.0f, 1.0f, "%.3f", flags); - ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags); - ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags); - ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags); - ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags); - - // Sliders - static float slider_f = 0.5f; - static int slider_i = 50; - ImGui::Text("Underlying float value: %f", slider_f); - ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags); - ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags); - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Range Widgets"); - if (ImGui::TreeNode("Range Widgets")) - { - static float begin = 10, end = 90; - static int begin_i = 100, end_i = 1000; - ImGui::DragFloatRange2("range float", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%", ImGuiSliderFlags_AlwaysClamp); - ImGui::DragIntRange2("range int", &begin_i, &end_i, 5, 0, 1000, "Min: %d units", "Max: %d units"); - ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units"); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Data Types"); - if (ImGui::TreeNode("Data Types")) - { - // DragScalar/InputScalar/SliderScalar functions allow various data types - // - signed/unsigned - // - 8/16/32/64-bits - // - integer/float/double - // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum - // to pass the type, and passing all arguments by pointer. - // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each types. - // In practice, if you frequently use a given type that is not covered by the normal API entry points, - // you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*, - // and then pass their address to the generic function. For example: - // bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld") - // { - // return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format); - // } - - // Setup limits (as helper variables so we can take their address, as explained above) - // Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2. - #ifndef LLONG_MIN - ImS64 LLONG_MIN = -9223372036854775807LL - 1; - ImS64 LLONG_MAX = 9223372036854775807LL; - ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1); - #endif - const char s8_zero = 0, s8_one = 1, s8_fifty = 50, s8_min = -128, s8_max = 127; - const ImU8 u8_zero = 0, u8_one = 1, u8_fifty = 50, u8_min = 0, u8_max = 255; - const short s16_zero = 0, s16_one = 1, s16_fifty = 50, s16_min = -32768, s16_max = 32767; - const ImU16 u16_zero = 0, u16_one = 1, u16_fifty = 50, u16_min = 0, u16_max = 65535; - const ImS32 s32_zero = 0, s32_one = 1, s32_fifty = 50, s32_min = INT_MIN/2, s32_max = INT_MAX/2, s32_hi_a = INT_MAX/2 - 100, s32_hi_b = INT_MAX/2; - const ImU32 u32_zero = 0, u32_one = 1, u32_fifty = 50, u32_min = 0, u32_max = UINT_MAX/2, u32_hi_a = UINT_MAX/2 - 100, u32_hi_b = UINT_MAX/2; - const ImS64 s64_zero = 0, s64_one = 1, s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2, s64_hi_a = LLONG_MAX/2 - 100, s64_hi_b = LLONG_MAX/2; - const ImU64 u64_zero = 0, u64_one = 1, u64_fifty = 50, u64_min = 0, u64_max = ULLONG_MAX/2, u64_hi_a = ULLONG_MAX/2 - 100, u64_hi_b = ULLONG_MAX/2; - const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f; - const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0; - - // State - static char s8_v = 127; - static ImU8 u8_v = 255; - static short s16_v = 32767; - static ImU16 u16_v = 65535; - static ImS32 s32_v = -1; - static ImU32 u32_v = (ImU32)-1; - static ImS64 s64_v = -1; - static ImU64 u64_v = (ImU64)-1; - static float f32_v = 0.123f; - static double f64_v = 90000.01234567890123456789; - - const float drag_speed = 0.2f; - static bool drag_clamp = false; - IMGUI_DEMO_MARKER("Widgets/Data Types/Drags"); - ImGui::Text("Drags:"); - ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); - ImGui::SameLine(); HelpMarker( - "As with every widgets in dear imgui, we never modify values unless there is a user interaction.\n" - "You can override the clamping limits by using CTRL+Click to input a value."); - ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL, drag_clamp ? &s8_fifty : NULL); - ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL, drag_clamp ? &u8_fifty : NULL, "%u ms"); - ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL); - ImGui::DragScalar("drag u16", ImGuiDataType_U16, &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, "%u ms"); - ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL); - ImGui::DragScalar("drag s32 hex", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL, "0x%08X"); - ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms"); - ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL); - ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL); - ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f"); - ImGui::DragScalar("drag float log", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", ImGuiSliderFlags_Logarithmic); - ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams"); - ImGui::DragScalar("drag double log",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", ImGuiSliderFlags_Logarithmic); - - IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders"); - ImGui::Text("Sliders"); - ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d"); - ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u"); - ImGui::SliderScalar("slider s16 full", ImGuiDataType_S16, &s16_v, &s16_min, &s16_max, "%d"); - ImGui::SliderScalar("slider u16 full", ImGuiDataType_U16, &u16_v, &u16_min, &u16_max, "%u"); - ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d"); - ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d"); - ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d"); - ImGui::SliderScalar("slider s32 hex", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty, "0x%04X"); - ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u"); - ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u"); - ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u"); - ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%" IM_PRId64); - ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%" IM_PRId64); - ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%" IM_PRId64); - ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%" IM_PRIu64 " ms"); - ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%" IM_PRIu64 " ms"); - ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%" IM_PRIu64 " ms"); - ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one); - ImGui::SliderScalar("slider float low log", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", ImGuiSliderFlags_Logarithmic); - ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e"); - ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams"); - ImGui::SliderScalar("slider double low log",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", ImGuiSliderFlags_Logarithmic); - ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams"); - - ImGui::Text("Sliders (reverse)"); - ImGui::SliderScalar("slider s8 reverse", ImGuiDataType_S8, &s8_v, &s8_max, &s8_min, "%d"); - ImGui::SliderScalar("slider u8 reverse", ImGuiDataType_U8, &u8_v, &u8_max, &u8_min, "%u"); - ImGui::SliderScalar("slider s32 reverse", ImGuiDataType_S32, &s32_v, &s32_fifty, &s32_zero, "%d"); - ImGui::SliderScalar("slider u32 reverse", ImGuiDataType_U32, &u32_v, &u32_fifty, &u32_zero, "%u"); - ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" IM_PRId64); - ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" IM_PRIu64 " ms"); - - IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs"); - static bool inputs_step = true; - ImGui::Text("Inputs"); - ImGui::Checkbox("Show step buttons", &inputs_step); - ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d"); - ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u"); - ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d"); - ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u"); - ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d"); - ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%04X"); - ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u"); - ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X"); - ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL); - ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL); - ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL); - ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL); - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets"); - if (ImGui::TreeNode("Multi-component Widgets")) - { - static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; - static int vec4i[4] = { 1, 5, 100, 255 }; - - ImGui::InputFloat2("input float2", vec4f); - ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f); - ImGui::InputInt2("input int2", vec4i); - ImGui::DragInt2("drag int2", vec4i, 1, 0, 255); - ImGui::SliderInt2("slider int2", vec4i, 0, 255); - ImGui::Spacing(); - - ImGui::InputFloat3("input float3", vec4f); - ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f); - ImGui::InputInt3("input int3", vec4i); - ImGui::DragInt3("drag int3", vec4i, 1, 0, 255); - ImGui::SliderInt3("slider int3", vec4i, 0, 255); - ImGui::Spacing(); - - ImGui::InputFloat4("input float4", vec4f); - ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f); - ImGui::InputInt4("input int4", vec4i); - ImGui::DragInt4("drag int4", vec4i, 1, 0, 255); - ImGui::SliderInt4("slider int4", vec4i, 0, 255); - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Vertical Sliders"); - if (ImGui::TreeNode("Vertical Sliders")) - { - const float spacing = 4; - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); - - static int int_value = 0; - ImGui::VSliderInt("##int", ImVec2(18, 160), &int_value, 0, 5); - ImGui::SameLine(); - - static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f }; - ImGui::PushID("set1"); - for (int i = 0; i < 7; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i / 7.0f, 0.5f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i / 7.0f, 0.9f, 0.9f)); - ImGui::VSliderFloat("##v", ImVec2(18, 160), &values[i], 0.0f, 1.0f, ""); - if (ImGui::IsItemActive() || ImGui::IsItemHovered()) - ImGui::SetTooltip("%.3f", values[i]); - ImGui::PopStyleColor(4); - ImGui::PopID(); - } - ImGui::PopID(); - - ImGui::SameLine(); - ImGui::PushID("set2"); - static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f }; - const int rows = 3; - const ImVec2 small_slider_size(18, (float)(int)((160.0f - (rows - 1) * spacing) / rows)); - for (int nx = 0; nx < 4; nx++) - { - if (nx > 0) ImGui::SameLine(); - ImGui::BeginGroup(); - for (int ny = 0; ny < rows; ny++) - { - ImGui::PushID(nx * rows + ny); - ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, ""); - if (ImGui::IsItemActive() || ImGui::IsItemHovered()) - ImGui::SetTooltip("%.3f", values2[nx]); - ImGui::PopID(); - } - ImGui::EndGroup(); - } - ImGui::PopID(); - - ImGui::SameLine(); - ImGui::PushID("set3"); - for (int i = 0; i < 4; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40); - ImGui::VSliderFloat("##v", ImVec2(40, 160), &values[i], 0.0f, 1.0f, "%.2f\nsec"); - ImGui::PopStyleVar(); - ImGui::PopID(); - } - ImGui::PopID(); - ImGui::PopStyleVar(); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Drag and drop"); - if (ImGui::TreeNode("Drag and Drop")) - { - IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets"); - if (ImGui::TreeNode("Drag and drop in standard widgets")) - { - // ColorEdit widgets automatically act as drag source and drag target. - // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F - // to allow your own widgets to use colors in their drag and drop interaction. - // Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo. - HelpMarker("You can drag from the color squares."); - static float col1[3] = { 1.0f, 0.0f, 0.2f }; - static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; - ImGui::ColorEdit3("color 1", col1); - ImGui::ColorEdit4("color 2", col2); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items"); - if (ImGui::TreeNode("Drag and drop to copy/swap items")) - { - enum Mode - { - Mode_Copy, - Mode_Move, - Mode_Swap - }; - static int mode = 0; - if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine(); - if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine(); - if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; } - static const char* names[9] = - { - "Bobby", "Beatrice", "Betty", - "Brianna", "Barry", "Bernard", - "Bibi", "Blaine", "Bryn" - }; - for (int n = 0; n < IM_ARRAYSIZE(names); n++) - { - ImGui::PushID(n); - if ((n % 3) != 0) - ImGui::SameLine(); - ImGui::Button(names[n], ImVec2(60, 60)); - - // Our buttons are both drag sources and drag targets here! - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) - { - // Set payload to carry the index of our item (could be anything) - ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int)); - - // Display preview (could be anything, e.g. when dragging an image we could decide to display - // the filename and a small preview of the image, etc.) - if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); } - if (mode == Mode_Move) { ImGui::Text("Move %s", names[n]); } - if (mode == Mode_Swap) { ImGui::Text("Swap %s", names[n]); } - ImGui::EndDragDropSource(); - } - if (ImGui::BeginDragDropTarget()) - { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) - { - IM_ASSERT(payload->DataSize == sizeof(int)); - int payload_n = *(const int*)payload->Data; - if (mode == Mode_Copy) - { - names[n] = names[payload_n]; - } - if (mode == Mode_Move) - { - names[n] = names[payload_n]; - names[payload_n] = ""; - } - if (mode == Mode_Swap) - { - const char* tmp = names[n]; - names[n] = names[payload_n]; - names[payload_n] = tmp; - } - } - ImGui::EndDragDropTarget(); - } - ImGui::PopID(); - } - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)"); - if (ImGui::TreeNode("Drag to reorder items (simple)")) - { - // Simple reordering - HelpMarker( - "We don't use the drag and drop api at all here! " - "Instead we query when the item is held but not hovered, and order items accordingly."); - static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" }; - for (int n = 0; n < IM_ARRAYSIZE(item_names); n++) - { - const char* item = item_names[n]; - ImGui::Selectable(item); - - if (ImGui::IsItemActive() && !ImGui::IsItemHovered()) - { - int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1); - if (n_next >= 0 && n_next < IM_ARRAYSIZE(item_names)) - { - item_names[n] = item_names[n_next]; - item_names[n_next] = item; - ImGui::ResetMouseDragDelta(); - } - } - } - ImGui::TreePop(); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)"); - if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)")) - { - // Select an item type - const char* item_names[] = - { - "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat", - "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox" - }; - static int item_type = 4; - static bool item_disabled = false; - ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names)); - ImGui::SameLine(); - HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered()."); - ImGui::Checkbox("Item Disabled", &item_disabled); - - // Submit selected item item so we can query their status in the code following it. - bool ret = false; - static bool b = false; - static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; - static char str[16] = {}; - if (item_disabled) - ImGui::BeginDisabled(true); - if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction - if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button - if (item_type == 2) { ImGui::PushButtonRepeat(true); ret = ImGui::Button("ITEM: Button"); ImGui::PopButtonRepeat(); } // Testing button (with repeater) - if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox - if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item - if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) - if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window) - if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input - if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 10){ ret = ImGui::Selectable("ITEM: Selectable"); } // Testing selectable item - if (item_type == 11){ ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) - if (item_type == 12){ ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node - if (item_type == 13){ ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. - if (item_type == 14){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items)); } - if (item_type == 15){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } - - // Display the values of IsItemHovered() and other common item state functions. - // Note that the ImGuiHoveredFlags_XXX flags can be combined. - // Because BulletText is an item itself and that would affect the output of IsItemXXX functions, - // we query every state in a single call to avoid storing them and to simplify the code. - ImGui::BulletText( - "Return value = %d\n" - "IsItemFocused() = %d\n" - "IsItemHovered() = %d\n" - "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" - "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" - "IsItemHovered(_AllowWhenOverlapped) = %d\n" - "IsItemHovered(_AllowWhenDisabled) = %d\n" - "IsItemHovered(_RectOnly) = %d\n" - "IsItemActive() = %d\n" - "IsItemEdited() = %d\n" - "IsItemActivated() = %d\n" - "IsItemDeactivated() = %d\n" - "IsItemDeactivatedAfterEdit() = %d\n" - "IsItemVisible() = %d\n" - "IsItemClicked() = %d\n" - "IsItemToggledOpen() = %d\n" - "GetItemRectMin() = (%.1f, %.1f)\n" - "GetItemRectMax() = (%.1f, %.1f)\n" - "GetItemRectSize() = (%.1f, %.1f)", - ret, - ImGui::IsItemFocused(), - ImGui::IsItemHovered(), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled), - ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), - ImGui::IsItemActive(), - ImGui::IsItemEdited(), - ImGui::IsItemActivated(), - ImGui::IsItemDeactivated(), - ImGui::IsItemDeactivatedAfterEdit(), - ImGui::IsItemVisible(), - ImGui::IsItemClicked(), - ImGui::IsItemToggledOpen(), - ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y, - ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y, - ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y - ); - - if (item_disabled) - ImGui::EndDisabled(); - - char buf[1] = ""; - ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly); - ImGui::SameLine(); - HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)"); - if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)")) - { - static bool embed_all_inside_a_child_window = false; - ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); - if (embed_all_inside_a_child_window) - ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true); - - // Testing IsWindowFocused() function with its various flags. - ImGui::BulletText( - "IsWindowFocused() = %d\n" - "IsWindowFocused(_ChildWindows) = %d\n" - "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" - "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" - "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" - "IsWindowFocused(_RootWindow) = %d\n" - "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" - "IsWindowFocused(_AnyWindow) = %d\n", - ImGui::IsWindowFocused(), - ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), - ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy), - ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), - ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), - ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), - ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), - ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); - - // Testing IsWindowHovered() function with its various flags. - ImGui::BulletText( - "IsWindowHovered() = %d\n" - "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" - "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" - "IsWindowHovered(_ChildWindows) = %d\n" - "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" - "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" - "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" - "IsWindowHovered(_RootWindow) = %d\n" - "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" - "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" - "IsWindowHovered(_AnyWindow) = %d\n", - ImGui::IsWindowHovered(), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), - ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); - - ImGui::BeginChild("child", ImVec2(0, 50), true); - ImGui::Text("This is another child window for testing the _ChildWindows flag."); - ImGui::EndChild(); - if (embed_all_inside_a_child_window) - ImGui::EndChild(); - - // Calling IsItemHovered() after begin returns the hovered status of the title bar. - // This is useful in particular if you want to create a context menu associated to the title bar of a window. - static bool test_window = false; - ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window); - if (test_window) - { - ImGui::Begin("Title bar Hovered/Active tests", &test_window); - if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered() - { - if (ImGui::MenuItem("Close")) { test_window = false; } - ImGui::EndPopup(); - } - ImGui::Text( - "IsItemHovered() after begin = %d (== is title bar hovered)\n" - "IsItemActive() after begin = %d (== is window being clicked/moved)\n", - ImGui::IsItemHovered(), ImGui::IsItemActive()); - ImGui::End(); - } - - ImGui::TreePop(); - } - - // Demonstrate BeginDisabled/EndDisabled using a checkbox located at the bottom of the section (which is a bit odd: - // logically we'd have this checkbox at the top of the section, but we don't want this feature to steal that space) - if (disable_all) - ImGui::EndDisabled(); - - IMGUI_DEMO_MARKER("Widgets/Disable Block"); - if (ImGui::TreeNode("Disable block")) - { - ImGui::Checkbox("Disable entire section above", &disable_all); - ImGui::SameLine(); HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across this section."); - ImGui::TreePop(); - } -} - -static void ShowDemoWindowLayout() -{ - IMGUI_DEMO_MARKER("Layout"); - if (!ImGui::CollapsingHeader("Layout & Scrolling")) - return; - - IMGUI_DEMO_MARKER("Layout/Child windows"); - if (ImGui::TreeNode("Child windows")) - { - HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window."); - static bool disable_mouse_wheel = false; - static bool disable_menu = false; - ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); - ImGui::Checkbox("Disable Menu", &disable_menu); - - // Child 1: no border, enable horizontal scrollbar - { - ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; - if (disable_mouse_wheel) - window_flags |= ImGuiWindowFlags_NoScrollWithMouse; - ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), false, window_flags); - for (int i = 0; i < 100; i++) - ImGui::Text("%04d: scrollable region", i); - ImGui::EndChild(); - } - - ImGui::SameLine(); - - // Child 2: rounded border - { - ImGuiWindowFlags window_flags = ImGuiWindowFlags_None; - if (disable_mouse_wheel) - window_flags |= ImGuiWindowFlags_NoScrollWithMouse; - if (!disable_menu) - window_flags |= ImGuiWindowFlags_MenuBar; - ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); - ImGui::BeginChild("ChildR", ImVec2(0, 260), true, window_flags); - if (!disable_menu && ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("Menu")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - if (ImGui::BeginTable("split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings)) - { - for (int i = 0; i < 100; i++) - { - char buf[32]; - sprintf(buf, "%03d", i); - ImGui::TableNextColumn(); - ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f)); - } - ImGui::EndTable(); - } - ImGui::EndChild(); - ImGui::PopStyleVar(); - } - - ImGui::Separator(); - - // Demonstrate a few extra things - // - Changing ImGuiCol_ChildBg (which is transparent black in default styles) - // - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window) - // You can also call SetNextWindowPos() to position the child window. The parent window will effectively - // layout from this position. - // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from - // the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details. - { - static int offset_x = 0; - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); - ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000); - - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x); - ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100)); - ImGui::BeginChild("Red", ImVec2(200, 100), true, ImGuiWindowFlags_None); - for (int n = 0; n < 50; n++) - ImGui::Text("Some test %d", n); - ImGui::EndChild(); - bool child_is_hovered = ImGui::IsItemHovered(); - ImVec2 child_rect_min = ImGui::GetItemRectMin(); - ImVec2 child_rect_max = ImGui::GetItemRectMax(); - ImGui::PopStyleColor(); - ImGui::Text("Hovered: %d", child_is_hovered); - ImGui::Text("Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Layout/Widgets Width"); - if (ImGui::TreeNode("Widgets Width")) - { - static float f = 0.0f; - static bool show_indented_items = true; - ImGui::Checkbox("Show indented items", &show_indented_items); - - // Use SetNextItemWidth() to set the width of a single upcoming item. - // Use PushItemWidth()/PopItemWidth() to set the width of a group of items. - // In real code use you'll probably want to choose width values that are proportional to your font size - // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc. - - ImGui::Text("SetNextItemWidth/PushItemWidth(100)"); - ImGui::SameLine(); HelpMarker("Fixed width."); - ImGui::PushItemWidth(100); - ImGui::DragFloat("float##1b", &f); - if (show_indented_items) - { - ImGui::Indent(); - ImGui::DragFloat("float (indented)##1b", &f); - ImGui::Unindent(); - } - ImGui::PopItemWidth(); - - ImGui::Text("SetNextItemWidth/PushItemWidth(-100)"); - ImGui::SameLine(); HelpMarker("Align to right edge minus 100"); - ImGui::PushItemWidth(-100); - ImGui::DragFloat("float##2a", &f); - if (show_indented_items) - { - ImGui::Indent(); - ImGui::DragFloat("float (indented)##2b", &f); - ImGui::Unindent(); - } - ImGui::PopItemWidth(); - - ImGui::Text("SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)"); - ImGui::SameLine(); HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); - ImGui::DragFloat("float##3a", &f); - if (show_indented_items) - { - ImGui::Indent(); - ImGui::DragFloat("float (indented)##3b", &f); - ImGui::Unindent(); - } - ImGui::PopItemWidth(); - - ImGui::Text("SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)"); - ImGui::SameLine(); HelpMarker("Align to right edge minus half"); - ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f); - ImGui::DragFloat("float##4a", &f); - if (show_indented_items) - { - ImGui::Indent(); - ImGui::DragFloat("float (indented)##4b", &f); - ImGui::Unindent(); - } - ImGui::PopItemWidth(); - - // Demonstrate using PushItemWidth to surround three items. - // Calling SetNextItemWidth() before each of them would have the same effect. - ImGui::Text("SetNextItemWidth/PushItemWidth(-FLT_MIN)"); - ImGui::SameLine(); HelpMarker("Align to right edge"); - ImGui::PushItemWidth(-FLT_MIN); - ImGui::DragFloat("##float5a", &f); - if (show_indented_items) - { - ImGui::Indent(); - ImGui::DragFloat("float (indented)##5b", &f); - ImGui::Unindent(); - } - ImGui::PopItemWidth(); - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout"); - if (ImGui::TreeNode("Basic Horizontal Layout")) - { - ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); - - // Text - IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine"); - ImGui::Text("Two items: Hello"); ImGui::SameLine(); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); - - // Adjust spacing - ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); - - // Button - ImGui::AlignTextToFramePadding(); - ImGui::Text("Normal buttons"); ImGui::SameLine(); - ImGui::Button("Banana"); ImGui::SameLine(); - ImGui::Button("Apple"); ImGui::SameLine(); - ImGui::Button("Corniflower"); - - // Button - ImGui::Text("Small buttons"); ImGui::SameLine(); - ImGui::SmallButton("Like this one"); ImGui::SameLine(); - ImGui::Text("can fit within a text block."); - - // Aligned to arbitrary position. Easy/cheap column. - IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)"); - ImGui::Text("Aligned"); - ImGui::SameLine(150); ImGui::Text("x=150"); - ImGui::SameLine(300); ImGui::Text("x=300"); - ImGui::Text("Aligned"); - ImGui::SameLine(150); ImGui::SmallButton("x=150"); - ImGui::SameLine(300); ImGui::SmallButton("x=300"); - - // Checkbox - IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)"); - static bool c1 = false, c2 = false, c3 = false, c4 = false; - ImGui::Checkbox("My", &c1); ImGui::SameLine(); - ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); - ImGui::Checkbox("Is", &c3); ImGui::SameLine(); - ImGui::Checkbox("Rich", &c4); - - // Various - static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f; - ImGui::PushItemWidth(80); - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; - static int item = -1; - ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); - ImGui::SliderFloat("X", &f0, 0.0f, 5.0f); ImGui::SameLine(); - ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f); ImGui::SameLine(); - ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f); - ImGui::PopItemWidth(); - - ImGui::PushItemWidth(80); - ImGui::Text("Lists:"); - static int selection[4] = { 0, 1, 2, 3 }; - for (int i = 0; i < 4; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); - ImGui::PopID(); - //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); - } - ImGui::PopItemWidth(); - - // Dummy - IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy"); - ImVec2 button_sz(40, 40); - ImGui::Button("A", button_sz); ImGui::SameLine(); - ImGui::Dummy(button_sz); ImGui::SameLine(); - ImGui::Button("B", button_sz); - - // Manually wrapping - // (we should eventually provide this as an automatic layout feature, but for now you can do it manually) - IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping"); - ImGui::Text("Manual wrapping:"); - ImGuiStyle& style = ImGui::GetStyle(); - int buttons_count = 20; - float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; - for (int n = 0; n < buttons_count; n++) - { - ImGui::PushID(n); - ImGui::Button("Box", button_sz); - float last_button_x2 = ImGui::GetItemRectMax().x; - float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line - if (n + 1 < buttons_count && next_button_x2 < window_visible_x2) - ImGui::SameLine(); - ImGui::PopID(); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Layout/Groups"); - if (ImGui::TreeNode("Groups")) - { - HelpMarker( - "BeginGroup() basically locks the horizontal position for new line. " - "EndGroup() bundles the whole group so that you can use \"item\" functions such as " - "IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group."); - ImGui::BeginGroup(); - { - ImGui::BeginGroup(); - ImGui::Button("AAA"); - ImGui::SameLine(); - ImGui::Button("BBB"); - ImGui::SameLine(); - ImGui::BeginGroup(); - ImGui::Button("CCC"); - ImGui::Button("DDD"); - ImGui::EndGroup(); - ImGui::SameLine(); - ImGui::Button("EEE"); - ImGui::EndGroup(); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("First group hovered"); - } - // Capture the group size and create widgets using the same size - ImVec2 size = ImGui::GetItemRectSize(); - const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; - ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); - - ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y)); - ImGui::SameLine(); - ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y)); - ImGui::EndGroup(); - ImGui::SameLine(); - - ImGui::Button("LEVERAGE\nBUZZWORD", size); - ImGui::SameLine(); - - if (ImGui::BeginListBox("List", size)) - { - ImGui::Selectable("Selected", true); - ImGui::Selectable("Not Selected", false); - ImGui::EndListBox(); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment"); - if (ImGui::TreeNode("Text Baseline Alignment")) - { - { - ImGui::BulletText("Text baseline:"); - ImGui::SameLine(); HelpMarker( - "This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. " - "Lines only composed of text or \"small\" widgets use less vertical space than lines with framed widgets."); - ImGui::Indent(); - - ImGui::Text("KO Blahblah"); ImGui::SameLine(); - ImGui::Button("Some framed item"); ImGui::SameLine(); - HelpMarker("Baseline of button will look misaligned with text.."); - - // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets. - // (because we don't know what's coming after the Text() statement, we need to move the text baseline - // down by FramePadding.y ahead of time) - ImGui::AlignTextToFramePadding(); - ImGui::Text("OK Blahblah"); ImGui::SameLine(); - ImGui::Button("Some framed item"); ImGui::SameLine(); - HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y"); - - // SmallButton() uses the same vertical padding as Text - ImGui::Button("TEST##1"); ImGui::SameLine(); - ImGui::Text("TEST"); ImGui::SameLine(); - ImGui::SmallButton("TEST##2"); - - // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets. - ImGui::AlignTextToFramePadding(); - ImGui::Text("Text aligned to framed item"); ImGui::SameLine(); - ImGui::Button("Item##1"); ImGui::SameLine(); - ImGui::Text("Item"); ImGui::SameLine(); - ImGui::SmallButton("Item##2"); ImGui::SameLine(); - ImGui::Button("Item##3"); - - ImGui::Unindent(); - } - - ImGui::Spacing(); - - { - ImGui::BulletText("Multi-line text:"); - ImGui::Indent(); - ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Text("Banana"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("One\nTwo\nThree"); - - ImGui::Button("HOP##1"); ImGui::SameLine(); - ImGui::Text("Banana"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Button("HOP##2"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - ImGui::Unindent(); - } - - ImGui::Spacing(); - - { - ImGui::BulletText("Misc items:"); - ImGui::Indent(); - - // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button. - ImGui::Button("80x80", ImVec2(80, 80)); - ImGui::SameLine(); - ImGui::Button("50x50", ImVec2(50, 50)); - ImGui::SameLine(); - ImGui::Button("Button()"); - ImGui::SameLine(); - ImGui::SmallButton("SmallButton()"); - - // Tree - const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - ImGui::Button("Button##1"); - ImGui::SameLine(0.0f, spacing); - if (ImGui::TreeNode("Node##1")) - { - // Placeholder tree data - for (int i = 0; i < 6; i++) - ImGui::BulletText("Item %d..", i); - ImGui::TreePop(); - } - - // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. - // Otherwise you can use SmallButton() (smaller fit). - ImGui::AlignTextToFramePadding(); - - // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add - // other contents below the node. - bool node_open = ImGui::TreeNode("Node##2"); - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2"); - if (node_open) - { - // Placeholder tree data - for (int i = 0; i < 6; i++) - ImGui::BulletText("Item %d..", i); - ImGui::TreePop(); - } - - // Bullet - ImGui::Button("Button##3"); - ImGui::SameLine(0.0f, spacing); - ImGui::BulletText("Bullet text"); - - ImGui::AlignTextToFramePadding(); - ImGui::BulletText("Node"); - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4"); - ImGui::Unindent(); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Layout/Scrolling"); - if (ImGui::TreeNode("Scrolling")) - { - // Vertical scroll functions - IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical"); - HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position."); - - static int track_item = 50; - static bool enable_track = true; - static bool enable_extra_decorations = false; - static float scroll_to_off_px = 0.0f; - static float scroll_to_pos_px = 200.0f; - - ImGui::Checkbox("Decoration", &enable_extra_decorations); - - ImGui::Checkbox("Track", &enable_track); - ImGui::PushItemWidth(100); - ImGui::SameLine(140); enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d"); - - bool scroll_to_off = ImGui::Button("Scroll Offset"); - ImGui::SameLine(140); scroll_to_off |= ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px"); - - bool scroll_to_pos = ImGui::Button("Scroll To Pos"); - ImGui::SameLine(140); scroll_to_pos |= ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px"); - ImGui::PopItemWidth(); - - if (scroll_to_off || scroll_to_pos) - enable_track = false; - - ImGuiStyle& style = ImGui::GetStyle(); - float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5; - if (child_w < 1.0f) - child_w = 1.0f; - ImGui::PushID("##VerticalScrolling"); - for (int i = 0; i < 5; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::BeginGroup(); - const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" }; - ImGui::TextUnformatted(names[i]); - - const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0; - const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); - const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), true, child_flags); - if (ImGui::BeginMenuBar()) - { - ImGui::TextUnformatted("abc"); - ImGui::EndMenuBar(); - } - if (scroll_to_off) - ImGui::SetScrollY(scroll_to_off_px); - if (scroll_to_pos) - ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f); - if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items - { - for (int item = 0; item < 100; item++) - { - if (enable_track && item == track_item) - { - ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item); - ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom - } - else - { - ImGui::Text("Item %d", item); - } - } - } - float scroll_y = ImGui::GetScrollY(); - float scroll_max_y = ImGui::GetScrollMaxY(); - ImGui::EndChild(); - ImGui::Text("%.0f/%.0f", scroll_y, scroll_max_y); - ImGui::EndGroup(); - } - ImGui::PopID(); - - // Horizontal scroll functions - IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal"); - ImGui::Spacing(); - HelpMarker( - "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n" - "Because the clipping rectangle of most window hides half worth of WindowPadding on the " - "left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the " - "equivalent SetScrollFromPosY(+1) wouldn't."); - ImGui::PushID("##HorizontalScrolling"); - for (int i = 0; i < 5; i++) - { - float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f; - ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0); - ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); - bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), true, child_flags); - if (scroll_to_off) - ImGui::SetScrollX(scroll_to_off_px); - if (scroll_to_pos) - ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f); - if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items - { - for (int item = 0; item < 100; item++) - { - if (item > 0) - ImGui::SameLine(); - if (enable_track && item == track_item) - { - ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item); - ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right - } - else - { - ImGui::Text("Item %d", item); - } - } - } - float scroll_x = ImGui::GetScrollX(); - float scroll_max_x = ImGui::GetScrollMaxX(); - ImGui::EndChild(); - ImGui::SameLine(); - const char* names[] = { "Left", "25%", "Center", "75%", "Right" }; - ImGui::Text("%s\n%.0f/%.0f", names[i], scroll_x, scroll_max_x); - ImGui::Spacing(); - } - ImGui::PopID(); - - // Miscellaneous Horizontal Scrolling Demo - IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)"); - HelpMarker( - "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n" - "You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin()."); - static int lines = 7; - ImGui::SliderInt("Lines", &lines, 1, 15); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); - ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30); - ImGui::BeginChild("scrolling", scrolling_child_size, true, ImGuiWindowFlags_HorizontalScrollbar); - for (int line = 0; line < lines; line++) - { - // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine() - // If you want to create your own time line for a real application you may be better off manipulating - // the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets - // yourself. You may also want to use the lower-level ImDrawList API. - int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3); - for (int n = 0; n < num_buttons; n++) - { - if (n > 0) ImGui::SameLine(); - ImGui::PushID(n + line * 1000); - char num_buf[16]; - sprintf(num_buf, "%d", n); - const char* label = (!(n % 15)) ? "FizzBuzz" : (!(n % 3)) ? "Fizz" : (!(n % 5)) ? "Buzz" : num_buf; - float hue = n * 0.05f; - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f)); - ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f)); - ImGui::PopStyleColor(3); - ImGui::PopID(); - } - } - float scroll_x = ImGui::GetScrollX(); - float scroll_max_x = ImGui::GetScrollMaxX(); - ImGui::EndChild(); - ImGui::PopStyleVar(2); - float scroll_x_delta = 0.0f; - ImGui::SmallButton("<<"); - if (ImGui::IsItemActive()) - scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; - ImGui::SameLine(); - ImGui::Text("Scroll from code"); ImGui::SameLine(); - ImGui::SmallButton(">>"); - if (ImGui::IsItemActive()) - scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; - ImGui::SameLine(); - ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x); - if (scroll_x_delta != 0.0f) - { - // Demonstrate a trick: you can use Begin to set yourself in the context of another window - // (here we are already out of your child window) - ImGui::BeginChild("scrolling"); - ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta); - ImGui::EndChild(); - } - ImGui::Spacing(); - - static bool show_horizontal_contents_size_demo_window = false; - ImGui::Checkbox("Show Horizontal contents size demo window", &show_horizontal_contents_size_demo_window); - - if (show_horizontal_contents_size_demo_window) - { - static bool show_h_scrollbar = true; - static bool show_button = true; - static bool show_tree_nodes = true; - static bool show_text_wrapped = false; - static bool show_columns = true; - static bool show_tab_bar = true; - static bool show_child = false; - static bool explicit_content_size = false; - static float contents_size_x = 300.0f; - if (explicit_content_size) - ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f)); - ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0); - IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window"); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0)); - HelpMarker("Test of different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\nUse 'Metrics->Tools->Show windows rectangles' to visualize rectangles."); - ImGui::Checkbox("H-scrollbar", &show_h_scrollbar); - ImGui::Checkbox("Button", &show_button); // Will grow contents size (unless explicitly overwritten) - ImGui::Checkbox("Tree nodes", &show_tree_nodes); // Will grow contents size and display highlight over full width - ImGui::Checkbox("Text wrapped", &show_text_wrapped);// Will grow and use contents size - ImGui::Checkbox("Columns", &show_columns); // Will use contents size - ImGui::Checkbox("Tab bar", &show_tab_bar); // Will use contents size - ImGui::Checkbox("Child", &show_child); // Will grow and use contents size - ImGui::Checkbox("Explicit content size", &explicit_content_size); - ImGui::Text("Scroll %.1f/%.1f %.1f/%.1f", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY()); - if (explicit_content_size) - { - ImGui::SameLine(); - ImGui::SetNextItemWidth(100); - ImGui::DragFloat("##csx", &contents_size_x); - ImVec2 p = ImGui::GetCursorScreenPos(); - ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE); - ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y), ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE); - ImGui::Dummy(ImVec2(0, 10)); - } - ImGui::PopStyleVar(2); - ImGui::Separator(); - if (show_button) - { - ImGui::Button("this is a 300-wide button", ImVec2(300, 0)); - } - if (show_tree_nodes) - { - bool open = true; - if (ImGui::TreeNode("this is a tree node")) - { - if (ImGui::TreeNode("another one of those tree node...")) - { - ImGui::Text("Some tree contents"); - ImGui::TreePop(); - } - ImGui::TreePop(); - } - ImGui::CollapsingHeader("CollapsingHeader", &open); - } - if (show_text_wrapped) - { - ImGui::TextWrapped("This text should automatically wrap on the edge of the work rectangle."); - } - if (show_columns) - { - ImGui::Text("Tables:"); - if (ImGui::BeginTable("table", 4, ImGuiTableFlags_Borders)) - { - for (int n = 0; n < 4; n++) - { - ImGui::TableNextColumn(); - ImGui::Text("Width %.2f", ImGui::GetContentRegionAvail().x); - } - ImGui::EndTable(); - } - ImGui::Text("Columns:"); - ImGui::Columns(4); - for (int n = 0; n < 4; n++) - { - ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); - ImGui::NextColumn(); - } - ImGui::Columns(1); - } - if (show_tab_bar && ImGui::BeginTabBar("Hello")) - { - if (ImGui::BeginTabItem("OneOneOne")) { ImGui::EndTabItem(); } - if (ImGui::BeginTabItem("TwoTwoTwo")) { ImGui::EndTabItem(); } - if (ImGui::BeginTabItem("ThreeThreeThree")) { ImGui::EndTabItem(); } - if (ImGui::BeginTabItem("FourFourFour")) { ImGui::EndTabItem(); } - ImGui::EndTabBar(); - } - if (show_child) - { - ImGui::BeginChild("child", ImVec2(0, 0), true); - ImGui::EndChild(); - } - ImGui::End(); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Layout/Clipping"); - if (ImGui::TreeNode("Clipping")) - { - static ImVec2 size(100.0f, 100.0f); - static ImVec2 offset(30.0f, 30.0f); - ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f"); - ImGui::TextWrapped("(Click and drag to scroll)"); - - HelpMarker( - "(Left) Using ImGui::PushClipRect():\n" - "Will alter ImGui hit-testing logic + ImDrawList rendering.\n" - "(use this if you want your clipping rectangle to affect interactions)\n\n" - "(Center) Using ImDrawList::PushClipRect():\n" - "Will alter ImDrawList rendering only.\n" - "(use this as a shortcut if you are only using ImDrawList calls)\n\n" - "(Right) Using ImDrawList::AddText() with a fine ClipRect:\n" - "Will alter only this specific ImDrawList::AddText() rendering.\n" - "This is often used internally to avoid altering the clipping rectangle and minimize draw calls."); - - for (int n = 0; n < 3; n++) - { - if (n > 0) - ImGui::SameLine(); - - ImGui::PushID(n); - ImGui::InvisibleButton("##canvas", size); - if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) - { - offset.x += ImGui::GetIO().MouseDelta.x; - offset.y += ImGui::GetIO().MouseDelta.y; - } - ImGui::PopID(); - if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped. - continue; - - const ImVec2 p0 = ImGui::GetItemRectMin(); - const ImVec2 p1 = ImGui::GetItemRectMax(); - const char* text_str = "Line 1 hello\nLine 2 clip me!"; - const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - switch (n) - { - case 0: - ImGui::PushClipRect(p0, p1, true); - draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); - draw_list->AddText(text_pos, IM_COL32_WHITE, text_str); - ImGui::PopClipRect(); - break; - case 1: - draw_list->PushClipRect(p0, p1, true); - draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); - draw_list->AddText(text_pos, IM_COL32_WHITE, text_str); - draw_list->PopClipRect(); - break; - case 2: - ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert. - draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); - draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect); - break; - } - } - - ImGui::TreePop(); - } -} - -static void ShowDemoWindowPopups() -{ - IMGUI_DEMO_MARKER("Popups"); - if (!ImGui::CollapsingHeader("Popups & Modal windows")) - return; - - // The properties of popups windows are: - // - They block normal mouse hovering detection outside them. (*) - // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE. - // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as - // we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup(). - // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even - // when normally blocked by a popup. - // Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close - // popups at any time. - - // Typical use for regular windows: - // bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End(); - // Typical use for popups: - // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); } - - // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state. - // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. - - IMGUI_DEMO_MARKER("Popups/Popups"); - if (ImGui::TreeNode("Popups")) - { - ImGui::TextWrapped( - "When a popup is active, it inhibits interacting with windows that are behind the popup. " - "Clicking outside the popup closes it."); - - static int selected_fish = -1; - const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" }; - static bool toggles[] = { true, false, false, false, false }; - - // Simple selection popup (if you want to show the current selection inside the Button itself, - // you may want to build a string using the "###" operator to preserve a constant ID with a variable label) - if (ImGui::Button("Select..")) - ImGui::OpenPopup("my_select_popup"); - ImGui::SameLine(); - ImGui::TextUnformatted(selected_fish == -1 ? "" : names[selected_fish]); - if (ImGui::BeginPopup("my_select_popup")) - { - ImGui::Text("Aquarium"); - ImGui::Separator(); - for (int i = 0; i < IM_ARRAYSIZE(names); i++) - if (ImGui::Selectable(names[i])) - selected_fish = i; - ImGui::EndPopup(); - } - - // Showing a menu with toggles - if (ImGui::Button("Toggle..")) - ImGui::OpenPopup("my_toggle_popup"); - if (ImGui::BeginPopup("my_toggle_popup")) - { - for (int i = 0; i < IM_ARRAYSIZE(names); i++) - ImGui::MenuItem(names[i], "", &toggles[i]); - if (ImGui::BeginMenu("Sub-menu")) - { - ImGui::MenuItem("Click me"); - ImGui::EndMenu(); - } - - ImGui::Separator(); - ImGui::Text("Tooltip here"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip over a popup"); - - if (ImGui::Button("Stacked Popup")) - ImGui::OpenPopup("another popup"); - if (ImGui::BeginPopup("another popup")) - { - for (int i = 0; i < IM_ARRAYSIZE(names); i++) - ImGui::MenuItem(names[i], "", &toggles[i]); - if (ImGui::BeginMenu("Sub-menu")) - { - ImGui::MenuItem("Click me"); - if (ImGui::Button("Stacked Popup")) - ImGui::OpenPopup("another popup"); - if (ImGui::BeginPopup("another popup")) - { - ImGui::Text("I am the last one here."); - ImGui::EndPopup(); - } - ImGui::EndMenu(); - } - ImGui::EndPopup(); - } - ImGui::EndPopup(); - } - - // Call the more complete ShowExampleMenuFile which we use in various places of this demo - if (ImGui::Button("With a menu..")) - ImGui::OpenPopup("my_file_popup"); - if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar)) - { - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("File")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Edit")) - { - ImGui::MenuItem("Dummy"); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - ImGui::Text("Hello from popup!"); - ImGui::Button("This is a dummy button.."); - ImGui::EndPopup(); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Popups/Context menus"); - if (ImGui::TreeNode("Context menus")) - { - HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier."); - - // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing: - // if (id == 0) - // id = GetItemID(); // Use last item id - // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) - // OpenPopup(id); - // return BeginPopup(id); - // For advanced advanced uses you may want to replicate and customize this code. - // See more details in BeginPopupContextItem(). - - // Example 1 - // When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(), - // and BeginPopupContextItem() will use the last item ID as the popup ID. - { - const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" }; - for (int n = 0; n < 5; n++) - { - ImGui::Selectable(names[n]); - if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id - { - ImGui::Text("This a popup for \"%s\"!", names[n]); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Right-click to open popup"); - } - } - - // Example 2 - // Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem(). - // Using an explicit identifier is also convenient if you want to activate the popups from different locations. - { - HelpMarker("Text() elements don't have stable identifiers so we need to provide one."); - static float value = 0.5f; - ImGui::Text("Value = %.3f <-- (1) right-click this text", value); - if (ImGui::BeginPopupContextItem("my popup")) - { - if (ImGui::Selectable("Set to zero")) value = 0.0f; - if (ImGui::Selectable("Set to PI")) value = 3.1415f; - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f); - ImGui::EndPopup(); - } - - // We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup. - // Here we make it that right-clicking this other text element opens the same popup as above. - // The popup itself will be submitted by the code above. - ImGui::Text("(2) Or right-click this text"); - ImGui::OpenPopupOnItemClick("my popup", ImGuiPopupFlags_MouseButtonRight); - - // Back to square one: manually open the same popup. - if (ImGui::Button("(3) Or click this button")) - ImGui::OpenPopup("my popup"); - } - - // Example 3 - // When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID), - // we need to make sure your item identifier is stable. - // In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ). - { - HelpMarker("Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator."); - static char name[32] = "Label1"; - char buf[64]; - sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label - ImGui::Button(buf); - if (ImGui::BeginPopupContextItem()) - { - ImGui::Text("Edit name:"); - ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Popups/Modals"); - if (ImGui::TreeNode("Modals")) - { - ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside."); - - if (ImGui::Button("Delete..")) - ImGui::OpenPopup("Delete?"); - - // Always center this window when appearing - ImVec2 center = ImGui::GetMainViewport()->GetCenter(); - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) - { - ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); - ImGui::Separator(); - - //static int unused_i = 0; - //ImGui::Combo("Combo", &unused_i, "Delete\0Delete harder\0"); - - static bool dont_ask_me_next_time = false; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time); - ImGui::PopStyleVar(); - - if (ImGui::Button("OK", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } - ImGui::SetItemDefaultFocus(); - ImGui::SameLine(); - if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } - ImGui::EndPopup(); - } - - if (ImGui::Button("Stacked modals..")) - ImGui::OpenPopup("Stacked 1"); - if (ImGui::BeginPopupModal("Stacked 1", NULL, ImGuiWindowFlags_MenuBar)) - { - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("File")) - { - if (ImGui::MenuItem("Some menu item")) {} - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it."); - - // Testing behavior of widgets stacking their own regular popups over the modal. - static int item = 1; - static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; - ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); - ImGui::ColorEdit4("color", color); - - if (ImGui::Button("Add another modal..")) - ImGui::OpenPopup("Stacked 2"); - - // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which - // will close the popup. Note that the visibility state of popups is owned by imgui, so the input value - // of the bool actually doesn't matter here. - bool unused_open = true; - if (ImGui::BeginPopupModal("Stacked 2", &unused_open)) - { - ImGui::Text("Hello from Stacked The Second!"); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Popups/Menus inside a regular window"); - if (ImGui::TreeNode("Menus inside a regular window")) - { - ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); - ImGui::Separator(); - - ImGui::MenuItem("Menu item", "CTRL+M"); - if (ImGui::BeginMenu("Menu inside a regular window")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - ImGui::Separator(); - ImGui::TreePop(); - } -} - -// Dummy data structure that we use for the Table demo. -// (pre-C++11 doesn't allow us to instantiate ImVector template if this structure if defined inside the demo function) -namespace -{ -// We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code. -// This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID. -// But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex) -// If you don't use sorting, you will generally never care about giving column an ID! -enum MyItemColumnID -{ - MyItemColumnID_ID, - MyItemColumnID_Name, - MyItemColumnID_Action, - MyItemColumnID_Quantity, - MyItemColumnID_Description -}; - -struct MyItem -{ - int ID; - const char* Name; - int Quantity; - - // We have a problem which is affecting _only this demo_ and should not affect your code: - // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(), - // however qsort doesn't allow passing user data to comparing function. - // As a workaround, we are storing the sort specs in a static/global for the comparing function to access. - // In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global. - // We could technically call ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called - // very often by the sorting algorithm it would be a little wasteful. - static const ImGuiTableSortSpecs* s_current_sort_specs; - - // Compare function to be used by qsort() - static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs) - { - const MyItem* a = (const MyItem*)lhs; - const MyItem* b = (const MyItem*)rhs; - for (int n = 0; n < s_current_sort_specs->SpecsCount; n++) - { - // Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn() - // We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler! - const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n]; - int delta = 0; - switch (sort_spec->ColumnUserID) - { - case MyItemColumnID_ID: delta = (a->ID - b->ID); break; - case MyItemColumnID_Name: delta = (strcmp(a->Name, b->Name)); break; - case MyItemColumnID_Quantity: delta = (a->Quantity - b->Quantity); break; - case MyItemColumnID_Description: delta = (strcmp(a->Name, b->Name)); break; - default: IM_ASSERT(0); break; - } - if (delta > 0) - return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1; - if (delta < 0) - return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1; - } - - // qsort() is instable so always return a way to differenciate items. - // Your own compare function may want to avoid fallback on implicit sort specs e.g. a Name compare if it wasn't already part of the sort specs. - return (a->ID - b->ID); - } -}; -const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL; -} - -// Make the UI compact because there are so many fields -static void PushStyleCompact() -{ - ImGuiStyle& style = ImGui::GetStyle(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, (float)(int)(style.FramePadding.y * 0.60f))); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, (float)(int)(style.ItemSpacing.y * 0.60f))); -} - -static void PopStyleCompact() -{ - ImGui::PopStyleVar(2); -} - -// Show a combo box with a choice of sizing policies -static void EditTableSizingFlags(ImGuiTableFlags* p_flags) -{ - struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; }; - static const EnumDesc policies[] = - { - { ImGuiTableFlags_None, "Default", "Use default sizing policy:\n- ImGuiTableFlags_SizingFixedFit if ScrollX is on or if host window has ImGuiWindowFlags_AlwaysAutoResize.\n- ImGuiTableFlags_SizingStretchSame otherwise." }, - { ImGuiTableFlags_SizingFixedFit, "ImGuiTableFlags_SizingFixedFit", "Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width." }, - { ImGuiTableFlags_SizingFixedSame, "ImGuiTableFlags_SizingFixedSame", "Columns are all the same width, matching the maximum contents width.\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible." }, - { ImGuiTableFlags_SizingStretchProp, "ImGuiTableFlags_SizingStretchProp", "Columns default to _WidthStretch with weights proportional to their widths." }, - { ImGuiTableFlags_SizingStretchSame, "ImGuiTableFlags_SizingStretchSame", "Columns default to _WidthStretch with same weights." } - }; - int idx; - for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++) - if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_)) - break; - const char* preview_text = (idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : ""; - if (ImGui::BeginCombo("Sizing Policy", preview_text)) - { - for (int n = 0; n < IM_ARRAYSIZE(policies); n++) - if (ImGui::Selectable(policies[n].Name, idx == n)) - *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value; - ImGui::EndCombo(); - } - ImGui::SameLine(); - ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f); - for (int m = 0; m < IM_ARRAYSIZE(policies); m++) - { - ImGui::Separator(); - ImGui::Text("%s:", policies[m].Name); - ImGui::Separator(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f); - ImGui::TextUnformatted(policies[m].Tooltip); - } - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } -} - -static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags) -{ - ImGui::CheckboxFlags("_Disabled", p_flags, ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker("Master disable flag (also hide from context menu)"); - ImGui::CheckboxFlags("_DefaultHide", p_flags, ImGuiTableColumnFlags_DefaultHide); - ImGui::CheckboxFlags("_DefaultSort", p_flags, ImGuiTableColumnFlags_DefaultSort); - if (ImGui::CheckboxFlags("_WidthStretch", p_flags, ImGuiTableColumnFlags_WidthStretch)) - *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch); - if (ImGui::CheckboxFlags("_WidthFixed", p_flags, ImGuiTableColumnFlags_WidthFixed)) - *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed); - ImGui::CheckboxFlags("_NoResize", p_flags, ImGuiTableColumnFlags_NoResize); - ImGui::CheckboxFlags("_NoReorder", p_flags, ImGuiTableColumnFlags_NoReorder); - ImGui::CheckboxFlags("_NoHide", p_flags, ImGuiTableColumnFlags_NoHide); - ImGui::CheckboxFlags("_NoClip", p_flags, ImGuiTableColumnFlags_NoClip); - ImGui::CheckboxFlags("_NoSort", p_flags, ImGuiTableColumnFlags_NoSort); - ImGui::CheckboxFlags("_NoSortAscending", p_flags, ImGuiTableColumnFlags_NoSortAscending); - ImGui::CheckboxFlags("_NoSortDescending", p_flags, ImGuiTableColumnFlags_NoSortDescending); - ImGui::CheckboxFlags("_NoHeaderLabel", p_flags, ImGuiTableColumnFlags_NoHeaderLabel); - ImGui::CheckboxFlags("_NoHeaderWidth", p_flags, ImGuiTableColumnFlags_NoHeaderWidth); - ImGui::CheckboxFlags("_PreferSortAscending", p_flags, ImGuiTableColumnFlags_PreferSortAscending); - ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending); - ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0"); - ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0"); -} - -static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags) -{ - ImGui::CheckboxFlags("_IsEnabled", &flags, ImGuiTableColumnFlags_IsEnabled); - ImGui::CheckboxFlags("_IsVisible", &flags, ImGuiTableColumnFlags_IsVisible); - ImGui::CheckboxFlags("_IsSorted", &flags, ImGuiTableColumnFlags_IsSorted); - ImGui::CheckboxFlags("_IsHovered", &flags, ImGuiTableColumnFlags_IsHovered); -} - -static void ShowDemoWindowTables() -{ - //ImGui::SetNextItemOpen(true, ImGuiCond_Once); - IMGUI_DEMO_MARKER("Tables"); - if (!ImGui::CollapsingHeader("Tables & Columns")) - return; - - // Using those as a base value to create width/height that are factor of the size of our font - const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; - const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); - - ImGui::PushID("Tables"); - - int open_action = -1; - if (ImGui::Button("Open all")) - open_action = 1; - ImGui::SameLine(); - if (ImGui::Button("Close all")) - open_action = 0; - ImGui::SameLine(); - - // Options - static bool disable_indent = false; - ImGui::Checkbox("Disable tree indentation", &disable_indent); - ImGui::SameLine(); - HelpMarker("Disable the indenting of tree nodes so demo tables can use the full window width."); - ImGui::Separator(); - if (disable_indent) - ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f); - - // About Styling of tables - // Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs. - // There are however a few settings that a shared and part of the ImGuiStyle structure: - // style.CellPadding // Padding within each cell - // style.Colors[ImGuiCol_TableHeaderBg] // Table header background - // style.Colors[ImGuiCol_TableBorderStrong] // Table outer and header borders - // style.Colors[ImGuiCol_TableBorderLight] // Table inner borders - // style.Colors[ImGuiCol_TableRowBg] // Table row background when ImGuiTableFlags_RowBg is enabled (even rows) - // style.Colors[ImGuiCol_TableRowBgAlt] // Table row background when ImGuiTableFlags_RowBg is enabled (odds rows) - - // Demos - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Basic"); - if (ImGui::TreeNode("Basic")) - { - // Here we will showcase three different ways to output a table. - // They are very simple variations of a same thing! - - // [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column. - // In many situations, this is the most flexible and easy to use pattern. - HelpMarker("Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop."); - if (ImGui::BeginTable("table1", 3)) - { - for (int row = 0; row < 4; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("Row %d Column %d", row, column); - } - } - ImGui::EndTable(); - } - - // [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex(). - // This is generally more convenient when you have code manually submitting the contents of each columns. - HelpMarker("Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually."); - if (ImGui::BeginTable("table2", 3)) - { - for (int row = 0; row < 4; row++) - { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Row %d", row); - ImGui::TableNextColumn(); - ImGui::Text("Some contents"); - ImGui::TableNextColumn(); - ImGui::Text("123.456"); - } - ImGui::EndTable(); - } - - // [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(), - // as TableNextColumn() will automatically wrap around and create new roes as needed. - // This is generally more convenient when your cells all contains the same type of data. - HelpMarker( - "Only using TableNextColumn(), which tends to be convenient for tables where every cells contains the same type of contents.\n" - "This is also more similar to the old NextColumn() function of the Columns API, and provided to facilitate the Columns->Tables API transition."); - if (ImGui::BeginTable("table3", 3)) - { - for (int item = 0; item < 14; item++) - { - ImGui::TableNextColumn(); - ImGui::Text("Item %d", item); - } - ImGui::EndTable(); - } - - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Borders, background"); - if (ImGui::TreeNode("Borders, background")) - { - // Expose a few Borders related flags interactively - enum ContentsType { CT_Text, CT_FillButton }; - static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; - static bool display_headers = false; - static int contents_type = CT_Text; - - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); - ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders); - ImGui::SameLine(); HelpMarker("ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterH"); - ImGui::Indent(); - - ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH); - ImGui::Indent(); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH); - ImGui::Unindent(); - - ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); - ImGui::Indent(); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV); - ImGui::Unindent(); - - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags, ImGuiTableFlags_BordersOuter); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags, ImGuiTableFlags_BordersInner); - ImGui::Unindent(); - - ImGui::AlignTextToFramePadding(); ImGui::Text("Cell contents:"); - ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text); - ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton); - ImGui::Checkbox("Display headers", &display_headers); - ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appears in Headers"); - PopStyleCompact(); - - if (ImGui::BeginTable("table1", 3, flags)) - { - // Display headers so we can inspect their interaction with borders. - // (Headers are not the main purpose of this section of the demo, so we are not elaborating on them too much. See other sections for details) - if (display_headers) - { - ImGui::TableSetupColumn("One"); - ImGui::TableSetupColumn("Two"); - ImGui::TableSetupColumn("Three"); - ImGui::TableHeadersRow(); - } - - for (int row = 0; row < 5; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - char buf[32]; - sprintf(buf, "Hello %d,%d", column, row); - if (contents_type == CT_Text) - ImGui::TextUnformatted(buf); - else if (contents_type == CT_FillButton) - ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f)); - } - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Resizable, stretch"); - if (ImGui::TreeNode("Resizable, stretch")) - { - // By default, if we don't enable ScrollX the sizing policy for each columns is "Stretch" - // Each columns maintain a sizing weight, and they will occupy all available width. - static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); - ImGui::SameLine(); HelpMarker("Using the _Resizable flag automatically enables the _BordersInnerV flag as well, this is why the resize borders are still showing when unchecking this."); - PopStyleCompact(); - - if (ImGui::BeginTable("table1", 3, flags)) - { - for (int row = 0; row < 5; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("Hello %d,%d", column, row); - } - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Resizable, fixed"); - if (ImGui::TreeNode("Resizable, fixed")) - { - // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set) - // So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available width (unless table is small) - // If there is not enough available width to fit all columns, they will however be resized down. - // FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings - HelpMarker( - "Using _Resizable + _SizingFixedFit flags.\n" - "Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n" - "Double-click a column border to auto-fit the column to its contents."); - PushStyleCompact(); - static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; - ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); - PopStyleCompact(); - - if (ImGui::BeginTable("table1", 3, flags)) - { - for (int row = 0; row < 5; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("Hello %d,%d", column, row); - } - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Resizable, mixed"); - if (ImGui::TreeNode("Resizable, mixed")) - { - HelpMarker( - "Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n" - "When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch."); - static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; - - if (ImGui::BeginTable("table1", 3, flags)) - { - ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableHeadersRow(); - for (int row = 0; row < 5; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("%s %d,%d", (column == 2) ? "Stretch" : "Fixed", column, row); - } - } - ImGui::EndTable(); - } - if (ImGui::BeginTable("table2", 6, flags)) - { - ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide); - ImGui::TableSetupColumn("DDD", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("EEE", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("FFF", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide); - ImGui::TableHeadersRow(); - for (int row = 0; row < 5; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 6; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("%s %d,%d", (column >= 3) ? "Stretch" : "Fixed", column, row); - } - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers"); - if (ImGui::TreeNode("Reorderable, hideable, with headers")) - { - HelpMarker( - "Click and drag column headers to reorder columns.\n\n" - "Right-click on a header to open a context menu."); - static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); - ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable); - ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable); - ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); - ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers)"); - PopStyleCompact(); - - if (ImGui::BeginTable("table1", 3, flags)) - { - // Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column. - // (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.) - ImGui::TableSetupColumn("One"); - ImGui::TableSetupColumn("Two"); - ImGui::TableSetupColumn("Three"); - ImGui::TableHeadersRow(); - for (int row = 0; row < 6; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("Hello %d,%d", column, row); - } - } - ImGui::EndTable(); - } - - // Use outer_size.x == 0.0f instead of default to make the table as tight as possible (only valid when no scrolling and no stretch column) - if (ImGui::BeginTable("table2", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f))) - { - ImGui::TableSetupColumn("One"); - ImGui::TableSetupColumn("Two"); - ImGui::TableSetupColumn("Three"); - ImGui::TableHeadersRow(); - for (int row = 0; row < 6; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("Fixed %d,%d", column, row); - } - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Padding"); - if (ImGui::TreeNode("Padding")) - { - // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding. - // We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding. - HelpMarker( - "We often want outer padding activated when any using features which makes the edges of a column visible:\n" - "e.g.:\n" - "- BorderOuterV\n" - "- any form of row selection\n" - "Because of this, activating BorderOuterV sets the default to PadOuterX. Using PadOuterX or NoPadOuterX you can override the default.\n\n" - "Actual padding values are using style.CellPadding.\n\n" - "In this demo we don't show horizontal borders to emphasis how they don't affect default horizontal padding."); - - static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV; - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags1, ImGuiTableFlags_PadOuterX); - ImGui::SameLine(); HelpMarker("Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)"); - ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags1, ImGuiTableFlags_NoPadOuterX); - ImGui::SameLine(); HelpMarker("Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)"); - ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags1, ImGuiTableFlags_NoPadInnerX); - ImGui::SameLine(); HelpMarker("Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)"); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags1, ImGuiTableFlags_BordersOuterV); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags1, ImGuiTableFlags_BordersInnerV); - static bool show_headers = false; - ImGui::Checkbox("show_headers", &show_headers); - PopStyleCompact(); - - if (ImGui::BeginTable("table_padding", 3, flags1)) - { - if (show_headers) - { - ImGui::TableSetupColumn("One"); - ImGui::TableSetupColumn("Two"); - ImGui::TableSetupColumn("Three"); - ImGui::TableHeadersRow(); - } - - for (int row = 0; row < 5; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - if (row == 0) - { - ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x); - } - else - { - char buf[32]; - sprintf(buf, "Hello %d,%d", column, row); - ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f)); - } - //if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) - // ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255)); - } - } - ImGui::EndTable(); - } - - // Second example: set style.CellPadding to (0.0) or a custom value. - // FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one... - HelpMarker("Setting style.CellPadding to (0,0) or a custom value."); - static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; - static ImVec2 cell_padding(0.0f, 0.0f); - static bool show_widget_frame_bg = true; - - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags2, ImGuiTableFlags_Borders); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags2, ImGuiTableFlags_BordersH); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags2, ImGuiTableFlags_BordersV); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags2, ImGuiTableFlags_BordersInner); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags2, ImGuiTableFlags_BordersOuter); - ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags2, ImGuiTableFlags_RowBg); - ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags2, ImGuiTableFlags_Resizable); - ImGui::Checkbox("show_widget_frame_bg", &show_widget_frame_bg); - ImGui::SliderFloat2("CellPadding", &cell_padding.x, 0.0f, 10.0f, "%.0f"); - PopStyleCompact(); - - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding); - if (ImGui::BeginTable("table_padding_2", 3, flags2)) - { - static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells - static bool init = true; - if (!show_widget_frame_bg) - ImGui::PushStyleColor(ImGuiCol_FrameBg, 0); - for (int cell = 0; cell < 3 * 5; cell++) - { - ImGui::TableNextColumn(); - if (init) - strcpy(text_bufs[cell], "edit me"); - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::PushID(cell); - ImGui::InputText("##cell", text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell])); - ImGui::PopID(); - } - if (!show_widget_frame_bg) - ImGui::PopStyleColor(); - init = false; - ImGui::EndTable(); - } - ImGui::PopStyleVar(); - - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Explicit widths"); - if (ImGui::TreeNode("Sizing policies")) - { - static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable); - ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags1, ImGuiTableFlags_NoHostExtendX); - PopStyleCompact(); - - static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame }; - for (int table_n = 0; table_n < 4; table_n++) - { - ImGui::PushID(table_n); - ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30); - EditTableSizingFlags(&sizing_policy_flags[table_n]); - - // To make it easier to understand the different sizing policy, - // For each policy: we display one table where the columns have equal contents width, and one where the columns have different contents width. - if (ImGui::BeginTable("table1", 3, sizing_policy_flags[table_n] | flags1)) - { - for (int row = 0; row < 3; row++) - { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); ImGui::Text("Oh dear"); - ImGui::TableNextColumn(); ImGui::Text("Oh dear"); - ImGui::TableNextColumn(); ImGui::Text("Oh dear"); - } - ImGui::EndTable(); - } - if (ImGui::BeginTable("table2", 3, sizing_policy_flags[table_n] | flags1)) - { - for (int row = 0; row < 3; row++) - { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); ImGui::Text("AAAA"); - ImGui::TableNextColumn(); ImGui::Text("BBBBBBBB"); - ImGui::TableNextColumn(); ImGui::Text("CCCCCCCCCCCC"); - } - ImGui::EndTable(); - } - ImGui::PopID(); - } - - ImGui::Spacing(); - ImGui::TextUnformatted("Advanced"); - ImGui::SameLine(); - HelpMarker("This section allows you to interact and see the effect of various sizing policies depending on whether Scroll is enabled and the contents of your columns."); - - enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText }; - static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable; - static int contents_type = CT_ShowWidth; - static int column_count = 3; - - PushStyleCompact(); - ImGui::PushID("Advanced"); - ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30); - EditTableSizingFlags(&flags); - ImGui::Combo("Contents", &contents_type, "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0"); - if (contents_type == CT_FillButton) - { - ImGui::SameLine(); - HelpMarker("Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop where contents width can feed into auto-column width can feed into contents width."); - } - ImGui::DragInt("Columns", &column_count, 0.1f, 1, 64, "%d", ImGuiSliderFlags_AlwaysClamp); - ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); - ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths); - ImGui::SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth."); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); - ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip); - ImGui::PopItemWidth(); - ImGui::PopID(); - PopStyleCompact(); - - if (ImGui::BeginTable("table2", column_count, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 7))) - { - for (int cell = 0; cell < 10 * column_count; cell++) - { - ImGui::TableNextColumn(); - int column = ImGui::TableGetColumnIndex(); - int row = ImGui::TableGetRowIndex(); - - ImGui::PushID(cell); - char label[32]; - static char text_buf[32] = ""; - sprintf(label, "Hello %d,%d", column, row); - switch (contents_type) - { - case CT_ShortText: ImGui::TextUnformatted(label); break; - case CT_LongText: ImGui::Text("Some %s text %d,%d\nOver two lines..", column == 0 ? "long" : "longeeer", column, row); break; - case CT_ShowWidth: ImGui::Text("W: %.1f", ImGui::GetContentRegionAvail().x); break; - case CT_Button: ImGui::Button(label); break; - case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break; - case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break; - } - ImGui::PopID(); - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping"); - if (ImGui::TreeNode("Vertical scrolling, with clipping")) - { - HelpMarker("Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\nWe also demonstrate using ImGuiListClipper to virtualize the submission of many items."); - static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; - - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); - PopStyleCompact(); - - // When using ScrollX or ScrollY we need to specify a size for our table container! - // Otherwise by default the table will fit all available space, like a BeginChild() call. - ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8); - if (ImGui::BeginTable("table_scrolly", 3, flags, outer_size)) - { - ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible - ImGui::TableSetupColumn("One", ImGuiTableColumnFlags_None); - ImGui::TableSetupColumn("Two", ImGuiTableColumnFlags_None); - ImGui::TableSetupColumn("Three", ImGuiTableColumnFlags_None); - ImGui::TableHeadersRow(); - - // Demonstrate using clipper for large vertical lists - ImGuiListClipper clipper; - clipper.Begin(1000); - while (clipper.Step()) - { - for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("Hello %d,%d", column, row); - } - } - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Horizontal scrolling"); - if (ImGui::TreeNode("Horizontal scrolling")) - { - HelpMarker( - "When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, " - "as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n" - "Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX," - "because the container window won't automatically extend vertically to fix contents (this may be improved in future versions)."); - static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; - static int freeze_cols = 1; - static int freeze_rows = 1; - - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); - ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); - ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); - ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); - ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); - PopStyleCompact(); - - // When using ScrollX or ScrollY we need to specify a size for our table container! - // Otherwise by default the table will fit all available space, like a BeginChild() call. - ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8); - if (ImGui::BeginTable("table_scrollx", 7, flags, outer_size)) - { - ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); - ImGui::TableSetupColumn("Line #", ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze() - ImGui::TableSetupColumn("One"); - ImGui::TableSetupColumn("Two"); - ImGui::TableSetupColumn("Three"); - ImGui::TableSetupColumn("Four"); - ImGui::TableSetupColumn("Five"); - ImGui::TableSetupColumn("Six"); - ImGui::TableHeadersRow(); - for (int row = 0; row < 20; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 7; column++) - { - // Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement. - // Because here we know that: - // - A) all our columns are contributing the same to row height - // - B) column 0 is always visible, - // We only always submit this one column and can skip others. - // More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags(). - if (!ImGui::TableSetColumnIndex(column) && column > 0) - continue; - if (column == 0) - ImGui::Text("Line %d", row); - else - ImGui::Text("Hello world %d,%d", column, row); - } - } - ImGui::EndTable(); - } - - ImGui::Spacing(); - ImGui::TextUnformatted("Stretch + ScrollX"); - ImGui::SameLine(); - HelpMarker( - "Showcase using Stretch columns + ScrollX together: " - "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n" - "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns + ScrollX together doesn't make sense."); - static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; - static float inner_width = 1000.0f; - PushStyleCompact(); - ImGui::PushID("flags3"); - ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags2, ImGuiTableFlags_ScrollX); - ImGui::DragFloat("inner_width", &inner_width, 1.0f, 0.0f, FLT_MAX, "%.1f"); - ImGui::PopItemWidth(); - ImGui::PopID(); - PopStyleCompact(); - if (ImGui::BeginTable("table2", 7, flags2, outer_size, inner_width)) - { - for (int cell = 0; cell < 20 * 7; cell++) - { - ImGui::TableNextColumn(); - ImGui::Text("Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex()); - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Columns flags"); - if (ImGui::TreeNode("Columns flags")) - { - // Create a first table just to show all the options/flags we want to make visible in our example! - const int column_count = 3; - const char* column_names[column_count] = { "One", "Two", "Three" }; - static ImGuiTableColumnFlags column_flags[column_count] = { ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide }; - static ImGuiTableColumnFlags column_flags_out[column_count] = { 0, 0, 0 }; // Output from TableGetColumnFlags() - - if (ImGui::BeginTable("table_columns_flags_checkboxes", column_count, ImGuiTableFlags_None)) - { - PushStyleCompact(); - for (int column = 0; column < column_count; column++) - { - ImGui::TableNextColumn(); - ImGui::PushID(column); - ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns - ImGui::Text("'%s'", column_names[column]); - ImGui::Spacing(); - ImGui::Text("Input flags:"); - EditTableColumnsFlags(&column_flags[column]); - ImGui::Spacing(); - ImGui::Text("Output flags:"); - ImGui::BeginDisabled(); - ShowTableColumnsStatusFlags(column_flags_out[column]); - ImGui::EndDisabled(); - ImGui::PopID(); - } - PopStyleCompact(); - ImGui::EndTable(); - } - - // Create the real table we care about for the example! - // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above, otherwise in - // a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible + resizing the parent window down) - const ImGuiTableFlags flags - = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY - | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV - | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable; - ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9); - if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size)) - { - for (int column = 0; column < column_count; column++) - ImGui::TableSetupColumn(column_names[column], column_flags[column]); - ImGui::TableHeadersRow(); - for (int column = 0; column < column_count; column++) - column_flags_out[column] = ImGui::TableGetColumnFlags(column); - float indent_step = (float)((int)TEXT_BASE_WIDTH / 2); - for (int row = 0; row < 8; row++) - { - ImGui::Indent(indent_step); // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags. - ImGui::TableNextRow(); - for (int column = 0; column < column_count; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("%s %s", (column == 0) ? "Indented" : "Hello", ImGui::TableGetColumnName(column)); - } - } - ImGui::Unindent(indent_step * 8.0f); - - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Columns widths"); - if (ImGui::TreeNode("Columns widths")) - { - HelpMarker("Using TableSetupColumn() to setup default width."); - - static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize; - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable); - ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags1, ImGuiTableFlags_NoBordersInBodyUntilResize); - PopStyleCompact(); - if (ImGui::BeginTable("table1", 3, flags1)) - { - // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed. - ImGui::TableSetupColumn("one", ImGuiTableColumnFlags_WidthFixed, 100.0f); // Default to 100.0f - ImGui::TableSetupColumn("two", ImGuiTableColumnFlags_WidthFixed, 200.0f); // Default to 200.0f - ImGui::TableSetupColumn("three", ImGuiTableColumnFlags_WidthFixed); // Default to auto - ImGui::TableHeadersRow(); - for (int row = 0; row < 4; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - if (row == 0) - ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x); - else - ImGui::Text("Hello %d,%d", column, row); - } - } - ImGui::EndTable(); - } - - HelpMarker("Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, fixed columns with set width may still be shrunk down if there's not enough space in the host."); - - static ImGuiTableFlags flags2 = ImGuiTableFlags_None; - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags2, ImGuiTableFlags_NoKeepColumnsVisible); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags2, ImGuiTableFlags_BordersInnerV); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags2, ImGuiTableFlags_BordersOuterV); - PopStyleCompact(); - if (ImGui::BeginTable("table2", 4, flags2)) - { - // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed. - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); - for (int row = 0; row < 5; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 4; column++) - { - ImGui::TableSetColumnIndex(column); - if (row == 0) - ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x); - else - ImGui::Text("Hello %d,%d", column, row); - } - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Nested tables"); - if (ImGui::TreeNode("Nested tables")) - { - HelpMarker("This demonstrate embedding a table into another table cell."); - - if (ImGui::BeginTable("table_nested1", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) - { - ImGui::TableSetupColumn("A0"); - ImGui::TableSetupColumn("A1"); - ImGui::TableHeadersRow(); - - ImGui::TableNextColumn(); - ImGui::Text("A0 Row 0"); - { - float rows_height = TEXT_BASE_HEIGHT * 2; - if (ImGui::BeginTable("table_nested2", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) - { - ImGui::TableSetupColumn("B0"); - ImGui::TableSetupColumn("B1"); - ImGui::TableHeadersRow(); - - ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height); - ImGui::TableNextColumn(); - ImGui::Text("B0 Row 0"); - ImGui::TableNextColumn(); - ImGui::Text("B1 Row 0"); - ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height); - ImGui::TableNextColumn(); - ImGui::Text("B0 Row 1"); - ImGui::TableNextColumn(); - ImGui::Text("B1 Row 1"); - - ImGui::EndTable(); - } - } - ImGui::TableNextColumn(); ImGui::Text("A1 Row 0"); - ImGui::TableNextColumn(); ImGui::Text("A0 Row 1"); - ImGui::TableNextColumn(); ImGui::Text("A1 Row 1"); - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Row height"); - if (ImGui::TreeNode("Row height")) - { - HelpMarker("You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\nWe cannot honor a _maximum_ row height as that would requires a unique clipping rectangle per row."); - if (ImGui::BeginTable("table_row_height", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerV)) - { - for (int row = 0; row < 10; row++) - { - float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row); - ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height); - ImGui::TableNextColumn(); - ImGui::Text("min_row_height = %.2f", min_row_height); - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Outer size"); - if (ImGui::TreeNode("Outer size")) - { - // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY - // Important to that note how the two flags have slightly different behaviors! - ImGui::Text("Using NoHostExtendX and NoHostExtendY:"); - PushStyleCompact(); - static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX; - ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); - ImGui::SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used."); - ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); - ImGui::SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible."); - PopStyleCompact(); - - ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f); - if (ImGui::BeginTable("table1", 3, flags, outer_size)) - { - for (int row = 0; row < 10; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableNextColumn(); - ImGui::Text("Cell %d,%d", column, row); - } - } - ImGui::EndTable(); - } - ImGui::SameLine(); - ImGui::Text("Hello!"); - - ImGui::Spacing(); - - ImGui::Text("Using explicit size:"); - if (ImGui::BeginTable("table2", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) - { - for (int row = 0; row < 5; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableNextColumn(); - ImGui::Text("Cell %d,%d", column, row); - } - } - ImGui::EndTable(); - } - ImGui::SameLine(); - if (ImGui::BeginTable("table3", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) - { - for (int row = 0; row < 3; row++) - { - ImGui::TableNextRow(0, TEXT_BASE_HEIGHT * 1.5f); - for (int column = 0; column < 3; column++) - { - ImGui::TableNextColumn(); - ImGui::Text("Cell %d,%d", column, row); - } - } - ImGui::EndTable(); - } - - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Background color"); - if (ImGui::TreeNode("Background color")) - { - static ImGuiTableFlags flags = ImGuiTableFlags_RowBg; - static int row_bg_type = 1; - static int row_bg_target = 1; - static int cell_bg_type = 1; - - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders); - ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); - ImGui::SameLine(); HelpMarker("ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style."); - ImGui::Combo("row bg type", (int*)&row_bg_type, "None\0Red\0Gradient\0"); - ImGui::Combo("row bg target", (int*)&row_bg_target, "RowBg0\0RowBg1\0"); ImGui::SameLine(); HelpMarker("Target RowBg0 to override the alternating odd/even colors,\nTarget RowBg1 to blend with them."); - ImGui::Combo("cell bg type", (int*)&cell_bg_type, "None\0Blue\0"); ImGui::SameLine(); HelpMarker("We are colorizing cells to B1->C2 here."); - IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2); - IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1); - IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1); - PopStyleCompact(); - - if (ImGui::BeginTable("table1", 5, flags)) - { - for (int row = 0; row < 6; row++) - { - ImGui::TableNextRow(); - - // Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)' - // We use a transparent color so we can see the one behind in case our target is RowBg1 and RowBg0 was already targeted by the ImGuiTableFlags_RowBg flag. - if (row_bg_type != 0) - { - ImU32 row_bg_color = ImGui::GetColorU32(row_bg_type == 1 ? ImVec4(0.7f, 0.3f, 0.3f, 0.65f) : ImVec4(0.2f + row * 0.1f, 0.2f, 0.2f, 0.65f)); // Flat or Gradient? - ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color); - } - - // Fill cells - for (int column = 0; column < 5; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("%c%c", 'A' + row, '0' + column); - - // Change background of Cells B1->C2 - // Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)' - // (the CellBg color will be blended over the RowBg and ColumnBg colors) - // We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop. - if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1) - { - ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 0.65f)); - ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color); - } - } - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Tree view"); - if (ImGui::TreeNode("Tree view")) - { - static ImGuiTableFlags flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; - - if (ImGui::BeginTable("3ways", 3, flags)) - { - // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide); - ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); - ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f); - ImGui::TableHeadersRow(); - - // Simple storage to output a dummy file-system. - struct MyTreeNode - { - const char* Name; - const char* Type; - int Size; - int ChildIdx; - int ChildCount; - static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes) - { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - const bool is_folder = (node->ChildCount > 0); - if (is_folder) - { - bool open = ImGui::TreeNodeEx(node->Name, ImGuiTreeNodeFlags_SpanFullWidth); - ImGui::TableNextColumn(); - ImGui::TextDisabled("--"); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(node->Type); - if (open) - { - for (int child_n = 0; child_n < node->ChildCount; child_n++) - DisplayNode(&all_nodes[node->ChildIdx + child_n], all_nodes); - ImGui::TreePop(); - } - } - else - { - ImGui::TreeNodeEx(node->Name, ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth); - ImGui::TableNextColumn(); - ImGui::Text("%d", node->Size); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(node->Type); - } - } - }; - static const MyTreeNode nodes[] = - { - { "Root", "Folder", -1, 1, 3 }, // 0 - { "Music", "Folder", -1, 4, 2 }, // 1 - { "Textures", "Folder", -1, 6, 3 }, // 2 - { "desktop.ini", "System file", 1024, -1,-1 }, // 3 - { "File1_a.wav", "Audio file", 123000, -1,-1 }, // 4 - { "File1_b.wav", "Audio file", 456000, -1,-1 }, // 5 - { "Image001.png", "Image file", 203128, -1,-1 }, // 6 - { "Copy of Image001.png", "Image file", 203256, -1,-1 }, // 7 - { "Copy of Image001 (Final2).png","Image file", 203512, -1,-1 }, // 8 - }; - - MyTreeNode::DisplayNode(&nodes[0], nodes); - - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Item width"); - if (ImGui::TreeNode("Item width")) - { - HelpMarker( - "Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n" - "Note that on auto-resizing non-resizable fixed columns, querying the content width for e.g. right-alignment doesn't make sense."); - if (ImGui::BeginTable("table_item_width", 3, ImGuiTableFlags_Borders)) - { - ImGui::TableSetupColumn("small"); - ImGui::TableSetupColumn("half"); - ImGui::TableSetupColumn("right-align"); - ImGui::TableHeadersRow(); - - for (int row = 0; row < 3; row++) - { - ImGui::TableNextRow(); - if (row == 0) - { - // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient) - ImGui::TableSetColumnIndex(0); - ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small - ImGui::TableSetColumnIndex(1); - ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f); - ImGui::TableSetColumnIndex(2); - ImGui::PushItemWidth(-FLT_MIN); // Right-aligned - } - - // Draw our contents - static float dummy_f = 0.0f; - ImGui::PushID(row); - ImGui::TableSetColumnIndex(0); - ImGui::SliderFloat("float0", &dummy_f, 0.0f, 1.0f); - ImGui::TableSetColumnIndex(1); - ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f); - ImGui::TableSetColumnIndex(2); - ImGui::SliderFloat("##float2", &dummy_f, 0.0f, 1.0f); // No visible label since right-aligned - ImGui::PopID(); - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - // Demonstrate using TableHeader() calls instead of TableHeadersRow() - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Custom headers"); - if (ImGui::TreeNode("Custom headers")) - { - const int COLUMNS_COUNT = 3; - if (ImGui::BeginTable("table_custom_headers", COLUMNS_COUNT, ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) - { - ImGui::TableSetupColumn("Apricot"); - ImGui::TableSetupColumn("Banana"); - ImGui::TableSetupColumn("Cherry"); - - // Dummy entire-column selection storage - // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox. - static bool column_selected[3] = {}; - - // Instead of calling TableHeadersRow() we'll submit custom headers ourselves - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); - for (int column = 0; column < COLUMNS_COUNT; column++) - { - ImGui::TableSetColumnIndex(column); - const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn() - ImGui::PushID(column); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::Checkbox("##checkall", &column_selected[column]); - ImGui::PopStyleVar(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::TableHeader(column_name); - ImGui::PopID(); - } - - for (int row = 0; row < 5; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - char buf[32]; - sprintf(buf, "Cell %d,%d", column, row); - ImGui::TableSetColumnIndex(column); - ImGui::Selectable(buf, column_selected[column]); - } - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - // Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by TableHeadersRow()/TableHeader() - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Context menus"); - if (ImGui::TreeNode("Context menus")) - { - HelpMarker("By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\nUsing ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body."); - static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody; - - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags1, ImGuiTableFlags_ContextMenuInBody); - PopStyleCompact(); - - // Context Menus: first example - // [1.1] Right-click on the TableHeadersRow() line to open the default table context menu. - // [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set) - const int COLUMNS_COUNT = 3; - if (ImGui::BeginTable("table_context_menu", COLUMNS_COUNT, flags1)) - { - ImGui::TableSetupColumn("One"); - ImGui::TableSetupColumn("Two"); - ImGui::TableSetupColumn("Three"); - - // [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu. - ImGui::TableHeadersRow(); - - // Submit dummy contents - for (int row = 0; row < 4; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < COLUMNS_COUNT; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::Text("Cell %d,%d", column, row); - } - } - ImGui::EndTable(); - } - - // Context Menus: second example - // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu. - // [2.2] Right-click on the ".." to open a custom popup - // [2.3] Right-click in columns to open another custom popup - HelpMarker("Demonstrate mixing table context menu (over header), item context button (over button) and custom per-colum context menu (over column body)."); - ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders; - if (ImGui::BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2)) - { - ImGui::TableSetupColumn("One"); - ImGui::TableSetupColumn("Two"); - ImGui::TableSetupColumn("Three"); - - // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu. - ImGui::TableHeadersRow(); - for (int row = 0; row < 4; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < COLUMNS_COUNT; column++) - { - // Submit dummy contents - ImGui::TableSetColumnIndex(column); - ImGui::Text("Cell %d,%d", column, row); - ImGui::SameLine(); - - // [2.2] Right-click on the ".." to open a custom popup - ImGui::PushID(row * COLUMNS_COUNT + column); - ImGui::SmallButton(".."); - if (ImGui::BeginPopupContextItem()) - { - ImGui::Text("This is the popup for Button(\"..\") in Cell %d,%d", column, row); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - ImGui::PopID(); - } - } - - // [2.3] Right-click anywhere in columns to open another custom popup - // (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup - // to manage popup priority as the popups triggers, here "are we hovering a column" are overlapping) - int hovered_column = -1; - for (int column = 0; column < COLUMNS_COUNT + 1; column++) - { - ImGui::PushID(column); - if (ImGui::TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered) - hovered_column = column; - if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(1)) - ImGui::OpenPopup("MyPopup"); - if (ImGui::BeginPopup("MyPopup")) - { - if (column == COLUMNS_COUNT) - ImGui::Text("This is a custom popup for unused space after the last column."); - else - ImGui::Text("This is a custom popup for Column %d", column); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - ImGui::PopID(); - } - - ImGui::EndTable(); - ImGui::Text("Hovered column: %d", hovered_column); - } - ImGui::TreePop(); - } - - // Demonstrate creating multiple tables with the same ID - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Synced instances"); - if (ImGui::TreeNode("Synced instances")) - { - HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc."); - for (int n = 0; n < 3; n++) - { - char buf[32]; - sprintf(buf, "Synced Table %d", n); - bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen); - if (open && ImGui::BeginTable("Table", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings)) - { - ImGui::TableSetupColumn("One"); - ImGui::TableSetupColumn("Two"); - ImGui::TableSetupColumn("Three"); - ImGui::TableHeadersRow(); - for (int cell = 0; cell < 9; cell++) - { - ImGui::TableNextColumn(); - ImGui::Text("this cell %d", cell); - } - ImGui::EndTable(); - } - } - ImGui::TreePop(); - } - - // Demonstrate using Sorting facilities - // This is a simplified version of the "Advanced" example, where we mostly focus on the code necessary to handle sorting. - // Note that the "Advanced" example also showcase manually triggering a sort (e.g. if item quantities have been modified) - static const char* template_items_names[] = - { - "Banana", "Apple", "Cherry", "Watermelon", "Grapefruit", "Strawberry", "Mango", - "Kiwi", "Orange", "Pineapple", "Blueberry", "Plum", "Coconut", "Pear", "Apricot" - }; - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Sorting"); - if (ImGui::TreeNode("Sorting")) - { - // Create item list - static ImVector items; - if (items.Size == 0) - { - items.resize(50, MyItem()); - for (int n = 0; n < items.Size; n++) - { - const int template_n = n % IM_ARRAYSIZE(template_items_names); - MyItem& item = items[n]; - item.ID = n; - item.Name = template_items_names[template_n]; - item.Quantity = (n * n - n) % 20; // Assign default quantities - } - } - - // Options - static ImGuiTableFlags flags = - ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti - | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody - | ImGuiTableFlags_ScrollY; - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti); - ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1)."); - ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate); - ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0)."); - PopStyleCompact(); - - if (ImGui::BeginTable("table_sorting", 4, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), 0.0f)) - { - // Declare columns - // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications. - // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index! - // Demonstrate using a mixture of flags among available sort-related flags: - // - ImGuiTableColumnFlags_DefaultSort - // - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending - // - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending - ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_ID); - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); - ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); - ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Quantity); - ImGui::TableSetupScrollFreeze(0, 1); // Make row always visible - ImGui::TableHeadersRow(); - - // Sort our data if sort specs have been changed! - if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) - if (sorts_specs->SpecsDirty) - { - MyItem::s_current_sort_specs = sorts_specs; // Store in variable accessible by the sort function. - if (items.Size > 1) - qsort(&items[0], (size_t)items.Size, sizeof(items[0]), MyItem::CompareWithSortSpecs); - MyItem::s_current_sort_specs = NULL; - sorts_specs->SpecsDirty = false; - } - - // Demonstrate using clipper for large vertical lists - ImGuiListClipper clipper; - clipper.Begin(items.Size); - while (clipper.Step()) - for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) - { - // Display a data item - MyItem* item = &items[row_n]; - ImGui::PushID(item->ID); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("%04d", item->ID); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(item->Name); - ImGui::TableNextColumn(); - ImGui::SmallButton("None"); - ImGui::TableNextColumn(); - ImGui::Text("%d", item->Quantity); - ImGui::PopID(); - } - ImGui::EndTable(); - } - ImGui::TreePop(); - } - - // In this example we'll expose most table flags and settings. - // For specific flags and settings refer to the corresponding section for more detailed explanation. - // This section is mostly useful to experiment with combining certain flags or settings with each others. - //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG] - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - IMGUI_DEMO_MARKER("Tables/Advanced"); - if (ImGui::TreeNode("Advanced")) - { - static ImGuiTableFlags flags = - ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable - | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti - | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody - | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY - | ImGuiTableFlags_SizingFixedFit; - - enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow }; - static int contents_type = CT_SelectableSpanRow; - const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" }; - static int freeze_cols = 1; - static int freeze_rows = 1; - static int items_count = IM_ARRAYSIZE(template_items_names) * 2; - static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12); - static float row_min_height = 0.0f; // Auto - static float inner_width_with_scroll = 0.0f; // Auto-extend - static bool outer_size_enabled = true; - static bool show_headers = true; - static bool show_wrapped_text = false; - //static ImGuiTextFilter filter; - //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affects column sizing - if (ImGui::TreeNode("Options")) - { - // Make the UI compact because there are so many fields - PushStyleCompact(); - ImGui::PushItemWidth(TEXT_BASE_WIDTH * 28.0f); - - if (ImGui::TreeNodeEx("Features:", ImGuiTreeNodeFlags_DefaultOpen)) - { - ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); - ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable); - ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable); - ImGui::CheckboxFlags("ImGuiTableFlags_Sortable", &flags, ImGuiTableFlags_Sortable); - ImGui::CheckboxFlags("ImGuiTableFlags_NoSavedSettings", &flags, ImGuiTableFlags_NoSavedSettings); - ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags, ImGuiTableFlags_ContextMenuInBody); - ImGui::TreePop(); - } - - if (ImGui::TreeNodeEx("Decorations:", ImGuiTreeNodeFlags_DefaultOpen)) - { - ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH); - ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appears in Headers"); - ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers)"); - ImGui::TreePop(); - } - - if (ImGui::TreeNodeEx("Sizing:", ImGuiTreeNodeFlags_DefaultOpen)) - { - EditTableSizingFlags(&flags); - ImGui::SameLine(); HelpMarker("In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical."); - ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); - ImGui::SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used."); - ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); - ImGui::SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible."); - ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible); - ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled."); - ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths); - ImGui::SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth."); - ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip); - ImGui::SameLine(); HelpMarker("Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with ScrollFreeze options."); - ImGui::TreePop(); - } - - if (ImGui::TreeNodeEx("Padding:", ImGuiTreeNodeFlags_DefaultOpen)) - { - ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags, ImGuiTableFlags_PadOuterX); - ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags, ImGuiTableFlags_NoPadOuterX); - ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags, ImGuiTableFlags_NoPadInnerX); - ImGui::TreePop(); - } - - if (ImGui::TreeNodeEx("Scrolling:", ImGuiTreeNodeFlags_DefaultOpen)) - { - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); - ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); - ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); - ImGui::TreePop(); - } - - if (ImGui::TreeNodeEx("Sorting:", ImGuiTreeNodeFlags_DefaultOpen)) - { - ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti); - ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1)."); - ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate); - ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0)."); - ImGui::TreePop(); - } - - if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen)) - { - ImGui::Checkbox("show_headers", &show_headers); - ImGui::Checkbox("show_wrapped_text", &show_wrapped_text); - - ImGui::DragFloat2("##OuterSize", &outer_size_value.x); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::Checkbox("outer_size", &outer_size_enabled); - ImGui::SameLine(); - HelpMarker("If scrolling is disabled (ScrollX and ScrollY not set):\n" - "- The table is output directly in the parent window.\n" - "- OuterSize.x < 0.0f will right-align the table.\n" - "- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch column.\n" - "- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set)."); - - // From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling. - // To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled. - ImGui::DragFloat("inner_width (when ScrollX active)", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX); - - ImGui::DragFloat("row_min_height", &row_min_height, 1.0f, 0.0f, FLT_MAX); - ImGui::SameLine(); HelpMarker("Specify height of the Selectable item."); - - ImGui::DragInt("items_count", &items_count, 0.1f, 0, 9999); - ImGui::Combo("items_type (first column)", &contents_type, contents_type_names, IM_ARRAYSIZE(contents_type_names)); - //filter.Draw("filter"); - ImGui::TreePop(); - } - - ImGui::PopItemWidth(); - PopStyleCompact(); - ImGui::Spacing(); - ImGui::TreePop(); - } - - // Update item list if we changed the number of items - static ImVector items; - static ImVector selection; - static bool items_need_sort = false; - if (items.Size != items_count) - { - items.resize(items_count, MyItem()); - for (int n = 0; n < items_count; n++) - { - const int template_n = n % IM_ARRAYSIZE(template_items_names); - MyItem& item = items[n]; - item.ID = n; - item.Name = template_items_names[template_n]; - item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities - } - } - - const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList(); - const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size; - ImVec2 table_scroll_cur, table_scroll_max; // For debug display - const ImDrawList* table_draw_list = NULL; // " - - // Submit table - const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f; - if (ImGui::BeginTable("table_advanced", 6, flags, outer_size_enabled ? outer_size_value : ImVec2(0, 0), inner_width_to_use)) - { - // Declare columns - // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications. - // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index! - ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID); - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); - ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); - ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity); - ImGui::TableSetupColumn("Description", (flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Description); - ImGui::TableSetupColumn("Hidden", ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort); - ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); - - // Sort our data if sort specs have been changed! - ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs(); - if (sorts_specs && sorts_specs->SpecsDirty) - items_need_sort = true; - if (sorts_specs && items_need_sort && items.Size > 1) - { - MyItem::s_current_sort_specs = sorts_specs; // Store in variable accessible by the sort function. - qsort(&items[0], (size_t)items.Size, sizeof(items[0]), MyItem::CompareWithSortSpecs); - MyItem::s_current_sort_specs = NULL; - sorts_specs->SpecsDirty = false; - } - items_need_sort = false; - - // Take note of whether we are currently sorting based on the Quantity field, - // we will use this to trigger sorting when we know the data of this column has been modified. - const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0; - - // Show headers - if (show_headers) - ImGui::TableHeadersRow(); - - // Show data - // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here? - ImGui::PushButtonRepeat(true); -#if 1 - // Demonstrate using clipper for large vertical lists - ImGuiListClipper clipper; - clipper.Begin(items.Size); - while (clipper.Step()) - { - for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) -#else - // Without clipper - { - for (int row_n = 0; row_n < items.Size; row_n++) -#endif - { - MyItem* item = &items[row_n]; - //if (!filter.PassFilter(item->Name)) - // continue; - - const bool item_is_selected = selection.contains(item->ID); - ImGui::PushID(item->ID); - ImGui::TableNextRow(ImGuiTableRowFlags_None, row_min_height); - - // For the demo purpose we can select among different type of items submitted in the first column - ImGui::TableSetColumnIndex(0); - char label[32]; - sprintf(label, "%04d", item->ID); - if (contents_type == CT_Text) - ImGui::TextUnformatted(label); - else if (contents_type == CT_Button) - ImGui::Button(label); - else if (contents_type == CT_SmallButton) - ImGui::SmallButton(label); - else if (contents_type == CT_FillButton) - ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); - else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow) - { - ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap : ImGuiSelectableFlags_None; - if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height))) - { - if (ImGui::GetIO().KeyCtrl) - { - if (item_is_selected) - selection.find_erase_unsorted(item->ID); - else - selection.push_back(item->ID); - } - else - { - selection.clear(); - selection.push_back(item->ID); - } - } - } - - if (ImGui::TableSetColumnIndex(1)) - ImGui::TextUnformatted(item->Name); - - // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity, - // and we are currently sorting on the column showing the Quantity. - // To avoid triggering a sort while holding the button, we only trigger it when the button has been released. - // You will probably need a more advanced system in your code if you want to automatically sort when a specific entry changes. - if (ImGui::TableSetColumnIndex(2)) - { - if (ImGui::SmallButton("Chop")) { item->Quantity += 1; } - if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; } - ImGui::SameLine(); - if (ImGui::SmallButton("Eat")) { item->Quantity -= 1; } - if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; } - } - - if (ImGui::TableSetColumnIndex(3)) - ImGui::Text("%d", item->Quantity); - - ImGui::TableSetColumnIndex(4); - if (show_wrapped_text) - ImGui::TextWrapped("Lorem ipsum dolor sit amet"); - else - ImGui::Text("Lorem ipsum dolor sit amet"); - - if (ImGui::TableSetColumnIndex(5)) - ImGui::Text("1234"); - - ImGui::PopID(); - } - } - ImGui::PopButtonRepeat(); - - // Store some info to display debug details below - table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY()); - table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY()); - table_draw_list = ImGui::GetWindowDrawList(); - ImGui::EndTable(); - } - static bool show_debug_details = false; - ImGui::Checkbox("Debug details", &show_debug_details); - if (show_debug_details && table_draw_list) - { - ImGui::SameLine(0.0f, 0.0f); - const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size; - if (table_draw_list == parent_draw_list) - ImGui::Text(": DrawCmd: +%d (in same window)", - table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count); - else - ImGui::Text(": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)", - table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y); - } - ImGui::TreePop(); - } - - ImGui::PopID(); - - ShowDemoWindowColumns(); - - if (disable_indent) - ImGui::PopStyleVar(); -} - -// Demonstrate old/legacy Columns API! -// [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!] -static void ShowDemoWindowColumns() -{ - IMGUI_DEMO_MARKER("Columns (legacy API)"); - bool open = ImGui::TreeNode("Legacy Columns API"); - ImGui::SameLine(); - HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!"); - if (!open) - return; - - // Basic columns - IMGUI_DEMO_MARKER("Columns (legacy API)/Basic"); - if (ImGui::TreeNode("Basic")) - { - ImGui::Text("Without border:"); - ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border - ImGui::Separator(); - for (int n = 0; n < 14; n++) - { - char label[32]; - sprintf(label, "Item %d", n); - if (ImGui::Selectable(label)) {} - //if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {} - ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::Separator(); - - ImGui::Text("With border:"); - ImGui::Columns(4, "mycolumns"); // 4-ways, with border - ImGui::Separator(); - ImGui::Text("ID"); ImGui::NextColumn(); - ImGui::Text("Name"); ImGui::NextColumn(); - ImGui::Text("Path"); ImGui::NextColumn(); - ImGui::Text("Hovered"); ImGui::NextColumn(); - ImGui::Separator(); - const char* names[3] = { "One", "Two", "Three" }; - const char* paths[3] = { "/path/one", "/path/two", "/path/three" }; - static int selected = -1; - for (int i = 0; i < 3; i++) - { - char label[32]; - sprintf(label, "%04d", i); - if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns)) - selected = i; - bool hovered = ImGui::IsItemHovered(); - ImGui::NextColumn(); - ImGui::Text(names[i]); ImGui::NextColumn(); - ImGui::Text(paths[i]); ImGui::NextColumn(); - ImGui::Text("%d", hovered); ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Columns (legacy API)/Borders"); - if (ImGui::TreeNode("Borders")) - { - // NB: Future columns API should allow automatic horizontal borders. - static bool h_borders = true; - static bool v_borders = true; - static int columns_count = 4; - const int lines_count = 3; - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); - ImGui::DragInt("##columns_count", &columns_count, 0.1f, 2, 10, "%d columns"); - if (columns_count < 2) - columns_count = 2; - ImGui::SameLine(); - ImGui::Checkbox("horizontal", &h_borders); - ImGui::SameLine(); - ImGui::Checkbox("vertical", &v_borders); - ImGui::Columns(columns_count, NULL, v_borders); - for (int i = 0; i < columns_count * lines_count; i++) - { - if (h_borders && ImGui::GetColumnIndex() == 0) - ImGui::Separator(); - ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i); - ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); - ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x); - ImGui::Text("Offset %.2f", ImGui::GetColumnOffset()); - ImGui::Text("Long text that is likely to clip"); - ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f)); - ImGui::NextColumn(); - } - ImGui::Columns(1); - if (h_borders) - ImGui::Separator(); - ImGui::TreePop(); - } - - // Create multiple items in a same cell before switching to next column - IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items"); - if (ImGui::TreeNode("Mixed items")) - { - ImGui::Columns(3, "mixed"); - ImGui::Separator(); - - ImGui::Text("Hello"); - ImGui::Button("Banana"); - ImGui::NextColumn(); - - ImGui::Text("ImGui"); - ImGui::Button("Apple"); - static float foo = 1.0f; - ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f"); - ImGui::Text("An extra line here."); - ImGui::NextColumn(); - - ImGui::Text("Sailor"); - ImGui::Button("Corniflower"); - static float bar = 1.0f; - ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f"); - ImGui::NextColumn(); - - if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - if (ImGui::CollapsingHeader("Category B")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - if (ImGui::CollapsingHeader("Category C")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); - } - - // Word wrapping - IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping"); - if (ImGui::TreeNode("Word-wrapping")) - { - ImGui::Columns(2, "word-wrapping"); - ImGui::Separator(); - ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); - ImGui::TextWrapped("Hello Left"); - ImGui::NextColumn(); - ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); - ImGui::TextWrapped("Hello Right"); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling"); - if (ImGui::TreeNode("Horizontal Scrolling")) - { - ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); - ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f); - ImGui::BeginChild("##ScrollingRegion", child_size, false, ImGuiWindowFlags_HorizontalScrollbar); - ImGui::Columns(10); - - // Also demonstrate using clipper for large vertical lists - int ITEMS_COUNT = 2000; - ImGuiListClipper clipper; - clipper.Begin(ITEMS_COUNT); - while (clipper.Step()) - { - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - for (int j = 0; j < 10; j++) - { - ImGui::Text("Line %d Column %d...", i, j); - ImGui::NextColumn(); - } - } - ImGui::Columns(1); - ImGui::EndChild(); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Columns (legacy API)/Tree"); - if (ImGui::TreeNode("Tree")) - { - ImGui::Columns(2, "tree", true); - for (int x = 0; x < 3; x++) - { - bool open1 = ImGui::TreeNode((void*)(intptr_t)x, "Node%d", x); - ImGui::NextColumn(); - ImGui::Text("Node contents"); - ImGui::NextColumn(); - if (open1) - { - for (int y = 0; y < 3; y++) - { - bool open2 = ImGui::TreeNode((void*)(intptr_t)y, "Node%d.%d", x, y); - ImGui::NextColumn(); - ImGui::Text("Node contents"); - if (open2) - { - ImGui::Text("Even more contents"); - if (ImGui::TreeNode("Tree in column")) - { - ImGui::Text("The quick brown fox jumps over the lazy dog"); - ImGui::TreePop(); - } - } - ImGui::NextColumn(); - if (open2) - ImGui::TreePop(); - } - ImGui::TreePop(); - } - } - ImGui::Columns(1); - ImGui::TreePop(); - } - - ImGui::TreePop(); -} - -namespace ImGui { extern ImGuiKeyData* GetKeyData(ImGuiKey key); } - -static void ShowDemoWindowMisc() -{ - IMGUI_DEMO_MARKER("Filtering"); - if (ImGui::CollapsingHeader("Filtering")) - { - // Helper class to easy setup a text filter. - // You may want to implement a more feature-full filtering scheme in your own application. - static ImGuiTextFilter filter; - ImGui::Text("Filter usage:\n" - " \"\" display all lines\n" - " \"xxx\" display lines containing \"xxx\"\n" - " \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n" - " \"-xxx\" hide lines containing \"xxx\""); - filter.Draw(); - const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" }; - for (int i = 0; i < IM_ARRAYSIZE(lines); i++) - if (filter.PassFilter(lines[i])) - ImGui::BulletText("%s", lines[i]); - } - - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus"); - if (ImGui::CollapsingHeader("Inputs, Navigation & Focus")) - { - ImGuiIO& io = ImGui::GetIO(); - - // Display ImGuiIO output flags - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Output"); - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode("Output")) - { - ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse); - ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose); - ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard); - ImGui::Text("io.WantTextInput: %d", io.WantTextInput); - ImGui::Text("io.WantSetMousePos: %d", io.WantSetMousePos); - ImGui::Text("io.NavActive: %d, io.NavVisible: %d", io.NavActive, io.NavVisible); - ImGui::TreePop(); - } - - // Display Mouse state - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse State"); - if (ImGui::TreeNode("Mouse State")) - { - if (ImGui::IsMousePosValid()) - ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); - else - ImGui::Text("Mouse pos: "); - ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); - - int count = IM_ARRAYSIZE(io.MouseDown); - ImGui::Text("Mouse down:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } - ImGui::Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, ImGui::GetMouseClickedCount(i)); } - ImGui::Text("Mouse released:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); - ImGui::Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused - ImGui::TreePop(); - } - - // Display Keyboard/Mouse state - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard, Gamepad & Navigation State"); - if (ImGui::TreeNode("Keyboard, Gamepad & Navigation State")) - { - // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allow displaying the data for old/new backends. - // User code should never have to go through such hoops: old code may use native keycodes, new code may use ImGuiKey codes. -#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; - const ImGuiKey key_first = ImGuiKey_NamedKey_BEGIN; -#else - struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array - const ImGuiKey key_first = 0; - //ImGui::Text("Legacy raw:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } -#endif - ImGui::Text("Keys down:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f secs)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } } - ImGui::Text("Keys pressed:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } - ImGui::Text("Keys released:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } - ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); - ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. - ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f (%.02f secs)", i, io.NavInputs[i], io.NavInputsDownDuration[i]); } - ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); } - - // Draw an arbitrary US keyboard layout to visualize translated keys - { - const ImVec2 key_size = ImVec2(35.0f, 35.0f); - const float key_rounding = 3.0f; - const ImVec2 key_face_size = ImVec2(25.0f, 25.0f); - const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f); - const float key_face_rounding = 2.0f; - const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f); - const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f); - const float key_row_offset = 9.0f; - - ImVec2 board_min = ImGui::GetCursorScreenPos(); - ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f); - ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y); - - struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; }; - const KeyLayoutData keys_to_display[] = - { - { 0, 0, "", ImGuiKey_Tab }, { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R }, - { 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F }, - { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V } - }; - - // Elements rendered manually via ImDrawList API are not clipped automatically. - // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view. - ImGui::Dummy(ImVec2(board_max.x - board_min.x, board_max.y - board_min.y)); - if (ImGui::IsItemVisible()) - { - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - draw_list->PushClipRect(board_min, board_max, true); - for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++) - { - const KeyLayoutData* key_data = &keys_to_display[n]; - ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y); - ImVec2 key_max = ImVec2(key_min.x + key_size.x, key_min.y + key_size.y); - draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding); - draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding); - ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y); - ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y); - draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f); - draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding); - ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y); - draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label); - if (ImGui::IsKeyDown(key_data->Key)) - draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding); - } - draw_list->PopClipRect(); - } - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Capture override")) - { - HelpMarker( - "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui " - "to instruct your application of how to route inputs. Typically, when a value is true, it means " - "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n" - "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, " - "and underlying application should ignore mouse inputs (in practice there are many and more subtle " - "rules leading to how those flags are set)."); - - ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse); - ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose); - ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard); - - HelpMarker( - "Hovering the colored canvas will override io.WantCaptureXXX fields.\n" - "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering and true when clicking."); - static int capture_override_mouse = -1; - static int capture_override_keyboard = -1; - const char* capture_override_desc[] = { "None", "Set to false", "Set to true" }; - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); - ImGui::SliderInt("SetNextFrameWantCaptureMouse()", &capture_override_mouse, -1, +1, capture_override_desc[capture_override_mouse + 1], ImGuiSliderFlags_AlwaysClamp); - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); - ImGui::SliderInt("SetNextFrameWantCaptureKeyboard()", &capture_override_keyboard, -1, +1, capture_override_desc[capture_override_keyboard + 1], ImGuiSliderFlags_AlwaysClamp); - - ImGui::ColorButton("##panel", ImVec4(0.7f, 0.1f, 0.7f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, ImVec2(256.0f, 192.0f)); // Dummy item - if (ImGui::IsItemHovered() && capture_override_mouse != -1) - ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1); - if (ImGui::IsItemHovered() && capture_override_keyboard != -1) - ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1); - - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Tabbing"); - if (ImGui::TreeNode("Tabbing")) - { - ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); - static char buf[32] = "hello"; - ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); - ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); - ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); - ImGui::PushAllowKeyboardFocus(false); - ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); - ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); - ImGui::PopAllowKeyboardFocus(); - ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Focus from code"); - if (ImGui::TreeNode("Focus from code")) - { - bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine(); - bool focus_2 = ImGui::Button("Focus on 2"); ImGui::SameLine(); - bool focus_3 = ImGui::Button("Focus on 3"); - int has_focus = 0; - static char buf[128] = "click on a button to set focus"; - - if (focus_1) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); - if (ImGui::IsItemActive()) has_focus = 1; - - if (focus_2) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); - if (ImGui::IsItemActive()) has_focus = 2; - - ImGui::PushAllowKeyboardFocus(false); - if (focus_3) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); - if (ImGui::IsItemActive()) has_focus = 3; - ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); - ImGui::PopAllowKeyboardFocus(); - - if (has_focus) - ImGui::Text("Item with focus: %d", has_focus); - else - ImGui::Text("Item with focus: "); - - // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item - static float f3[3] = { 0.0f, 0.0f, 0.0f }; - int focus_ahead = -1; - if (ImGui::Button("Focus on X")) { focus_ahead = 0; } ImGui::SameLine(); - if (ImGui::Button("Focus on Y")) { focus_ahead = 1; } ImGui::SameLine(); - if (ImGui::Button("Focus on Z")) { focus_ahead = 2; } - if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead); - ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f); - - ImGui::TextWrapped("NB: Cursor & selection are preserved when refocusing last used item in code."); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Dragging"); - if (ImGui::TreeNode("Dragging")) - { - ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); - for (int button = 0; button < 3; button++) - { - ImGui::Text("IsMouseDragging(%d):", button); - ImGui::Text(" w/ default threshold: %d,", ImGui::IsMouseDragging(button)); - ImGui::Text(" w/ zero threshold: %d,", ImGui::IsMouseDragging(button, 0.0f)); - ImGui::Text(" w/ large threshold: %d,", ImGui::IsMouseDragging(button, 20.0f)); - } - - ImGui::Button("Drag Me"); - if (ImGui::IsItemActive()) - ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); // Draw a line between the button and the mouse cursor - - // Drag operations gets "unlocked" when the mouse has moved past a certain threshold - // (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher - // threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta(). - ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f); - ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0); - ImVec2 mouse_delta = io.MouseDelta; - ImGui::Text("GetMouseDragDelta(0):"); - ImGui::Text(" w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y); - ImGui::Text(" w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y); - ImGui::Text("io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y); - ImGui::TreePop(); - } - - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse cursors"); - if (ImGui::TreeNode("Mouse cursors")) - { - const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "NotAllowed" }; - IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT); - - ImGuiMouseCursor current = ImGui::GetMouseCursor(); - ImGui::Text("Current mouse cursor = %d: %s", current, mouse_cursors_names[current]); - ImGui::Text("Hover to see mouse cursors:"); - ImGui::SameLine(); HelpMarker( - "Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. " - "If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, " - "otherwise your backend needs to handle it."); - for (int i = 0; i < ImGuiMouseCursor_COUNT; i++) - { - char label[32]; - sprintf(label, "Mouse cursor %d: %s", i, mouse_cursors_names[i]); - ImGui::Bullet(); ImGui::Selectable(label, false); - if (ImGui::IsItemHovered()) - ImGui::SetMouseCursor(i); - } - ImGui::TreePop(); - } - } -} - -//----------------------------------------------------------------------------- -// [SECTION] About Window / ShowAboutWindow() -// Access from Dear ImGui Demo -> Tools -> About -//----------------------------------------------------------------------------- - -void ImGui::ShowAboutWindow(bool* p_open) -{ - if (!ImGui::Begin("About Dear ImGui", p_open, ImGuiWindowFlags_AlwaysAutoResize)) - { - ImGui::End(); - return; - } - IMGUI_DEMO_MARKER("Tools/About Dear ImGui"); - ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); - ImGui::Separator(); - ImGui::Text("By Omar Cornut and all Dear ImGui contributors."); - ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); - - static bool show_config_info = false; - ImGui::Checkbox("Config/Build Information", &show_config_info); - if (show_config_info) - { - ImGuiIO& io = ImGui::GetIO(); - ImGuiStyle& style = ImGui::GetStyle(); - - bool copy_to_clipboard = ImGui::Button("Copy to clipboard"); - ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18); - ImGui::BeginChildFrame(ImGui::GetID("cfg_infos"), child_size, ImGuiWindowFlags_NoMove); - if (copy_to_clipboard) - { - ImGui::LogToClipboard(); - ImGui::LogText("```\n"); // Back quotes will make text appears without formatting when pasting on GitHub - } - - ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); - ImGui::Separator(); - ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert)); - ImGui::Text("define: __cplusplus=%d", (int)__cplusplus); -#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS"); -#endif -#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_KEYIO"); -#endif -#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS - ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS"); -#endif -#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS - ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS"); -#endif -#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS - ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS"); -#endif -#ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS - ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS"); -#endif -#ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS - ImGui::Text("define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS"); -#endif -#ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS - ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS"); -#endif -#ifdef IMGUI_DISABLE_FILE_FUNCTIONS - ImGui::Text("define: IMGUI_DISABLE_FILE_FUNCTIONS"); -#endif -#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS - ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS"); -#endif -#ifdef IMGUI_USE_BGRA_PACKED_COLOR - ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR"); -#endif -#ifdef _WIN32 - ImGui::Text("define: _WIN32"); -#endif -#ifdef _WIN64 - ImGui::Text("define: _WIN64"); -#endif -#ifdef __linux__ - ImGui::Text("define: __linux__"); -#endif -#ifdef __APPLE__ - ImGui::Text("define: __APPLE__"); -#endif -#ifdef _MSC_VER - ImGui::Text("define: _MSC_VER=%d", _MSC_VER); -#endif -#ifdef _MSVC_LANG - ImGui::Text("define: _MSVC_LANG=%d", (int)_MSVC_LANG); -#endif -#ifdef __MINGW32__ - ImGui::Text("define: __MINGW32__"); -#endif -#ifdef __MINGW64__ - ImGui::Text("define: __MINGW64__"); -#endif -#ifdef __GNUC__ - ImGui::Text("define: __GNUC__=%d", (int)__GNUC__); -#endif -#ifdef __clang_version__ - ImGui::Text("define: __clang_version__=%s", __clang_version__); -#endif - ImGui::Separator(); - ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); - ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL"); - ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags); - if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(" NavEnableKeyboard"); - if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(" NavEnableGamepad"); - if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) ImGui::Text(" NavEnableSetMousePos"); - if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard) ImGui::Text(" NavNoCaptureKeyboard"); - if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); - if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); - if (io.MouseDrawCursor) ImGui::Text("io.MouseDrawCursor"); - if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); - if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink"); - if (io.ConfigWindowsResizeFromEdges) ImGui::Text("io.ConfigWindowsResizeFromEdges"); - if (io.ConfigWindowsMoveFromTitleBarOnly) ImGui::Text("io.ConfigWindowsMoveFromTitleBarOnly"); - if (io.ConfigMemoryCompactTimer >= 0.0f) ImGui::Text("io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer); - ImGui::Text("io.BackendFlags: 0x%08X", io.BackendFlags); - if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad"); - if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors"); - if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); - if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset"); - ImGui::Separator(); - ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight); - ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y); - ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); - ImGui::Separator(); - ImGui::Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y); - ImGui::Text("style.WindowBorderSize: %.2f", style.WindowBorderSize); - ImGui::Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y); - ImGui::Text("style.FrameRounding: %.2f", style.FrameRounding); - ImGui::Text("style.FrameBorderSize: %.2f", style.FrameBorderSize); - ImGui::Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y); - ImGui::Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y); - - if (copy_to_clipboard) - { - ImGui::LogText("\n```\n"); - ImGui::LogFinish(); - } - ImGui::EndChildFrame(); - } - ImGui::End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Style Editor / ShowStyleEditor() -//----------------------------------------------------------------------------- -// - ShowFontSelector() -// - ShowStyleSelector() -// - ShowStyleEditor() -//----------------------------------------------------------------------------- - -// Forward declare ShowFontAtlas() which isn't worth putting in public API yet -namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); } - -// Demo helper function to select among loaded fonts. -// Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one. -void ImGui::ShowFontSelector(const char* label) -{ - ImGuiIO& io = ImGui::GetIO(); - ImFont* font_current = ImGui::GetFont(); - if (ImGui::BeginCombo(label, font_current->GetDebugName())) - { - for (int n = 0; n < io.Fonts->Fonts.Size; n++) - { - ImFont* font = io.Fonts->Fonts[n]; - ImGui::PushID((void*)font); - if (ImGui::Selectable(font->GetDebugName(), font == font_current)) - io.FontDefault = font; - ImGui::PopID(); - } - ImGui::EndCombo(); - } - ImGui::SameLine(); - HelpMarker( - "- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n" - "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" - "- Read FAQ and docs/FONTS.md for more details.\n" - "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); -} - -// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. -// Here we use the simplified Combo() api that packs items into a single literal string. -// Useful for quick combo boxes where the choices are known locally. -bool ImGui::ShowStyleSelector(const char* label) -{ - static int style_idx = -1; - if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) - { - switch (style_idx) - { - case 0: ImGui::StyleColorsDark(); break; - case 1: ImGui::StyleColorsLight(); break; - case 2: ImGui::StyleColorsClassic(); break; - } - return true; - } - return false; -} - -void ImGui::ShowStyleEditor(ImGuiStyle* ref) -{ - IMGUI_DEMO_MARKER("Tools/Style Editor"); - // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to - // (without a reference style pointer, we will use one compared locally as a reference) - ImGuiStyle& style = ImGui::GetStyle(); - static ImGuiStyle ref_saved_style; - - // Default to using internal storage as reference - static bool init = true; - if (init && ref == NULL) - ref_saved_style = style; - init = false; - if (ref == NULL) - ref = &ref_saved_style; - - ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f); - - if (ImGui::ShowStyleSelector("Colors##Selector")) - ref_saved_style = style; - ImGui::ShowFontSelector("Fonts##Selector"); - - // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f) - if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f")) - style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding - { bool border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox("WindowBorder", &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } } - ImGui::SameLine(); - { bool border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox("FrameBorder", &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } } - ImGui::SameLine(); - { bool border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox("PopupBorder", &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } } - - // Save/Revert button - if (ImGui::Button("Save Ref")) - *ref = ref_saved_style = style; - ImGui::SameLine(); - if (ImGui::Button("Revert Ref")) - style = *ref; - ImGui::SameLine(); - HelpMarker( - "Save/Revert in local non-persistent storage. Default Colors definition are not affected. " - "Use \"Export\" below to save them somewhere."); - - ImGui::Separator(); - - if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None)) - { - if (ImGui::BeginTabItem("Sizes")) - { - ImGui::Text("Main"); - ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); - ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); - ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); - ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); - ImGui::Text("Borders"); - ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::Text("Rounding"); - ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); - ImGui::Text("Alignment"); - ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); - int window_menu_button_position = style.WindowMenuButtonPosition + 1; - if (ImGui::Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0")) - style.WindowMenuButtonPosition = window_menu_button_position - 1; - ImGui::Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0"); - ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); - ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content."); - ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f"); - ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content."); - ImGui::Text("Safe Area Padding"); - ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); - ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("Colors")) - { - static int output_dest = 0; - static bool output_only_modified = true; - if (ImGui::Button("Export")) - { - if (output_dest == 0) - ImGui::LogToClipboard(); - else - ImGui::LogToTTY(); - ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE); - for (int i = 0; i < ImGuiCol_COUNT; i++) - { - const ImVec4& col = style.Colors[i]; - const char* name = ImGui::GetStyleColorName(i); - if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) - ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE, - name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w); - } - ImGui::LogFinish(); - } - ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); - ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); - - static ImGuiTextFilter filter; - filter.Draw("Filter colors", ImGui::GetFontSize() * 16); - - static ImGuiColorEditFlags alpha_flags = 0; - if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); - if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine(); - if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); - HelpMarker( - "In the color list:\n" - "Left-click on color square to open color picker,\n" - "Right-click to open edit options menu."); - - ImGui::BeginChild("##colors", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); - ImGui::PushItemWidth(-160); - for (int i = 0; i < ImGuiCol_COUNT; i++) - { - const char* name = ImGui::GetStyleColorName(i); - if (!filter.PassFilter(name)) - continue; - ImGui::PushID(i); - ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags); - if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) - { - // Tips: in a real user application, you may want to merge and use an icon font into the main font, - // so instead of "Save"/"Revert" you'd use icons! - // Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient! - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; } - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) { style.Colors[i] = ref->Colors[i]; } - } - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); - ImGui::TextUnformatted(name); - ImGui::PopID(); - } - ImGui::PopItemWidth(); - ImGui::EndChild(); - - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("Fonts")) - { - ImGuiIO& io = ImGui::GetIO(); - ImFontAtlas* atlas = io.Fonts; - HelpMarker("Read FAQ and docs/FONTS.md for details on font loading."); - ImGui::ShowFontAtlas(atlas); - - // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below. - // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds). - const float MIN_SCALE = 0.3f; - const float MAX_SCALE = 2.0f; - HelpMarker( - "Those are old settings provided for convenience.\n" - "However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, " - "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n" - "Using those settings here will give you poor quality results."); - static float window_scale = 1.0f; - ImGui::PushItemWidth(ImGui::GetFontSize() * 8); - if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window - ImGui::SetWindowFontScale(window_scale); - ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything - ImGui::PopItemWidth(); - - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("Rendering")) - { - ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); - ImGui::SameLine(); - HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); - - ImGui::Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex); - ImGui::SameLine(); - HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering)."); - - ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); - ImGui::PushItemWidth(ImGui::GetFontSize() * 8); - ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); - if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; - - // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles. - ImGui::DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp); - if (ImGui::IsItemActive()) - { - ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos()); - ImGui::BeginTooltip(); - ImGui::TextUnformatted("(R = radius, N = number of segments)"); - ImGui::Spacing(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - const float min_widget_width = ImGui::CalcTextSize("N: MMM\nR: MMM").x; - for (int n = 0; n < 8; n++) - { - const float RAD_MIN = 5.0f; - const float RAD_MAX = 70.0f; - const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f); - - ImGui::BeginGroup(); - - ImGui::Text("R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(rad)); - - const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f); - const float offset_x = floorf(canvas_width * 0.5f); - const float offset_y = floorf(RAD_MAX); - - const ImVec2 p1 = ImGui::GetCursorScreenPos(); - draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text)); - ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2)); - - /* - const ImVec2 p2 = ImGui::GetCursorScreenPos(); - draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text)); - ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2)); - */ - - ImGui::EndGroup(); - ImGui::SameLine(); - } - ImGui::EndTooltip(); - } - ImGui::SameLine(); - HelpMarker("When drawing circle primitives with \"num_segments == 0\" tesselation will be calculated automatically."); - - ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. - ImGui::DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha)."); - ImGui::PopItemWidth(); - - ImGui::EndTabItem(); - } - - ImGui::EndTabBar(); - } - - ImGui::PopItemWidth(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() -//----------------------------------------------------------------------------- -// - ShowExampleAppMainMenuBar() -// - ShowExampleMenuFile() -//----------------------------------------------------------------------------- - -// Demonstrate creating a "main" fullscreen menu bar and populating it. -// Note the difference between BeginMainMenuBar() and BeginMenuBar(): -// - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!) -// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it. -static void ShowExampleAppMainMenuBar() -{ - if (ImGui::BeginMainMenuBar()) - { - if (ImGui::BeginMenu("File")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Edit")) - { - if (ImGui::MenuItem("Undo", "CTRL+Z")) {} - if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item - ImGui::Separator(); - if (ImGui::MenuItem("Cut", "CTRL+X")) {} - if (ImGui::MenuItem("Copy", "CTRL+C")) {} - if (ImGui::MenuItem("Paste", "CTRL+V")) {} - ImGui::EndMenu(); - } - ImGui::EndMainMenuBar(); - } -} - -// Note that shortcuts are currently provided for display only -// (future version will add explicit flags to BeginMenu() to request processing shortcuts) -static void ShowExampleMenuFile() -{ - IMGUI_DEMO_MARKER("Examples/Menu"); - ImGui::MenuItem("(demo menu)", NULL, false, false); - if (ImGui::MenuItem("New")) {} - if (ImGui::MenuItem("Open", "Ctrl+O")) {} - if (ImGui::BeginMenu("Open Recent")) - { - ImGui::MenuItem("fish_hat.c"); - ImGui::MenuItem("fish_hat.inl"); - ImGui::MenuItem("fish_hat.h"); - if (ImGui::BeginMenu("More..")) - { - ImGui::MenuItem("Hello"); - ImGui::MenuItem("Sailor"); - if (ImGui::BeginMenu("Recurse..")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - ImGui::EndMenu(); - } - ImGui::EndMenu(); - } - if (ImGui::MenuItem("Save", "Ctrl+S")) {} - if (ImGui::MenuItem("Save As..")) {} - - ImGui::Separator(); - IMGUI_DEMO_MARKER("Examples/Menu/Options"); - if (ImGui::BeginMenu("Options")) - { - static bool enabled = true; - ImGui::MenuItem("Enabled", "", &enabled); - ImGui::BeginChild("child", ImVec2(0, 60), true); - for (int i = 0; i < 10; i++) - ImGui::Text("Scrolling Text %d", i); - ImGui::EndChild(); - static float f = 0.5f; - static int n = 0; - ImGui::SliderFloat("Value", &f, 0.0f, 1.0f); - ImGui::InputFloat("Input", &f, 0.1f); - ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0"); - ImGui::EndMenu(); - } - - IMGUI_DEMO_MARKER("Examples/Menu/Colors"); - if (ImGui::BeginMenu("Colors")) - { - float sz = ImGui::GetTextLineHeight(); - for (int i = 0; i < ImGuiCol_COUNT; i++) - { - const char* name = ImGui::GetStyleColorName((ImGuiCol)i); - ImVec2 p = ImGui::GetCursorScreenPos(); - ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui::GetColorU32((ImGuiCol)i)); - ImGui::Dummy(ImVec2(sz, sz)); - ImGui::SameLine(); - ImGui::MenuItem(name); - } - ImGui::EndMenu(); - } - - // Here we demonstrate appending again to the "Options" menu (which we already created above) - // Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice. - // In a real code-base using it would make senses to use this feature from very different code locations. - if (ImGui::BeginMenu("Options")) // <-- Append! - { - IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu"); - static bool b = true; - ImGui::Checkbox("SomeOption", &b); - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Disabled", false)) // Disabled - { - IM_ASSERT(0); - } - if (ImGui::MenuItem("Checked", NULL, true)) {} - if (ImGui::MenuItem("Quit", "Alt+F4")) {} -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Debug Console / ShowExampleAppConsole() -//----------------------------------------------------------------------------- - -// Demonstrate creating a simple console window, with scrolling, filtering, completion and history. -// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions. -struct ExampleAppConsole -{ - char InputBuf[256]; - ImVector Items; - ImVector Commands; - ImVector History; - int HistoryPos; // -1: new line, 0..History.Size-1 browsing history. - ImGuiTextFilter Filter; - bool AutoScroll; - bool ScrollToBottom; - - ExampleAppConsole() - { - IMGUI_DEMO_MARKER("Examples/Console"); - ClearLog(); - memset(InputBuf, 0, sizeof(InputBuf)); - HistoryPos = -1; - - // "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches. - Commands.push_back("HELP"); - Commands.push_back("HISTORY"); - Commands.push_back("CLEAR"); - Commands.push_back("CLASSIFY"); - AutoScroll = true; - ScrollToBottom = false; - AddLog("Welcome to Dear ImGui!"); - } - ~ExampleAppConsole() - { - ClearLog(); - for (int i = 0; i < History.Size; i++) - free(History[i]); - } - - // Portable helpers - static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; } - static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; } - static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = malloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); } - static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; } - - void ClearLog() - { - for (int i = 0; i < Items.Size; i++) - free(Items[i]); - Items.clear(); - } - - void AddLog(const char* fmt, ...) IM_FMTARGS(2) - { - // FIXME-OPT - char buf[1024]; - va_list args; - va_start(args, fmt); - vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); - buf[IM_ARRAYSIZE(buf)-1] = 0; - va_end(args); - Items.push_back(Strdup(buf)); - } - - void Draw(const char* title, bool* p_open) - { - ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); - if (!ImGui::Begin(title, p_open)) - { - ImGui::End(); - return; - } - - // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar. - // So e.g. IsItemHovered() will return true when hovering the title bar. - // Here we create a context menu only available from the title bar. - if (ImGui::BeginPopupContextItem()) - { - if (ImGui::MenuItem("Close Console")) - *p_open = false; - ImGui::EndPopup(); - } - - ImGui::TextWrapped( - "This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate " - "implementation may want to store entries along with extra data such as timestamp, emitter, etc."); - ImGui::TextWrapped("Enter 'HELP' for help."); - - // TODO: display items starting from the bottom - - if (ImGui::SmallButton("Add Debug Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } - ImGui::SameLine(); - if (ImGui::SmallButton("Add Debug Error")) { AddLog("[error] something went wrong"); } - ImGui::SameLine(); - if (ImGui::SmallButton("Clear")) { ClearLog(); } - ImGui::SameLine(); - bool copy_to_clipboard = ImGui::SmallButton("Copy"); - //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); } - - ImGui::Separator(); - - // Options menu - if (ImGui::BeginPopup("Options")) - { - ImGui::Checkbox("Auto-scroll", &AutoScroll); - ImGui::EndPopup(); - } - - // Options, Filter - if (ImGui::Button("Options")) - ImGui::OpenPopup("Options"); - ImGui::SameLine(); - Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); - ImGui::Separator(); - - // Reserve enough left-over height for 1 separator + 1 input text - const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false, ImGuiWindowFlags_HorizontalScrollbar); - if (ImGui::BeginPopupContextWindow()) - { - if (ImGui::Selectable("Clear")) ClearLog(); - ImGui::EndPopup(); - } - - // Display every line as a separate entry so we can change their color or add custom widgets. - // If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end()); - // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping - // to only process visible items. The clipper will automatically measure the height of your first item and then - // "seek" to display only items in the visible area. - // To use the clipper we can replace your standard loop: - // for (int i = 0; i < Items.Size; i++) - // With: - // ImGuiListClipper clipper; - // clipper.Begin(Items.Size); - // while (clipper.Step()) - // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - // - That your items are evenly spaced (same height) - // - That you have cheap random access to your elements (you can access them given their index, - // without processing all the ones before) - // You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property. - // We would need random-access on the post-filtered list. - // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices - // or offsets of items that passed the filtering test, recomputing this array when user changes the filter, - // and appending newly elements as they are inserted. This is left as a task to the user until we can manage - // to improve this example code! - // If your items are of variable height: - // - Split them into same height items would be simpler and facilitate random-seeking into your list. - // - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items. - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing - if (copy_to_clipboard) - ImGui::LogToClipboard(); - for (int i = 0; i < Items.Size; i++) - { - const char* item = Items[i]; - if (!Filter.PassFilter(item)) - continue; - - // Normally you would store more information in your item than just a string. - // (e.g. make Items[] an array of structure, store color/type etc.) - ImVec4 color; - bool has_color = false; - if (strstr(item, "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; } - else if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; } - if (has_color) - ImGui::PushStyleColor(ImGuiCol_Text, color); - ImGui::TextUnformatted(item); - if (has_color) - ImGui::PopStyleColor(); - } - if (copy_to_clipboard) - ImGui::LogFinish(); - - if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) - ImGui::SetScrollHereY(1.0f); - ScrollToBottom = false; - - ImGui::PopStyleVar(); - ImGui::EndChild(); - ImGui::Separator(); - - // Command-line - bool reclaim_focus = false; - ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; - if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) - { - char* s = InputBuf; - Strtrim(s); - if (s[0]) - ExecCommand(s); - strcpy(s, ""); - reclaim_focus = true; - } - - // Auto-focus on window apparition - ImGui::SetItemDefaultFocus(); - if (reclaim_focus) - ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget - - ImGui::End(); - } - - void ExecCommand(const char* command_line) - { - AddLog("# %s\n", command_line); - - // Insert into history. First find match and delete it so it can be pushed to the back. - // This isn't trying to be smart or optimal. - HistoryPos = -1; - for (int i = History.Size - 1; i >= 0; i--) - if (Stricmp(History[i], command_line) == 0) - { - free(History[i]); - History.erase(History.begin() + i); - break; - } - History.push_back(Strdup(command_line)); - - // Process command - if (Stricmp(command_line, "CLEAR") == 0) - { - ClearLog(); - } - else if (Stricmp(command_line, "HELP") == 0) - { - AddLog("Commands:"); - for (int i = 0; i < Commands.Size; i++) - AddLog("- %s", Commands[i]); - } - else if (Stricmp(command_line, "HISTORY") == 0) - { - int first = History.Size - 10; - for (int i = first > 0 ? first : 0; i < History.Size; i++) - AddLog("%3d: %s\n", i, History[i]); - } - else - { - AddLog("Unknown command: '%s'\n", command_line); - } - - // On command input, we scroll to bottom even if AutoScroll==false - ScrollToBottom = true; - } - - // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks - static int TextEditCallbackStub(ImGuiInputTextCallbackData* data) - { - ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; - return console->TextEditCallback(data); - } - - int TextEditCallback(ImGuiInputTextCallbackData* data) - { - //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd); - switch (data->EventFlag) - { - case ImGuiInputTextFlags_CallbackCompletion: - { - // Example of TEXT COMPLETION - - // Locate beginning of current word - const char* word_end = data->Buf + data->CursorPos; - const char* word_start = word_end; - while (word_start > data->Buf) - { - const char c = word_start[-1]; - if (c == ' ' || c == '\t' || c == ',' || c == ';') - break; - word_start--; - } - - // Build a list of candidates - ImVector candidates; - for (int i = 0; i < Commands.Size; i++) - if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0) - candidates.push_back(Commands[i]); - - if (candidates.Size == 0) - { - // No match - AddLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start); - } - else if (candidates.Size == 1) - { - // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing. - data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start)); - data->InsertChars(data->CursorPos, candidates[0]); - data->InsertChars(data->CursorPos, " "); - } - else - { - // Multiple matches. Complete as much as we can.. - // So inputing "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches. - int match_len = (int)(word_end - word_start); - for (;;) - { - int c = 0; - bool all_candidates_matches = true; - for (int i = 0; i < candidates.Size && all_candidates_matches; i++) - if (i == 0) - c = toupper(candidates[i][match_len]); - else if (c == 0 || c != toupper(candidates[i][match_len])) - all_candidates_matches = false; - if (!all_candidates_matches) - break; - match_len++; - } - - if (match_len > 0) - { - data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start)); - data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len); - } - - // List matches - AddLog("Possible matches:\n"); - for (int i = 0; i < candidates.Size; i++) - AddLog("- %s\n", candidates[i]); - } - - break; - } - case ImGuiInputTextFlags_CallbackHistory: - { - // Example of HISTORY - const int prev_history_pos = HistoryPos; - if (data->EventKey == ImGuiKey_UpArrow) - { - if (HistoryPos == -1) - HistoryPos = History.Size - 1; - else if (HistoryPos > 0) - HistoryPos--; - } - else if (data->EventKey == ImGuiKey_DownArrow) - { - if (HistoryPos != -1) - if (++HistoryPos >= History.Size) - HistoryPos = -1; - } - - // A better implementation would preserve the data on the current input line along with cursor position. - if (prev_history_pos != HistoryPos) - { - const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : ""; - data->DeleteChars(0, data->BufTextLen); - data->InsertChars(0, history_str); - } - } - } - return 0; - } -}; - -static void ShowExampleAppConsole(bool* p_open) -{ - static ExampleAppConsole console; - console.Draw("Example: Console", p_open); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Debug Log / ShowExampleAppLog() -//----------------------------------------------------------------------------- - -// Usage: -// static ExampleAppLog my_log; -// my_log.AddLog("Hello %d world\n", 123); -// my_log.Draw("title"); -struct ExampleAppLog -{ - ImGuiTextBuffer Buf; - ImGuiTextFilter Filter; - ImVector LineOffsets; // Index to lines offset. We maintain this with AddLog() calls. - bool AutoScroll; // Keep scrolling if already at the bottom. - - ExampleAppLog() - { - AutoScroll = true; - Clear(); - } - - void Clear() - { - Buf.clear(); - LineOffsets.clear(); - LineOffsets.push_back(0); - } - - void AddLog(const char* fmt, ...) IM_FMTARGS(2) - { - int old_size = Buf.size(); - va_list args; - va_start(args, fmt); - Buf.appendfv(fmt, args); - va_end(args); - for (int new_size = Buf.size(); old_size < new_size; old_size++) - if (Buf[old_size] == '\n') - LineOffsets.push_back(old_size + 1); - } - - void Draw(const char* title, bool* p_open = NULL) - { - if (!ImGui::Begin(title, p_open)) - { - ImGui::End(); - return; - } - - // Options menu - if (ImGui::BeginPopup("Options")) - { - ImGui::Checkbox("Auto-scroll", &AutoScroll); - ImGui::EndPopup(); - } - - // Main window - if (ImGui::Button("Options")) - ImGui::OpenPopup("Options"); - ImGui::SameLine(); - bool clear = ImGui::Button("Clear"); - ImGui::SameLine(); - bool copy = ImGui::Button("Copy"); - ImGui::SameLine(); - Filter.Draw("Filter", -100.0f); - - ImGui::Separator(); - ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar); - - if (clear) - Clear(); - if (copy) - ImGui::LogToClipboard(); - - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - const char* buf = Buf.begin(); - const char* buf_end = Buf.end(); - if (Filter.IsActive()) - { - // In this example we don't use the clipper when Filter is enabled. - // This is because we don't have a random access on the result on our filter. - // A real application processing logs with ten of thousands of entries may want to store the result of - // search/filter.. especially if the filtering function is not trivial (e.g. reg-exp). - for (int line_no = 0; line_no < LineOffsets.Size; line_no++) - { - const char* line_start = buf + LineOffsets[line_no]; - const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; - if (Filter.PassFilter(line_start, line_end)) - ImGui::TextUnformatted(line_start, line_end); - } - } - else - { - // The simplest and easy way to display the entire buffer: - // ImGui::TextUnformatted(buf_begin, buf_end); - // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward - // to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are - // within the visible area. - // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them - // on your side is recommended. Using ImGuiListClipper requires - // - A) random access into your data - // - B) items all being the same height, - // both of which we can handle since we an array pointing to the beginning of each line of text. - // When using the filter (in the block of code above) we don't have random access into the data to display - // anymore, which is why we don't use the clipper. Storing or skimming through the search result would make - // it possible (and would be recommended if you want to search through tens of thousands of entries). - ImGuiListClipper clipper; - clipper.Begin(LineOffsets.Size); - while (clipper.Step()) - { - for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) - { - const char* line_start = buf + LineOffsets[line_no]; - const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; - ImGui::TextUnformatted(line_start, line_end); - } - } - clipper.End(); - } - ImGui::PopStyleVar(); - - if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) - ImGui::SetScrollHereY(1.0f); - - ImGui::EndChild(); - ImGui::End(); - } -}; - -// Demonstrate creating a simple log window with basic filtering. -static void ShowExampleAppLog(bool* p_open) -{ - static ExampleAppLog log; - - // For the demo: add a debug button _BEFORE_ the normal log window contents - // We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window. - // Most of the contents of the window will be added by the log.Draw() call. - ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); - ImGui::Begin("Example: Log", p_open); - IMGUI_DEMO_MARKER("Examples/Log"); - if (ImGui::SmallButton("[Debug] Add 5 entries")) - { - static int counter = 0; - const char* categories[3] = { "info", "warn", "error" }; - const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" }; - for (int n = 0; n < 5; n++) - { - const char* category = categories[counter % IM_ARRAYSIZE(categories)]; - const char* word = words[counter % IM_ARRAYSIZE(words)]; - log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n", - ImGui::GetFrameCount(), category, ImGui::GetTime(), word); - counter++; - } - } - ImGui::End(); - - // Actually call in the regular Log helper (which will Begin() into the same window as we just did) - log.Draw("Example: Log", p_open); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Simple Layout / ShowExampleAppLayout() -//----------------------------------------------------------------------------- - -// Demonstrate create a window with multiple child windows. -static void ShowExampleAppLayout(bool* p_open) -{ - ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar)) - { - IMGUI_DEMO_MARKER("Examples/Simple layout"); - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("File")) - { - if (ImGui::MenuItem("Close")) *p_open = false; - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - - // Left - static int selected = 0; - { - ImGui::BeginChild("left pane", ImVec2(150, 0), true); - for (int i = 0; i < 100; i++) - { - // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav - char label[128]; - sprintf(label, "MyObject %d", i); - if (ImGui::Selectable(label, selected == i)) - selected = i; - } - ImGui::EndChild(); - } - ImGui::SameLine(); - - // Right - { - ImGui::BeginGroup(); - ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us - ImGui::Text("MyObject: %d", selected); - ImGui::Separator(); - if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None)) - { - if (ImGui::BeginTabItem("Description")) - { - ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Details")) - { - ImGui::Text("ID: 0123456789"); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } - ImGui::EndChild(); - if (ImGui::Button("Revert")) {} - ImGui::SameLine(); - if (ImGui::Button("Save")) {} - ImGui::EndGroup(); - } - } - ImGui::End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() -//----------------------------------------------------------------------------- - -static void ShowPlaceholderObject(const char* prefix, int uid) -{ - // Use object uid as identifier. Most commonly you could also use the object pointer as a base ID. - ImGui::PushID(uid); - - // Text and Tree nodes are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the tree lines equal high. - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - bool node_open = ImGui::TreeNode("Object", "%s_%u", prefix, uid); - ImGui::TableSetColumnIndex(1); - ImGui::Text("my sailor is rich"); - - if (node_open) - { - static float placeholder_members[8] = { 0.0f, 0.0f, 1.0f, 3.1416f, 100.0f, 999.0f }; - for (int i = 0; i < 8; i++) - { - ImGui::PushID(i); // Use field index as identifier. - if (i < 2) - { - ShowPlaceholderObject("Child", 424242); - } - else - { - // Here we use a TreeNode to highlight on hover (we could use e.g. Selectable as well) - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Bullet; - ImGui::TreeNodeEx("Field", flags, "Field_%d", i); - - ImGui::TableSetColumnIndex(1); - ImGui::SetNextItemWidth(-FLT_MIN); - if (i >= 5) - ImGui::InputFloat("##value", &placeholder_members[i], 1.0f); - else - ImGui::DragFloat("##value", &placeholder_members[i], 0.01f); - ImGui::NextColumn(); - } - ImGui::PopID(); - } - ImGui::TreePop(); - } - ImGui::PopID(); -} - -// Demonstrate create a simple property editor. -static void ShowExampleAppPropertyEditor(bool* p_open) -{ - ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("Example: Property editor", p_open)) - { - ImGui::End(); - return; - } - IMGUI_DEMO_MARKER("Examples/Property Editor"); - - HelpMarker( - "This example shows how you may implement a property editor using two columns.\n" - "All objects/fields data are dummies here.\n" - "Remember that in many simple cases, you can use ImGui::SameLine(xxx) to position\n" - "your cursor horizontally instead of using the Columns() API."); - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); - if (ImGui::BeginTable("split", 2, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable)) - { - // Iterate placeholder objects (all the same data) - for (int obj_i = 0; obj_i < 4; obj_i++) - { - ShowPlaceholderObject("Object", obj_i); - //ImGui::Separator(); - } - ImGui::EndTable(); - } - ImGui::PopStyleVar(); - ImGui::End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Long Text / ShowExampleAppLongText() -//----------------------------------------------------------------------------- - -// Demonstrate/test rendering huge amount of text, and the incidence of clipping. -static void ShowExampleAppLongText(bool* p_open) -{ - ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("Example: Long text display", p_open)) - { - ImGui::End(); - return; - } - IMGUI_DEMO_MARKER("Examples/Long text display"); - - static int test_type = 0; - static ImGuiTextBuffer log; - static int lines = 0; - ImGui::Text("Printing unusually long amount of text."); - ImGui::Combo("Test type", &test_type, - "Single call to TextUnformatted()\0" - "Multiple calls to Text(), clipped\0" - "Multiple calls to Text(), not clipped (slow)\0"); - ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size()); - if (ImGui::Button("Clear")) { log.clear(); lines = 0; } - ImGui::SameLine(); - if (ImGui::Button("Add 1000 lines")) - { - for (int i = 0; i < 1000; i++) - log.appendf("%i The quick brown fox jumps over the lazy dog\n", lines + i); - lines += 1000; - } - ImGui::BeginChild("Log"); - switch (test_type) - { - case 0: - // Single call to TextUnformatted() with a big buffer - ImGui::TextUnformatted(log.begin(), log.end()); - break; - case 1: - { - // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper. - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - ImGuiListClipper clipper; - clipper.Begin(lines); - while (clipper.Step()) - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); - ImGui::PopStyleVar(); - break; - } - case 2: - // Multiple calls to Text(), not clipped (slow) - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - for (int i = 0; i < lines; i++) - ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); - ImGui::PopStyleVar(); - break; - } - ImGui::EndChild(); - ImGui::End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() -//----------------------------------------------------------------------------- - -// Demonstrate creating a window which gets auto-resized according to its content. -static void ShowExampleAppAutoResize(bool* p_open) -{ - if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize)) - { - ImGui::End(); - return; - } - IMGUI_DEMO_MARKER("Examples/Auto-resizing window"); - - static int lines = 10; - ImGui::TextUnformatted( - "Window will resize every-frame to the size of its content.\n" - "Note that you probably don't want to query the window size to\n" - "output your content because that would create a feedback loop."); - ImGui::SliderInt("Number of lines", &lines, 1, 20); - for (int i = 0; i < lines; i++) - ImGui::Text("%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally - ImGui::End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize() -//----------------------------------------------------------------------------- - -// Demonstrate creating a window with custom resize constraints. -static void ShowExampleAppConstrainedResize(bool* p_open) -{ - struct CustomConstraints - { - // Helper functions to demonstrate programmatic constraints - static void Square(ImGuiSizeCallbackData* data) { data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y); } - static void Step(ImGuiSizeCallbackData* data) { float step = (float)(int)(intptr_t)data->UserData; data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); } - }; - - const char* test_desc[] = - { - "Resize vertical only", - "Resize horizontal only", - "Width > 100, Height > 100", - "Width 400-500", - "Height 400-500", - "Custom: Always Square", - "Custom: Fixed Steps (100)", - }; - - static bool auto_resize = false; - static int type = 0; - static int display_lines = 10; - if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Vertical only - if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Horizontal only - if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 - if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width 400-500 - if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, 500)); // Height 400-500 - if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square - if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)(intptr_t)100); // Fixed Step - - ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; - if (ImGui::Begin("Example: Constrained Resize", p_open, flags)) - { - IMGUI_DEMO_MARKER("Examples/Constrained Resizing window"); - if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); - if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); - if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } - ImGui::SetNextItemWidth(200); - ImGui::Combo("Constraint", &type, test_desc, IM_ARRAYSIZE(test_desc)); - ImGui::SetNextItemWidth(200); - ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100); - ImGui::Checkbox("Auto-resize", &auto_resize); - for (int i = 0; i < display_lines; i++) - ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, ""); - } - ImGui::End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay() -//----------------------------------------------------------------------------- - -// Demonstrate creating a simple static window with no decoration -// + a context-menu to choose which corner of the screen to use. -static void ShowExampleAppSimpleOverlay(bool* p_open) -{ - static int corner = 0; - ImGuiIO& io = ImGui::GetIO(); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; - if (corner != -1) - { - const float PAD = 10.0f; - const ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any! - ImVec2 work_size = viewport->WorkSize; - ImVec2 window_pos, window_pos_pivot; - window_pos.x = (corner & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD); - window_pos.y = (corner & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD); - window_pos_pivot.x = (corner & 1) ? 1.0f : 0.0f; - window_pos_pivot.y = (corner & 2) ? 1.0f : 0.0f; - ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); - window_flags |= ImGuiWindowFlags_NoMove; - } - ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background - if (ImGui::Begin("Example: Simple overlay", p_open, window_flags)) - { - IMGUI_DEMO_MARKER("Examples/Simple Overlay"); - ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); - ImGui::Separator(); - if (ImGui::IsMousePosValid()) - ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y); - else - ImGui::Text("Mouse Position: "); - if (ImGui::BeginPopupContextWindow()) - { - if (ImGui::MenuItem("Custom", NULL, corner == -1)) corner = -1; - if (ImGui::MenuItem("Top-left", NULL, corner == 0)) corner = 0; - if (ImGui::MenuItem("Top-right", NULL, corner == 1)) corner = 1; - if (ImGui::MenuItem("Bottom-left", NULL, corner == 2)) corner = 2; - if (ImGui::MenuItem("Bottom-right", NULL, corner == 3)) corner = 3; - if (p_open && ImGui::MenuItem("Close")) *p_open = false; - ImGui::EndPopup(); - } - } - ImGui::End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen() -//----------------------------------------------------------------------------- - -// Demonstrate creating a window covering the entire screen/viewport -static void ShowExampleAppFullscreen(bool* p_open) -{ - static bool use_work_area = true; - static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings; - - // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.) - // Based on your use case you may want one of the other. - const ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos); - ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size); - - if (ImGui::Begin("Example: Fullscreen window", p_open, flags)) - { - ImGui::Checkbox("Use work area instead of main area", &use_work_area); - ImGui::SameLine(); - HelpMarker("Main Area = entire viewport,\nWork Area = entire viewport minus sections used by the main menu bars, task bars etc.\n\nEnable the main-menu bar in Examples menu to see the difference."); - - ImGui::CheckboxFlags("ImGuiWindowFlags_NoBackground", &flags, ImGuiWindowFlags_NoBackground); - ImGui::CheckboxFlags("ImGuiWindowFlags_NoDecoration", &flags, ImGuiWindowFlags_NoDecoration); - ImGui::Indent(); - ImGui::CheckboxFlags("ImGuiWindowFlags_NoTitleBar", &flags, ImGuiWindowFlags_NoTitleBar); - ImGui::CheckboxFlags("ImGuiWindowFlags_NoCollapse", &flags, ImGuiWindowFlags_NoCollapse); - ImGui::CheckboxFlags("ImGuiWindowFlags_NoScrollbar", &flags, ImGuiWindowFlags_NoScrollbar); - ImGui::Unindent(); - - if (p_open && ImGui::Button("Close this window")) - *p_open = false; - } - ImGui::End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() -//----------------------------------------------------------------------------- - -// Demonstrate using "##" and "###" in identifiers to manipulate ID generation. -// This apply to all regular items as well. -// Read FAQ section "How can I have multiple widgets with the same label?" for details. -static void ShowExampleAppWindowTitles(bool*) -{ - const ImGuiViewport* viewport = ImGui::GetMainViewport(); - const ImVec2 base_pos = viewport->Pos; - - // By default, Windows are uniquely identified by their title. - // You can use the "##" and "###" markers to manipulate the display/ID. - - // Using "##" to display same title but have unique identifier. - ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver); - ImGui::Begin("Same title as another window##1"); - IMGUI_DEMO_MARKER("Examples/Manipulating window titles"); - ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); - ImGui::End(); - - ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver); - ImGui::Begin("Same title as another window##2"); - ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); - ImGui::End(); - - // Using "###" to display a changing title but keep a static identifier "AnimatedTitle" - char buf[128]; - sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount()); - ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver); - ImGui::Begin(buf); - ImGui::Text("This window has a changing title."); - ImGui::End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() -//----------------------------------------------------------------------------- - -// Demonstrate using the low-level ImDrawList to draw custom shapes. -static void ShowExampleAppCustomRendering(bool* p_open) -{ - if (!ImGui::Begin("Example: Custom rendering", p_open)) - { - ImGui::End(); - return; - } - IMGUI_DEMO_MARKER("Examples/Custom Rendering"); - - // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of - // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your - // types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not - // exposed outside (to avoid messing with your types) In this example we are not using the maths operators! - - if (ImGui::BeginTabBar("##TabBar")) - { - if (ImGui::BeginTabItem("Primitives")) - { - ImGui::PushItemWidth(-ImGui::GetFontSize() * 15); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - - // Draw gradients - // (note that those are currently exacerbating our sRGB/Linear issues) - // Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well.. - ImGui::Text("Gradients"); - ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight()); - { - ImVec2 p0 = ImGui::GetCursorScreenPos(); - ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y); - ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255)); - ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255)); - draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a); - ImGui::InvisibleButton("##gradient1", gradient_size); - } - { - ImVec2 p0 = ImGui::GetCursorScreenPos(); - ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y); - ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255)); - ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255)); - draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a); - ImGui::InvisibleButton("##gradient2", gradient_size); - } - - // Draw a bunch of primitives - ImGui::Text("All primitives"); - static float sz = 36.0f; - static float thickness = 3.0f; - static int ngon_sides = 6; - static bool circle_segments_override = false; - static int circle_segments_override_v = 12; - static bool curve_segments_override = false; - static int curve_segments_override_v = 8; - static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); - ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 100.0f, "%.0f"); - ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); - ImGui::SliderInt("N-gon sides", &ngon_sides, 3, 12); - ImGui::Checkbox("##circlesegmentoverride", &circle_segments_override); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - circle_segments_override |= ImGui::SliderInt("Circle segments override", &circle_segments_override_v, 3, 40); - ImGui::Checkbox("##curvessegmentoverride", &curve_segments_override); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - curve_segments_override |= ImGui::SliderInt("Curves segments override", &curve_segments_override_v, 3, 40); - ImGui::ColorEdit4("Color", &colf.x); - - const ImVec2 p = ImGui::GetCursorScreenPos(); - const ImU32 col = ImColor(colf); - const float spacing = 10.0f; - const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight; - const float rounding = sz / 5.0f; - const int circle_segments = circle_segments_override ? circle_segments_override_v : 0; - const int curve_segments = curve_segments_override ? curve_segments_override_v : 0; - float x = p.x + 4.0f; - float y = p.y + 4.0f; - for (int n = 0; n < 2; n++) - { - // First line uses a thickness of 1.0f, second line uses the configurable thickness - float th = (n == 0) ? 1.0f : thickness; - draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon - draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle - draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th); x += sz + spacing; // Square - draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th); x += sz + spacing; // Square with all rounded corners - draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners - draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing; // Triangle - //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle - draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line - - // Quadratic Bezier Curve (3 control points) - ImVec2 cp3[3] = { ImVec2(x, y + sz * 0.6f), ImVec2(x + sz * 0.5f, y - sz * 0.4f), ImVec2(x + sz, y + sz) }; - draw_list->AddBezierQuadratic(cp3[0], cp3[1], cp3[2], col, th, curve_segments); x += sz + spacing; - - // Cubic Bezier Curve (4 control points) - ImVec2 cp4[4] = { ImVec2(x, y), ImVec2(x + sz * 1.3f, y + sz * 0.3f), ImVec2(x + sz - sz * 1.3f, y + sz - sz * 0.3f), ImVec2(x + sz, y + sz) }; - draw_list->AddBezierCubic(cp4[0], cp4[1], cp4[2], cp4[3], col, th, curve_segments); - - x = p.x + 4; - y += sz + spacing; - } - draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz*0.5f, col, ngon_sides); x += sz + spacing; // N-gon - draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments); x += sz + spacing; // Circle - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br); x += sz + spacing; // Square with two rounded corners - draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col); x += sz + spacing; // Triangle - //draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine) - draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255)); - - ImGui::Dummy(ImVec2((sz + spacing) * 10.2f, (sz + spacing) * 3.0f)); - ImGui::PopItemWidth(); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("Canvas")) - { - static ImVector points; - static ImVec2 scrolling(0.0f, 0.0f); - static bool opt_enable_grid = true; - static bool opt_enable_context_menu = true; - static bool adding_line = false; - - ImGui::Checkbox("Enable grid", &opt_enable_grid); - ImGui::Checkbox("Enable context menu", &opt_enable_context_menu); - ImGui::Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu."); - - // Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling. - // Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls. - // To use a child window instead we could use, e.g: - // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding - // ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color - // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_NoMove); - // ImGui::PopStyleColor(); - // ImGui::PopStyleVar(); - // [...] - // ImGui::EndChild(); - - // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive() - ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! - ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available - if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f; - if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f; - ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y); - - // Draw border and background color - ImGuiIO& io = ImGui::GetIO(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255)); - draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255)); - - // This will catch our interactions - ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); - const bool is_hovered = ImGui::IsItemHovered(); // Hovered - const bool is_active = ImGui::IsItemActive(); // Held - const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin - const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y); - - // Add first and second point - if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) - { - points.push_back(mouse_pos_in_canvas); - points.push_back(mouse_pos_in_canvas); - adding_line = true; - } - if (adding_line) - { - points.back() = mouse_pos_in_canvas; - if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) - adding_line = false; - } - - // Pan (we use a zero mouse threshold when there's no context menu) - // You may decide to make that threshold dynamic based on whether the mouse is hovering something etc. - const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f; - if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan)) - { - scrolling.x += io.MouseDelta.x; - scrolling.y += io.MouseDelta.y; - } - - // Context menu (under default mouse threshold) - ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right); - if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f) - ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); - if (ImGui::BeginPopup("context")) - { - if (adding_line) - points.resize(points.size() - 2); - adding_line = false; - if (ImGui::MenuItem("Remove one", NULL, false, points.Size > 0)) { points.resize(points.size() - 2); } - if (ImGui::MenuItem("Remove all", NULL, false, points.Size > 0)) { points.clear(); } - ImGui::EndPopup(); - } - - // Draw grid + all lines in the canvas - draw_list->PushClipRect(canvas_p0, canvas_p1, true); - if (opt_enable_grid) - { - const float GRID_STEP = 64.0f; - for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP) - draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40)); - for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP) - draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40)); - } - for (int n = 0; n < points.Size; n += 2) - draw_list->AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0f); - draw_list->PopClipRect(); - - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("BG/FG draw lists")) - { - static bool draw_bg = true; - static bool draw_fg = true; - ImGui::Checkbox("Draw in Background draw list", &draw_bg); - ImGui::SameLine(); HelpMarker("The Background draw list will be rendered below every Dear ImGui windows."); - ImGui::Checkbox("Draw in Foreground draw list", &draw_fg); - ImGui::SameLine(); HelpMarker("The Foreground draw list will be rendered over every Dear ImGui windows."); - ImVec2 window_pos = ImGui::GetWindowPos(); - ImVec2 window_size = ImGui::GetWindowSize(); - ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f); - if (draw_bg) - ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 0, 10 + 4); - if (draw_fg) - ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 0, 10); - ImGui::EndTabItem(); - } - - ImGui::EndTabBar(); - } - - ImGui::End(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() -//----------------------------------------------------------------------------- - -// Simplified structure to mimic a Document model -struct MyDocument -{ - const char* Name; // Document title - bool Open; // Set when open (we keep an array of all available documents to simplify demo code!) - bool OpenPrev; // Copy of Open from last update. - bool Dirty; // Set when the document has been modified - bool WantClose; // Set when the document - ImVec4 Color; // An arbitrary variable associated to the document - - MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f)) - { - Name = name; - Open = OpenPrev = open; - Dirty = false; - WantClose = false; - Color = color; - } - void DoOpen() { Open = true; } - void DoQueueClose() { WantClose = true; } - void DoForceClose() { Open = false; Dirty = false; } - void DoSave() { Dirty = false; } - - // Display placeholder contents for the Document - static void DisplayContents(MyDocument* doc) - { - ImGui::PushID(doc); - ImGui::Text("Document \"%s\"", doc->Name); - ImGui::PushStyleColor(ImGuiCol_Text, doc->Color); - ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); - ImGui::PopStyleColor(); - if (ImGui::Button("Modify", ImVec2(100, 0))) - doc->Dirty = true; - ImGui::SameLine(); - if (ImGui::Button("Save", ImVec2(100, 0))) - doc->DoSave(); - ImGui::ColorEdit3("color", &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior. - ImGui::PopID(); - } - - // Display context menu for the Document - static void DisplayContextMenu(MyDocument* doc) - { - if (!ImGui::BeginPopupContextItem()) - return; - - char buf[256]; - sprintf(buf, "Save %s", doc->Name); - if (ImGui::MenuItem(buf, "CTRL+S", false, doc->Open)) - doc->DoSave(); - if (ImGui::MenuItem("Close", "CTRL+W", false, doc->Open)) - doc->DoQueueClose(); - ImGui::EndPopup(); - } -}; - -struct ExampleAppDocuments -{ - ImVector Documents; - - ExampleAppDocuments() - { - Documents.push_back(MyDocument("Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f))); - Documents.push_back(MyDocument("Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); - Documents.push_back(MyDocument("Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); - Documents.push_back(MyDocument("Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); - Documents.push_back(MyDocument("A Rather Long Title", false)); - Documents.push_back(MyDocument("Some Document", false)); - } -}; - -// [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface. -// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo, -// as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for -// the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has -// disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively -// give the impression of a flicker for one frame. -// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch. -// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag. -static void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app) -{ - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - { - MyDocument* doc = &app.Documents[doc_n]; - if (!doc->Open && doc->OpenPrev) - ImGui::SetTabItemClosed(doc->Name); - doc->OpenPrev = doc->Open; - } -} - -void ShowExampleAppDocuments(bool* p_open) -{ - static ExampleAppDocuments app; - - // Options - static bool opt_reorderable = true; - static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; - - bool window_contents_visible = ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar); - if (!window_contents_visible) - { - ImGui::End(); - return; - } - - // Menu - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("File")) - { - int open_count = 0; - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - open_count += app.Documents[doc_n].Open ? 1 : 0; - - if (ImGui::BeginMenu("Open", open_count < app.Documents.Size)) - { - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - { - MyDocument* doc = &app.Documents[doc_n]; - if (!doc->Open) - if (ImGui::MenuItem(doc->Name)) - doc->DoOpen(); - } - ImGui::EndMenu(); - } - if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0)) - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - app.Documents[doc_n].DoQueueClose(); - if (ImGui::MenuItem("Exit", "Alt+F4")) {} - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - - // [Debug] List documents with one checkbox for each - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - { - MyDocument* doc = &app.Documents[doc_n]; - if (doc_n > 0) - ImGui::SameLine(); - ImGui::PushID(doc); - if (ImGui::Checkbox(doc->Name, &doc->Open)) - if (!doc->Open) - doc->DoForceClose(); - ImGui::PopID(); - } - - ImGui::Separator(); - - // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags. - // They have multiple effects: - // - Display a dot next to the title. - // - Tab is selected when clicking the X close button. - // - Closure is not assumed (will wait for user to stop submitting the tab). - // Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. - // We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty - // hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window. - // The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole. - - // Submit Tab Bar and Tabs - { - ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); - if (ImGui::BeginTabBar("##tabs", tab_bar_flags)) - { - if (opt_reorderable) - NotifyOfDocumentsClosedElsewhere(app); - - // [DEBUG] Stress tests - //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on. - //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway.. - - // Submit Tabs - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - { - MyDocument* doc = &app.Documents[doc_n]; - if (!doc->Open) - continue; - - ImGuiTabItemFlags tab_flags = (doc->Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); - bool visible = ImGui::BeginTabItem(doc->Name, &doc->Open, tab_flags); - - // Cancel attempt to close when unsaved add to save queue so we can display a popup. - if (!doc->Open && doc->Dirty) - { - doc->Open = true; - doc->DoQueueClose(); - } - - MyDocument::DisplayContextMenu(doc); - if (visible) - { - MyDocument::DisplayContents(doc); - ImGui::EndTabItem(); - } - } - - ImGui::EndTabBar(); - } - } - - // Update closing queue - static ImVector close_queue; - if (close_queue.empty()) - { - // Close queue is locked once we started a popup - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - { - MyDocument* doc = &app.Documents[doc_n]; - if (doc->WantClose) - { - doc->WantClose = false; - close_queue.push_back(doc); - } - } - } - - // Display closing confirmation UI - if (!close_queue.empty()) - { - int close_queue_unsaved_documents = 0; - for (int n = 0; n < close_queue.Size; n++) - if (close_queue[n]->Dirty) - close_queue_unsaved_documents++; - - if (close_queue_unsaved_documents == 0) - { - // Close documents when all are unsaved - for (int n = 0; n < close_queue.Size; n++) - close_queue[n]->DoForceClose(); - close_queue.clear(); - } - else - { - if (!ImGui::IsPopupOpen("Save?")) - ImGui::OpenPopup("Save?"); - if (ImGui::BeginPopupModal("Save?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) - { - ImGui::Text("Save change to the following items?"); - float item_height = ImGui::GetTextLineHeightWithSpacing(); - if (ImGui::BeginChildFrame(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height))) - { - for (int n = 0; n < close_queue.Size; n++) - if (close_queue[n]->Dirty) - ImGui::Text("%s", close_queue[n]->Name); - ImGui::EndChildFrame(); - } - - ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f); - if (ImGui::Button("Yes", button_size)) - { - for (int n = 0; n < close_queue.Size; n++) - { - if (close_queue[n]->Dirty) - close_queue[n]->DoSave(); - close_queue[n]->DoForceClose(); - } - close_queue.clear(); - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if (ImGui::Button("No", button_size)) - { - for (int n = 0; n < close_queue.Size; n++) - close_queue[n]->DoForceClose(); - close_queue.clear(); - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if (ImGui::Button("Cancel", button_size)) - { - close_queue.clear(); - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - } - } - - ImGui::End(); -} - -// End of Demo code -#else - -void ImGui::ShowAboutWindow(bool*) {} -void ImGui::ShowDemoWindow(bool*) {} -void ImGui::ShowUserGuide() {} -void ImGui::ShowStyleEditor(ImGuiStyle*) {} - -#endif - -#endif // #ifndef IMGUI_DISABLE diff --git a/libs/zgui/libs/imgui/imgui_draw.cpp b/libs/zgui/libs/imgui/imgui_draw.cpp deleted file mode 100644 index fcdb9dfe8..000000000 --- a/libs/zgui/libs/imgui/imgui_draw.cpp +++ /dev/null @@ -1,4162 +0,0 @@ -// dear imgui, v1.88 -// (drawing and font code) - -/* - -Index of this file: - -// [SECTION] STB libraries implementation -// [SECTION] Style functions -// [SECTION] ImDrawList -// [SECTION] ImDrawListSplitter -// [SECTION] ImDrawData -// [SECTION] Helpers ShadeVertsXXX functions -// [SECTION] ImFontConfig -// [SECTION] ImFontAtlas -// [SECTION] ImFontAtlas glyph ranges helpers -// [SECTION] ImFontGlyphRangesBuilder -// [SECTION] ImFont -// [SECTION] ImGui Internal Render Helpers -// [SECTION] Decompression code -// [SECTION] Default font data (ProggyClean.ttf) - -*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "imgui.h" -#ifndef IMGUI_DISABLE - -#ifndef IMGUI_DEFINE_MATH_OPERATORS -#define IMGUI_DEFINE_MATH_OPERATORS -#endif - -#include "imgui_internal.h" -#ifdef IMGUI_ENABLE_FREETYPE -#include "misc/freetype/imgui_freetype.h" -#endif - -#include // vsnprintf, sscanf, printf -#if !defined(alloca) -#if defined(__GLIBC__) || defined(__sun) || defined(__APPLE__) || defined(__NEWLIB__) -#include // alloca (glibc uses . Note that Cygwin may have _WIN32 defined, so the order matters here) -#elif defined(_WIN32) -#include // alloca -#if !defined(alloca) -#define alloca _alloca // for clang with MS Codegen -#endif -#else -#include // alloca -#endif -#endif - -// Visual Studio warnings -#ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead. -#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). -#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) -#endif - -// Clang/GCC warnings with -Weverything -#if defined(__clang__) -#if __has_warning("-Wunknown-warning-option") -#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! -#endif -#if __has_warning("-Walloca") -#pragma clang diagnostic ignored "-Walloca" // warning: use of function '__builtin_alloca' is discouraged -#endif -#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' -#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. -#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok. -#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is. -#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 -#pragma clang diagnostic ignored "-Wcomma" // warning: possible misuse of comma operator here -#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier -#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. -#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision -#elif defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind -#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used -#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function -#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value -#pragma GCC diagnostic ignored "-Wstack-protector" // warning: stack protector not protecting local variables: variable length buffer -#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead -#endif - -//------------------------------------------------------------------------- -// [SECTION] STB libraries implementation (for stb_truetype and stb_rect_pack) -//------------------------------------------------------------------------- - -// Compile time options: -//#define IMGUI_STB_NAMESPACE ImStb -//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" -//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" -//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION -//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION - -#ifdef IMGUI_STB_NAMESPACE -namespace IMGUI_STB_NAMESPACE -{ -#endif - -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration -#pragma warning (disable: 6011) // (stb_rectpack) Dereferencing NULL pointer 'cur->next'. -#pragma warning (disable: 6385) // (stb_truetype) Reading invalid data from 'buffer': the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read. -#pragma warning (disable: 28182) // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did. -#endif - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wimplicit-fallthrough" -#pragma clang diagnostic ignored "-Wcast-qual" // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier -#endif - -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtype-limits" // warning: comparison is always true due to limited range of data type [-Wtype-limits] -#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers -#endif - -#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) -#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in another compilation unit -#define STBRP_STATIC -#define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0) -#define STBRP_SORT ImQsort -#define STB_RECT_PACK_IMPLEMENTATION -#endif -#ifdef IMGUI_STB_RECT_PACK_FILENAME -#include IMGUI_STB_RECT_PACK_FILENAME -#else -#include "imstb_rectpack.h" -#endif -#endif - -#ifdef IMGUI_ENABLE_STB_TRUETYPE -#ifndef STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) -#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in another compilation unit -#define STBTT_malloc(x,u) ((void)(u), IM_ALLOC(x)) -#define STBTT_free(x,u) ((void)(u), IM_FREE(x)) -#define STBTT_assert(x) do { IM_ASSERT(x); } while(0) -#define STBTT_fmod(x,y) ImFmod(x,y) -#define STBTT_sqrt(x) ImSqrt(x) -#define STBTT_pow(x,y) ImPow(x,y) -#define STBTT_fabs(x) ImFabs(x) -#define STBTT_ifloor(x) ((int)ImFloorSigned(x)) -#define STBTT_iceil(x) ((int)ImCeil(x)) -#define STBTT_STATIC -#define STB_TRUETYPE_IMPLEMENTATION -#else -#define STBTT_DEF extern -#endif -#ifdef IMGUI_STB_TRUETYPE_FILENAME -#include IMGUI_STB_TRUETYPE_FILENAME -#else -#include "imstb_truetype.h" -#endif -#endif -#endif // IMGUI_ENABLE_STB_TRUETYPE - -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -#if defined(_MSC_VER) -#pragma warning (pop) -#endif - -#ifdef IMGUI_STB_NAMESPACE -} // namespace ImStb -using namespace IMGUI_STB_NAMESPACE; -#endif - -//----------------------------------------------------------------------------- -// [SECTION] Style functions -//----------------------------------------------------------------------------- - -void ImGui::StyleColorsDark(ImGuiStyle* dst) -{ - ImGuiStyle* style = dst ? dst : &ImGui::GetStyle(); - ImVec4* colors = style->Colors; - - colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); - colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f); - colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); - colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); - colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_FrameBg] = ImVec4(0.16f, 0.29f, 0.48f, 0.54f); - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.16f, 0.29f, 0.48f, 1.00f); - colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f); - colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); - colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f); - colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f); - colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); - colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f); - colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f); - colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); - colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); - colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); - colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); - colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_Separator] = colors[ImGuiCol_Border]; - colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.78f); - colors[ImGuiCol_SeparatorActive] = ImVec4(0.10f, 0.40f, 0.75f, 1.00f); - colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.20f); - colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); - colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f); - colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); - colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); - colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); - colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); - colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); - colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); - colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f); - colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f); // Prefer using Alpha=1.0 here - colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); // Prefer using Alpha=1.0 here - colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); - colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); - colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); - colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); - colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); - colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); -} - -void ImGui::StyleColorsClassic(ImGuiStyle* dst) -{ - ImGuiStyle* style = dst ? dst : &ImGui::GetStyle(); - ImVec4* colors = style->Colors; - - colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); - colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.85f); - colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_PopupBg] = ImVec4(0.11f, 0.11f, 0.14f, 0.92f); - colors[ImGuiCol_Border] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); - colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_FrameBg] = ImVec4(0.43f, 0.43f, 0.43f, 0.39f); - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.47f, 0.47f, 0.69f, 0.40f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.42f, 0.41f, 0.64f, 0.69f); - colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f); - colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f); - colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f); - colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f); - colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f); - colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f); - colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f); - colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f); - colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); - colors[ImGuiCol_SliderGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f); - colors[ImGuiCol_Button] = ImVec4(0.35f, 0.40f, 0.61f, 0.62f); - colors[ImGuiCol_ButtonHovered] = ImVec4(0.40f, 0.48f, 0.71f, 0.79f); - colors[ImGuiCol_ButtonActive] = ImVec4(0.46f, 0.54f, 0.80f, 1.00f); - colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f); - colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f); - colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f); - colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.50f, 0.50f, 0.60f); - colors[ImGuiCol_SeparatorHovered] = ImVec4(0.60f, 0.60f, 0.70f, 1.00f); - colors[ImGuiCol_SeparatorActive] = ImVec4(0.70f, 0.70f, 0.90f, 1.00f); - colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f); - colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f); - colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f); - colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f); - colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); - colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); - colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); - colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); - colors[ImGuiCol_TableHeaderBg] = ImVec4(0.27f, 0.27f, 0.38f, 1.00f); - colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.45f, 1.00f); // Prefer using Alpha=1.0 here - colors[ImGuiCol_TableBorderLight] = ImVec4(0.26f, 0.26f, 0.28f, 1.00f); // Prefer using Alpha=1.0 here - colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f); - colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); - colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); - colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); - colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); - colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); -} - -// Those light colors are better suited with a thicker font than the default one + FrameBorder -void ImGui::StyleColorsLight(ImGuiStyle* dst) -{ - ImGuiStyle* style = dst ? dst : &ImGui::GetStyle(); - ImVec4* colors = style->Colors; - - colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); - colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 1.00f); - colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_PopupBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f); - colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.30f); - colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - colors[ImGuiCol_TitleBg] = ImVec4(0.96f, 0.96f, 0.96f, 1.00f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f); - colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.51f); - colors[ImGuiCol_MenuBarBg] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f); - colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f); - colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 0.80f); - colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.49f, 0.49f, 0.49f, 0.80f); - colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f); - colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_SliderGrab] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f); - colors[ImGuiCol_SliderGrabActive] = ImVec4(0.46f, 0.54f, 0.80f, 0.60f); - colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); - colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); - colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); - colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); - colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_Separator] = ImVec4(0.39f, 0.39f, 0.39f, 0.62f); - colors[ImGuiCol_SeparatorHovered] = ImVec4(0.14f, 0.44f, 0.80f, 0.78f); - colors[ImGuiCol_SeparatorActive] = ImVec4(0.14f, 0.44f, 0.80f, 1.00f); - colors[ImGuiCol_ResizeGrip] = ImVec4(0.35f, 0.35f, 0.35f, 0.17f); - colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); - colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f); - colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); - colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); - colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); - colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); - colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); - colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.45f, 0.00f, 1.00f); - colors[ImGuiCol_TableHeaderBg] = ImVec4(0.78f, 0.87f, 0.98f, 1.00f); - colors[ImGuiCol_TableBorderStrong] = ImVec4(0.57f, 0.57f, 0.64f, 1.00f); // Prefer using Alpha=1.0 here - colors[ImGuiCol_TableBorderLight] = ImVec4(0.68f, 0.68f, 0.74f, 1.00f); // Prefer using Alpha=1.0 here - colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.30f, 0.30f, 0.30f, 0.09f); - colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); - colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); - colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f); - colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f); - colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); -} - -//----------------------------------------------------------------------------- -// [SECTION] ImDrawList -//----------------------------------------------------------------------------- - -ImDrawListSharedData::ImDrawListSharedData() -{ - memset(this, 0, sizeof(*this)); - for (int i = 0; i < IM_ARRAYSIZE(ArcFastVtx); i++) - { - const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx); - ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a)); - } - ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); -} - -void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) -{ - if (CircleSegmentMaxError == max_error) - return; - - IM_ASSERT(max_error > 0.0f); - CircleSegmentMaxError = max_error; - for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++) - { - const float radius = (float)i; - CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX); - } - ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); -} - -// Initialize before use in a new frame. We always have a command ready in the buffer. -void ImDrawList::_ResetForNewFrame() -{ - // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. - IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0); - IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4)); - IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); - - CmdBuffer.resize(0); - IdxBuffer.resize(0); - VtxBuffer.resize(0); - Flags = _Data->InitialFlags; - memset(&_CmdHeader, 0, sizeof(_CmdHeader)); - _VtxCurrentIdx = 0; - _VtxWritePtr = NULL; - _IdxWritePtr = NULL; - _ClipRectStack.resize(0); - _TextureIdStack.resize(0); - _Path.resize(0); - _Splitter.Clear(); - CmdBuffer.push_back(ImDrawCmd()); - _FringeScale = 1.0f; -} - -void ImDrawList::_ClearFreeMemory() -{ - CmdBuffer.clear(); - IdxBuffer.clear(); - VtxBuffer.clear(); - Flags = ImDrawListFlags_None; - _VtxCurrentIdx = 0; - _VtxWritePtr = NULL; - _IdxWritePtr = NULL; - _ClipRectStack.clear(); - _TextureIdStack.clear(); - _Path.clear(); - _Splitter.ClearFreeMemory(); -} - -ImDrawList* ImDrawList::CloneOutput() const -{ - ImDrawList* dst = IM_NEW(ImDrawList(_Data)); - dst->CmdBuffer = CmdBuffer; - dst->IdxBuffer = IdxBuffer; - dst->VtxBuffer = VtxBuffer; - dst->Flags = Flags; - return dst; -} - -void ImDrawList::AddDrawCmd() -{ - ImDrawCmd draw_cmd; - draw_cmd.ClipRect = _CmdHeader.ClipRect; // Same as calling ImDrawCmd_HeaderCopy() - draw_cmd.TextureId = _CmdHeader.TextureId; - draw_cmd.VtxOffset = _CmdHeader.VtxOffset; - draw_cmd.IdxOffset = IdxBuffer.Size; - - IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); - CmdBuffer.push_back(draw_cmd); -} - -// Pop trailing draw command (used before merging or presenting to user) -// Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL -void ImDrawList::_PopUnusedDrawCmd() -{ - if (CmdBuffer.Size == 0) - return; - ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - if (curr_cmd->ElemCount == 0 && curr_cmd->UserCallback == NULL) - CmdBuffer.pop_back(); -} - -void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) -{ - IM_ASSERT_PARANOID(CmdBuffer.Size > 0); - ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - IM_ASSERT(curr_cmd->UserCallback == NULL); - if (curr_cmd->ElemCount != 0) - { - AddDrawCmd(); - curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - } - curr_cmd->UserCallback = callback; - curr_cmd->UserCallbackData = callback_data; - - AddDrawCmd(); // Force a new command after us (see comment below) -} - -// Compare ClipRect, TextureId and VtxOffset with a single memcmp() -#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int)) -#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset -#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset -#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset) - -// Try to merge two last draw commands -void ImDrawList::_TryMergeDrawCmds() -{ - IM_ASSERT_PARANOID(CmdBuffer.Size > 0); - ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - ImDrawCmd* prev_cmd = curr_cmd - 1; - if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) - { - prev_cmd->ElemCount += curr_cmd->ElemCount; - CmdBuffer.pop_back(); - } -} - -// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. -// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. -void ImDrawList::_OnChangedClipRect() -{ - // If current command is used with different settings we need to add a new command - IM_ASSERT_PARANOID(CmdBuffer.Size > 0); - ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0) - { - AddDrawCmd(); - return; - } - IM_ASSERT(curr_cmd->UserCallback == NULL); - - // Try to merge with previous command if it matches, else use current command - ImDrawCmd* prev_cmd = curr_cmd - 1; - if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL) - { - CmdBuffer.pop_back(); - return; - } - - curr_cmd->ClipRect = _CmdHeader.ClipRect; -} - -void ImDrawList::_OnChangedTextureID() -{ - // If current command is used with different settings we need to add a new command - IM_ASSERT_PARANOID(CmdBuffer.Size > 0); - ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId) - { - AddDrawCmd(); - return; - } - IM_ASSERT(curr_cmd->UserCallback == NULL); - - // Try to merge with previous command if it matches, else use current command - ImDrawCmd* prev_cmd = curr_cmd - 1; - if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL) - { - CmdBuffer.pop_back(); - return; - } - - curr_cmd->TextureId = _CmdHeader.TextureId; -} - -void ImDrawList::_OnChangedVtxOffset() -{ - // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this. - _VtxCurrentIdx = 0; - IM_ASSERT_PARANOID(CmdBuffer.Size > 0); - ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349 - if (curr_cmd->ElemCount != 0) - { - AddDrawCmd(); - return; - } - IM_ASSERT(curr_cmd->UserCallback == NULL); - curr_cmd->VtxOffset = _CmdHeader.VtxOffset; -} - -int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const -{ - // Automatic segment count - const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy - if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) - return _Data->CircleSegmentCounts[radius_idx]; // Use cached value - else - return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); -} - -// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) -void ImDrawList::PushClipRect(const ImVec2& cr_min, const ImVec2& cr_max, bool intersect_with_current_clip_rect) -{ - ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y); - if (intersect_with_current_clip_rect) - { - ImVec4 current = _CmdHeader.ClipRect; - if (cr.x < current.x) cr.x = current.x; - if (cr.y < current.y) cr.y = current.y; - if (cr.z > current.z) cr.z = current.z; - if (cr.w > current.w) cr.w = current.w; - } - cr.z = ImMax(cr.x, cr.z); - cr.w = ImMax(cr.y, cr.w); - - _ClipRectStack.push_back(cr); - _CmdHeader.ClipRect = cr; - _OnChangedClipRect(); -} - -void ImDrawList::PushClipRectFullScreen() -{ - PushClipRect(ImVec2(_Data->ClipRectFullscreen.x, _Data->ClipRectFullscreen.y), ImVec2(_Data->ClipRectFullscreen.z, _Data->ClipRectFullscreen.w)); -} - -void ImDrawList::PopClipRect() -{ - _ClipRectStack.pop_back(); - _CmdHeader.ClipRect = (_ClipRectStack.Size == 0) ? _Data->ClipRectFullscreen : _ClipRectStack.Data[_ClipRectStack.Size - 1]; - _OnChangedClipRect(); -} - -void ImDrawList::PushTextureID(ImTextureID texture_id) -{ - _TextureIdStack.push_back(texture_id); - _CmdHeader.TextureId = texture_id; - _OnChangedTextureID(); -} - -void ImDrawList::PopTextureID() -{ - _TextureIdStack.pop_back(); - _CmdHeader.TextureId = (_TextureIdStack.Size == 0) ? (ImTextureID)NULL : _TextureIdStack.Data[_TextureIdStack.Size - 1]; - _OnChangedTextureID(); -} - -// Reserve space for a number of vertices and indices. -// You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or -// submit the intermediate results. PrimUnreserve() can be used to release unused allocations. -void ImDrawList::PrimReserve(int idx_count, int vtx_count) -{ - // Large mesh support (when enabled) - IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0); - if (sizeof(ImDrawIdx) == 2 && (_VtxCurrentIdx + vtx_count >= (1 << 16)) && (Flags & ImDrawListFlags_AllowVtxOffset)) - { - // FIXME: In theory we should be testing that vtx_count <64k here. - // In practice, RenderText() relies on reserving ahead for a worst case scenario so it is currently useful for us - // to not make that check until we rework the text functions to handle clipping and large horizontal lines better. - _CmdHeader.VtxOffset = VtxBuffer.Size; - _OnChangedVtxOffset(); - } - - ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - draw_cmd->ElemCount += idx_count; - - int vtx_buffer_old_size = VtxBuffer.Size; - VtxBuffer.resize(vtx_buffer_old_size + vtx_count); - _VtxWritePtr = VtxBuffer.Data + vtx_buffer_old_size; - - int idx_buffer_old_size = IdxBuffer.Size; - IdxBuffer.resize(idx_buffer_old_size + idx_count); - _IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size; -} - -// Release the a number of reserved vertices/indices from the end of the last reservation made with PrimReserve(). -void ImDrawList::PrimUnreserve(int idx_count, int vtx_count) -{ - IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0); - - ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - draw_cmd->ElemCount -= idx_count; - VtxBuffer.shrink(VtxBuffer.Size - vtx_count); - IdxBuffer.shrink(IdxBuffer.Size - idx_count); -} - -// Fully unrolled with inline call to keep our debug builds decently fast. -void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col) -{ - ImVec2 b(c.x, a.y), d(a.x, c.y), uv(_Data->TexUvWhitePixel); - ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; - _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); - _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); - _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; - _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; - _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col; - _VtxWritePtr += 4; - _VtxCurrentIdx += 4; - _IdxWritePtr += 6; -} - -void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col) -{ - ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y); - ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; - _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); - _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); - _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; - _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; - _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; - _VtxWritePtr += 4; - _VtxCurrentIdx += 4; - _IdxWritePtr += 6; -} - -void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col) -{ - ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; - _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); - _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); - _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; - _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; - _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; - _VtxWritePtr += 4; - _VtxCurrentIdx += 4; - _IdxWritePtr += 6; -} - -// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds. -// - Those macros expects l-values and need to be used as their own statement. -// - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers. -#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0 -#define IM_FIXNORMAL2F_MAX_INVLEN2 100.0f // 500.0f (see #4053, #3366) -#define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } (void)0 - -// TODO: Thickness anti-aliased lines cap are missing their AA fringe. -// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. -void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, ImDrawFlags flags, float thickness) -{ - if (points_count < 2) - return; - - const bool closed = (flags & ImDrawFlags_Closed) != 0; - const ImVec2 opaque_uv = _Data->TexUvWhitePixel; - const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw - const bool thick_line = (thickness > _FringeScale); - - if (Flags & ImDrawListFlags_AntiAliasedLines) - { - // Anti-aliased stroke - const float AA_SIZE = _FringeScale; - const ImU32 col_trans = col & ~IM_COL32_A_MASK; - - // Thicknesses <1.0 should behave like thickness 1.0 - thickness = ImMax(thickness, 1.0f); - const int integer_thickness = (int)thickness; - const float fractional_thickness = thickness - integer_thickness; - - // Do we want to draw this line using a texture? - // - For now, only draw integer-width lines using textures to avoid issues with the way scaling occurs, could be improved. - // - If AA_SIZE is not 1.0f we cannot use the texture path. - const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f) && (AA_SIZE == 1.0f); - - // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off - IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines)); - - const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12); - const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3); - PrimReserve(idx_count, vtx_count); - - // Temporary buffer - // The first items are normals at each line point, then after that there are either 2 or 4 temp points for each line point - ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((use_texture || !thick_line) ? 3 : 5) * sizeof(ImVec2)); //-V630 - ImVec2* temp_points = temp_normals + points_count; - - // Calculate normals (tangents) for each line segment - for (int i1 = 0; i1 < count; i1++) - { - const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; - float dx = points[i2].x - points[i1].x; - float dy = points[i2].y - points[i1].y; - IM_NORMALIZE2F_OVER_ZERO(dx, dy); - temp_normals[i1].x = dy; - temp_normals[i1].y = -dx; - } - if (!closed) - temp_normals[points_count - 1] = temp_normals[points_count - 2]; - - // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point - if (use_texture || !thick_line) - { - // [PATH 1] Texture-based lines (thick or non-thick) - // [PATH 2] Non texture-based lines (non-thick) - - // The width of the geometry we need to draw - this is essentially pixels for the line itself, plus "one pixel" for AA. - // - In the texture-based path, we don't use AA_SIZE here because the +1 is tied to the generated texture - // (see ImFontAtlasBuildRenderLinesTexData() function), and so alternate values won't work without changes to that code. - // - In the non texture-based paths, we would allow AA_SIZE to potentially be != 1.0f with a patch (e.g. fringe_scale patch to - // allow scaling geometry while preserving one-screen-pixel AA fringe). - const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : AA_SIZE; - - // If line is not closed, the first and last points need to be generated differently as there are no normals to blend - if (!closed) - { - temp_points[0] = points[0] + temp_normals[0] * half_draw_size; - temp_points[1] = points[0] - temp_normals[0] * half_draw_size; - temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * half_draw_size; - temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * half_draw_size; - } - - // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges - // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps) - // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. - unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment - for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment - { - const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment - const unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_texture ? 2 : 3)); // Vertex index for end of segment - - // Average normals - float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; - float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f; - IM_FIXNORMAL2F(dm_x, dm_y); - dm_x *= half_draw_size; // dm_x, dm_y are offset to the outer edge of the AA area - dm_y *= half_draw_size; - - // Add temporary vertexes for the outer edges - ImVec2* out_vtx = &temp_points[i2 * 2]; - out_vtx[0].x = points[i2].x + dm_x; - out_vtx[0].y = points[i2].y + dm_y; - out_vtx[1].x = points[i2].x - dm_x; - out_vtx[1].y = points[i2].y - dm_y; - - if (use_texture) - { - // Add indices for two triangles - _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 1); // Right tri - _IdxWritePtr[3] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[4] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Left tri - _IdxWritePtr += 6; - } - else - { - // Add indexes for four triangles - _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 2); // Right tri 1 - _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Right tri 2 - _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0); // Left tri 1 - _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1); // Left tri 2 - _IdxWritePtr += 12; - } - - idx1 = idx2; - } - - // Add vertexes for each point on the line - if (use_texture) - { - // If we're using textures we only need to emit the left/right edge vertices - ImVec4 tex_uvs = _Data->TexUvLines[integer_thickness]; - /*if (fractional_thickness != 0.0f) // Currently always zero when use_texture==false! - { - const ImVec4 tex_uvs_1 = _Data->TexUvLines[integer_thickness + 1]; - tex_uvs.x = tex_uvs.x + (tex_uvs_1.x - tex_uvs.x) * fractional_thickness; // inlined ImLerp() - tex_uvs.y = tex_uvs.y + (tex_uvs_1.y - tex_uvs.y) * fractional_thickness; - tex_uvs.z = tex_uvs.z + (tex_uvs_1.z - tex_uvs.z) * fractional_thickness; - tex_uvs.w = tex_uvs.w + (tex_uvs_1.w - tex_uvs.w) * fractional_thickness; - }*/ - ImVec2 tex_uv0(tex_uvs.x, tex_uvs.y); - ImVec2 tex_uv1(tex_uvs.z, tex_uvs.w); - for (int i = 0; i < points_count; i++) - { - _VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = tex_uv0; _VtxWritePtr[0].col = col; // Left-side outer edge - _VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = tex_uv1; _VtxWritePtr[1].col = col; // Right-side outer edge - _VtxWritePtr += 2; - } - } - else - { - // If we're not using a texture, we need the center vertex as well - for (int i = 0; i < points_count; i++) - { - _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; // Center of line - _VtxWritePtr[1].pos = temp_points[i * 2 + 0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; // Left-side outer edge - _VtxWritePtr[2].pos = temp_points[i * 2 + 1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; // Right-side outer edge - _VtxWritePtr += 3; - } - } - } - else - { - // [PATH 2] Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point - const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; - - // If line is not closed, the first and last points need to be generated differently as there are no normals to blend - if (!closed) - { - const int points_last = points_count - 1; - temp_points[0] = points[0] + temp_normals[0] * (half_inner_thickness + AA_SIZE); - temp_points[1] = points[0] + temp_normals[0] * (half_inner_thickness); - temp_points[2] = points[0] - temp_normals[0] * (half_inner_thickness); - temp_points[3] = points[0] - temp_normals[0] * (half_inner_thickness + AA_SIZE); - temp_points[points_last * 4 + 0] = points[points_last] + temp_normals[points_last] * (half_inner_thickness + AA_SIZE); - temp_points[points_last * 4 + 1] = points[points_last] + temp_normals[points_last] * (half_inner_thickness); - temp_points[points_last * 4 + 2] = points[points_last] - temp_normals[points_last] * (half_inner_thickness); - temp_points[points_last * 4 + 3] = points[points_last] - temp_normals[points_last] * (half_inner_thickness + AA_SIZE); - } - - // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges - // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps) - // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. - unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment - for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment - { - const int i2 = (i1 + 1) == points_count ? 0 : (i1 + 1); // i2 is the second point of the line segment - const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : (idx1 + 4); // Vertex index for end of segment - - // Average normals - float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; - float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f; - IM_FIXNORMAL2F(dm_x, dm_y); - float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE); - float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE); - float dm_in_x = dm_x * half_inner_thickness; - float dm_in_y = dm_y * half_inner_thickness; - - // Add temporary vertices - ImVec2* out_vtx = &temp_points[i2 * 4]; - out_vtx[0].x = points[i2].x + dm_out_x; - out_vtx[0].y = points[i2].y + dm_out_y; - out_vtx[1].x = points[i2].x + dm_in_x; - out_vtx[1].y = points[i2].y + dm_in_y; - out_vtx[2].x = points[i2].x - dm_in_x; - out_vtx[2].y = points[i2].y - dm_in_y; - out_vtx[3].x = points[i2].x - dm_out_x; - out_vtx[3].y = points[i2].y - dm_out_y; - - // Add indexes - _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 2); - _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 1); - _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0); - _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1); - _IdxWritePtr[12] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[13] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[14] = (ImDrawIdx)(idx1 + 3); - _IdxWritePtr[15] = (ImDrawIdx)(idx1 + 3); _IdxWritePtr[16] = (ImDrawIdx)(idx2 + 3); _IdxWritePtr[17] = (ImDrawIdx)(idx2 + 2); - _IdxWritePtr += 18; - - idx1 = idx2; - } - - // Add vertices - for (int i = 0; i < points_count; i++) - { - _VtxWritePtr[0].pos = temp_points[i * 4 + 0]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col_trans; - _VtxWritePtr[1].pos = temp_points[i * 4 + 1]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col; - _VtxWritePtr[2].pos = temp_points[i * 4 + 2]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col; - _VtxWritePtr[3].pos = temp_points[i * 4 + 3]; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col_trans; - _VtxWritePtr += 4; - } - } - _VtxCurrentIdx += (ImDrawIdx)vtx_count; - } - else - { - // [PATH 4] Non texture-based, Non anti-aliased lines - const int idx_count = count * 6; - const int vtx_count = count * 4; // FIXME-OPT: Not sharing edges - PrimReserve(idx_count, vtx_count); - - for (int i1 = 0; i1 < count; i1++) - { - const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; - const ImVec2& p1 = points[i1]; - const ImVec2& p2 = points[i2]; - - float dx = p2.x - p1.x; - float dy = p2.y - p1.y; - IM_NORMALIZE2F_OVER_ZERO(dx, dy); - dx *= (thickness * 0.5f); - dy *= (thickness * 0.5f); - - _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col; - _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col; - _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col; - _VtxWritePtr += 4; - - _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + 2); - _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx + 2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx + 3); - _IdxWritePtr += 6; - _VtxCurrentIdx += 4; - } - } -} - -// - We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds. -// - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. -void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col) -{ - if (points_count < 3) - return; - - const ImVec2 uv = _Data->TexUvWhitePixel; - - if (Flags & ImDrawListFlags_AntiAliasedFill) - { - // Anti-aliased Fill - const float AA_SIZE = _FringeScale; - const ImU32 col_trans = col & ~IM_COL32_A_MASK; - const int idx_count = (points_count - 2)*3 + points_count * 6; - const int vtx_count = (points_count * 2); - PrimReserve(idx_count, vtx_count); - - // Add indexes for fill - unsigned int vtx_inner_idx = _VtxCurrentIdx; - unsigned int vtx_outer_idx = _VtxCurrentIdx + 1; - for (int i = 2; i < points_count; i++) - { - _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + ((i - 1) << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (i << 1)); - _IdxWritePtr += 3; - } - - // Compute normals - ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); //-V630 - for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++) - { - const ImVec2& p0 = points[i0]; - const ImVec2& p1 = points[i1]; - float dx = p1.x - p0.x; - float dy = p1.y - p0.y; - IM_NORMALIZE2F_OVER_ZERO(dx, dy); - temp_normals[i0].x = dy; - temp_normals[i0].y = -dx; - } - - for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++) - { - // Average normals - const ImVec2& n0 = temp_normals[i0]; - const ImVec2& n1 = temp_normals[i1]; - float dm_x = (n0.x + n1.x) * 0.5f; - float dm_y = (n0.y + n1.y) * 0.5f; - IM_FIXNORMAL2F(dm_x, dm_y); - dm_x *= AA_SIZE * 0.5f; - dm_y *= AA_SIZE * 0.5f; - - // Add vertices - _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner - _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer - _VtxWritePtr += 2; - - // Add indexes for fringes - _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); - _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); - _IdxWritePtr += 6; - } - _VtxCurrentIdx += (ImDrawIdx)vtx_count; - } - else - { - // Non Anti-aliased Fill - const int idx_count = (points_count - 2)*3; - const int vtx_count = points_count; - PrimReserve(idx_count, vtx_count); - for (int i = 0; i < vtx_count; i++) - { - _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; - _VtxWritePtr++; - } - for (int i = 2; i < points_count; i++) - { - _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + i - 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + i); - _IdxWritePtr += 3; - } - _VtxCurrentIdx += (ImDrawIdx)vtx_count; - } -} - -void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step) -{ - if (radius < 0.5f) - { - _Path.push_back(center); - return; - } - - // Calculate arc auto segment step size - if (a_step <= 0) - a_step = IM_DRAWLIST_ARCFAST_SAMPLE_MAX / _CalcCircleAutoSegmentCount(radius); - - // Make sure we never do steps larger than one quarter of the circle - a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4); - - const int sample_range = ImAbs(a_max_sample - a_min_sample); - const int a_next_step = a_step; - - int samples = sample_range + 1; - bool extra_max_sample = false; - if (a_step > 1) - { - samples = sample_range / a_step + 1; - const int overstep = sample_range % a_step; - - if (overstep > 0) - { - extra_max_sample = true; - samples++; - - // When we have overstep to avoid awkwardly looking one long line and one tiny one at the end, - // distribute first step range evenly between them by reducing first step size. - if (sample_range > 0) - a_step -= (a_step - overstep) / 2; - } - } - - _Path.resize(_Path.Size + samples); - ImVec2* out_ptr = _Path.Data + (_Path.Size - samples); - - int sample_index = a_min_sample; - if (sample_index < 0 || sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) - { - sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - if (sample_index < 0) - sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - } - - if (a_max_sample >= a_min_sample) - { - for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step) - { - // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more - if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) - sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - - const ImVec2 s = _Data->ArcFastVtx[sample_index]; - out_ptr->x = center.x + s.x * radius; - out_ptr->y = center.y + s.y * radius; - out_ptr++; - } - } - else - { - for (int a = a_min_sample; a >= a_max_sample; a -= a_step, sample_index -= a_step, a_step = a_next_step) - { - // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more - if (sample_index < 0) - sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - - const ImVec2 s = _Data->ArcFastVtx[sample_index]; - out_ptr->x = center.x + s.x * radius; - out_ptr->y = center.y + s.y * radius; - out_ptr++; - } - } - - if (extra_max_sample) - { - int normalized_max_sample = a_max_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - if (normalized_max_sample < 0) - normalized_max_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - - const ImVec2 s = _Data->ArcFastVtx[normalized_max_sample]; - out_ptr->x = center.x + s.x * radius; - out_ptr->y = center.y + s.y * radius; - out_ptr++; - } - - IM_ASSERT_PARANOID(_Path.Data + _Path.Size == out_ptr); -} - -void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments) -{ - if (radius < 0.5f) - { - _Path.push_back(center); - return; - } - - // Note that we are adding a point at both a_min and a_max. - // If you are trying to draw a full closed circle you don't want the overlapping points! - _Path.reserve(_Path.Size + (num_segments + 1)); - for (int i = 0; i <= num_segments; i++) - { - const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min); - _Path.push_back(ImVec2(center.x + ImCos(a) * radius, center.y + ImSin(a) * radius)); - } -} - -// 0: East, 3: South, 6: West, 9: North, 12: East -void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12) -{ - if (radius < 0.5f) - { - _Path.push_back(center); - return; - } - _PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0); -} - -void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments) -{ - if (radius < 0.5f) - { - _Path.push_back(center); - return; - } - - if (num_segments > 0) - { - _PathArcToN(center, radius, a_min, a_max, num_segments); - return; - } - - // Automatic segment count - if (radius <= _Data->ArcFastRadiusCutoff) - { - const bool a_is_reverse = a_max < a_min; - - // We are going to use precomputed values for mid samples. - // Determine first and last sample in lookup table that belong to the arc. - const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f); - const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f); - - const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f); - const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f); - const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0); - - const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - const bool a_emit_start = ImAbs(a_min_segment_angle - a_min) >= 1e-5f; - const bool a_emit_end = ImAbs(a_max - a_max_segment_angle) >= 1e-5f; - - _Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0))); - if (a_emit_start) - _Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius)); - if (a_mid_samples > 0) - _PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0); - if (a_emit_end) - _Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius)); - } - else - { - const float arc_length = ImAbs(a_max - a_min); - const int circle_segment_count = _CalcCircleAutoSegmentCount(radius); - const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length)); - _PathArcToN(center, radius, a_min, a_max, arc_segment_count); - } -} - -ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t) -{ - float u = 1.0f - t; - float w1 = u * u * u; - float w2 = 3 * u * u * t; - float w3 = 3 * u * t * t; - float w4 = t * t * t; - return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x, w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y); -} - -ImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t) -{ - float u = 1.0f - t; - float w1 = u * u; - float w2 = 2 * u * t; - float w3 = t * t; - return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x, w1 * p1.y + w2 * p2.y + w3 * p3.y); -} - -// Closely mimics ImBezierCubicClosestPointCasteljau() in imgui.cpp -static void PathBezierCubicCurveToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) -{ - float dx = x4 - x1; - float dy = y4 - y1; - float d2 = (x2 - x4) * dy - (y2 - y4) * dx; - float d3 = (x3 - x4) * dy - (y3 - y4) * dx; - d2 = (d2 >= 0) ? d2 : -d2; - d3 = (d3 >= 0) ? d3 : -d3; - if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy)) - { - path->push_back(ImVec2(x4, y4)); - } - else if (level < 10) - { - float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f; - float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f; - float x34 = (x3 + x4) * 0.5f, y34 = (y3 + y4) * 0.5f; - float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; - float x234 = (x23 + x34) * 0.5f, y234 = (y23 + y34) * 0.5f; - float x1234 = (x123 + x234) * 0.5f, y1234 = (y123 + y234) * 0.5f; - PathBezierCubicCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); - PathBezierCubicCurveToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); - } -} - -static void PathBezierQuadraticCurveToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float tess_tol, int level) -{ - float dx = x3 - x1, dy = y3 - y1; - float det = (x2 - x3) * dy - (y2 - y3) * dx; - if (det * det * 4.0f < tess_tol * (dx * dx + dy * dy)) - { - path->push_back(ImVec2(x3, y3)); - } - else if (level < 10) - { - float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f; - float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f; - float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; - PathBezierQuadraticCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1); - PathBezierQuadraticCurveToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1); - } -} - -void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments) -{ - ImVec2 p1 = _Path.back(); - if (num_segments == 0) - { - PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated - } - else - { - float t_step = 1.0f / (float)num_segments; - for (int i_step = 1; i_step <= num_segments; i_step++) - _Path.push_back(ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step)); - } -} - -void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments) -{ - ImVec2 p1 = _Path.back(); - if (num_segments == 0) - { - PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated - } - else - { - float t_step = 1.0f / (float)num_segments; - for (int i_step = 1; i_step <= num_segments; i_step++) - _Path.push_back(ImBezierQuadraticCalc(p1, p2, p3, t_step * i_step)); - } -} - -IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4)); -static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags) -{ -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - // Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All) - // ~0 --> ImDrawFlags_RoundCornersAll or 0 - if (flags == ~0) - return ImDrawFlags_RoundCornersAll; - - // Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations) - // 0x01 --> ImDrawFlags_RoundCornersTopLeft (VALUE 0x01 OVERLAPS ImDrawFlags_Closed but ImDrawFlags_Closed is never valid in this path!) - // 0x02 --> ImDrawFlags_RoundCornersTopRight - // 0x03 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight - // 0x04 --> ImDrawFlags_RoundCornersBotLeft - // 0x05 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBotLeft - // ... - // 0x0F --> ImDrawFlags_RoundCornersAll or 0 - // (See all values in ImDrawCornerFlags_) - if (flags >= 0x01 && flags <= 0x0F) - return (flags << 4); - - // We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f' -#endif - - // If this triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values. - // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc... - IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!"); - - if ((flags & ImDrawFlags_RoundCornersMask_) == 0) - flags |= ImDrawFlags_RoundCornersAll; - - return flags; -} - -void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags) -{ - flags = FixRectCornerFlags(flags); - rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f ) - 1.0f); - rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f ) - 1.0f); - - if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) - { - PathLineTo(a); - PathLineTo(ImVec2(b.x, a.y)); - PathLineTo(b); - PathLineTo(ImVec2(a.x, b.y)); - } - else - { - const float rounding_tl = (flags & ImDrawFlags_RoundCornersTopLeft) ? rounding : 0.0f; - const float rounding_tr = (flags & ImDrawFlags_RoundCornersTopRight) ? rounding : 0.0f; - const float rounding_br = (flags & ImDrawFlags_RoundCornersBottomRight) ? rounding : 0.0f; - const float rounding_bl = (flags & ImDrawFlags_RoundCornersBottomLeft) ? rounding : 0.0f; - PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl, 6, 9); - PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr, 9, 12); - PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br, 0, 3); - PathArcToFast(ImVec2(a.x + rounding_bl, b.y - rounding_bl), rounding_bl, 3, 6); - } -} - -void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - PathLineTo(p1 + ImVec2(0.5f, 0.5f)); - PathLineTo(p2 + ImVec2(0.5f, 0.5f)); - PathStroke(col, 0, thickness); -} - -// p_min = upper-left, p_max = lower-right -// Note we don't render 1 pixels sized rectangles properly. -void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - if (Flags & ImDrawListFlags_AntiAliasedLines) - PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags); - else - PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, flags); // Better looking lower-right corner and rounded non-AA shapes. - PathStroke(col, ImDrawFlags_Closed, thickness); -} - -void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) - { - PrimReserve(6, 4); - PrimRect(p_min, p_max, col); - } - else - { - PathRect(p_min, p_max, rounding, flags); - PathFillConvex(col); - } -} - -// p_min = upper-left, p_max = lower-right -void ImDrawList::AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left) -{ - if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) & IM_COL32_A_MASK) == 0) - return; - - const ImVec2 uv = _Data->TexUvWhitePixel; - PrimReserve(6, 4); - PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2)); - PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 3)); - PrimWriteVtx(p_min, uv, col_upr_left); - PrimWriteVtx(ImVec2(p_max.x, p_min.y), uv, col_upr_right); - PrimWriteVtx(p_max, uv, col_bot_right); - PrimWriteVtx(ImVec2(p_min.x, p_max.y), uv, col_bot_left); -} - -void ImDrawList::AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(p1); - PathLineTo(p2); - PathLineTo(p3); - PathLineTo(p4); - PathStroke(col, ImDrawFlags_Closed, thickness); -} - -void ImDrawList::AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(p1); - PathLineTo(p2); - PathLineTo(p3); - PathLineTo(p4); - PathFillConvex(col); -} - -void ImDrawList::AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(p1); - PathLineTo(p2); - PathLineTo(p3); - PathStroke(col, ImDrawFlags_Closed, thickness); -} - -void ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(p1); - PathLineTo(p2); - PathLineTo(p3); - PathFillConvex(col); -} - -void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f) - return; - - if (num_segments <= 0) - { - // Use arc with automatic segment count - _PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); - _Path.Size--; - } - else - { - // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) - num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); - - // Because we are filling a closed shape we remove 1 from the count of segments/points - const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; - PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); - } - - PathStroke(col, ImDrawFlags_Closed, thickness); -} - -void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments) -{ - if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f) - return; - - if (num_segments <= 0) - { - // Use arc with automatic segment count - _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); - _Path.Size--; - } - else - { - // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) - num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); - - // Because we are filling a closed shape we remove 1 from the count of segments/points - const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; - PathArcTo(center, radius, 0.0f, a_max, num_segments - 1); - } - - PathFillConvex(col); -} - -// Guaranteed to honor 'num_segments' -void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) - return; - - // Because we are filling a closed shape we remove 1 from the count of segments/points - const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; - PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); - PathStroke(col, ImDrawFlags_Closed, thickness); -} - -// Guaranteed to honor 'num_segments' -void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments) -{ - if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) - return; - - // Because we are filling a closed shape we remove 1 from the count of segments/points - const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; - PathArcTo(center, radius, 0.0f, a_max, num_segments - 1); - PathFillConvex(col); -} - -// Cubic Bezier takes 4 controls points -void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(p1); - PathBezierCubicCurveTo(p2, p3, p4, num_segments); - PathStroke(col, 0, thickness); -} - -// Quadratic Bezier takes 3 controls points -void ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(p1); - PathBezierQuadraticCurveTo(p2, p3, num_segments); - PathStroke(col, 0, thickness); -} - -void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - if (text_end == NULL) - text_end = text_begin + strlen(text_begin); - if (text_begin == text_end) - return; - - // Pull default font/size from the shared ImDrawListSharedData instance - if (font == NULL) - font = _Data->Font; - if (font_size == 0.0f) - font_size = _Data->FontSize; - - IM_ASSERT(font->ContainerAtlas->TexID == _CmdHeader.TextureId); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. - - ImVec4 clip_rect = _CmdHeader.ClipRect; - if (cpu_fine_clip_rect) - { - clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x); - clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y); - clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z); - clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w); - } - font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL); -} - -void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) -{ - AddText(NULL, 0.0f, pos, col, text_begin, text_end); -} - -void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - const bool push_texture_id = user_texture_id != _CmdHeader.TextureId; - if (push_texture_id) - PushTextureID(user_texture_id); - - PrimReserve(6, 4); - PrimRectUV(p_min, p_max, uv_min, uv_max, col); - - if (push_texture_id) - PopTextureID(); -} - -void ImDrawList::AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3, const ImVec2& uv4, ImU32 col) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - const bool push_texture_id = user_texture_id != _CmdHeader.TextureId; - if (push_texture_id) - PushTextureID(user_texture_id); - - PrimReserve(6, 4); - PrimQuadUV(p1, p2, p3, p4, uv1, uv2, uv3, uv4, col); - - if (push_texture_id) - PopTextureID(); -} - -void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - flags = FixRectCornerFlags(flags); - if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) - { - AddImage(user_texture_id, p_min, p_max, uv_min, uv_max, col); - return; - } - - const bool push_texture_id = user_texture_id != _CmdHeader.TextureId; - if (push_texture_id) - PushTextureID(user_texture_id); - - int vert_start_idx = VtxBuffer.Size; - PathRect(p_min, p_max, rounding, flags); - PathFillConvex(col); - int vert_end_idx = VtxBuffer.Size; - ImGui::ShadeVertsLinearUV(this, vert_start_idx, vert_end_idx, p_min, p_max, uv_min, uv_max, true); - - if (push_texture_id) - PopTextureID(); -} - - -//----------------------------------------------------------------------------- -// [SECTION] ImDrawListSplitter -//----------------------------------------------------------------------------- -// FIXME: This may be a little confusing, trying to be a little too low-level/optimal instead of just doing vector swap.. -//----------------------------------------------------------------------------- - -void ImDrawListSplitter::ClearFreeMemory() -{ - for (int i = 0; i < _Channels.Size; i++) - { - if (i == _Current) - memset(&_Channels[i], 0, sizeof(_Channels[i])); // Current channel is a copy of CmdBuffer/IdxBuffer, don't destruct again - _Channels[i]._CmdBuffer.clear(); - _Channels[i]._IdxBuffer.clear(); - } - _Current = 0; - _Count = 1; - _Channels.clear(); -} - -void ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count) -{ - IM_UNUSED(draw_list); - IM_ASSERT(_Current == 0 && _Count <= 1 && "Nested channel splitting is not supported. Please use separate instances of ImDrawListSplitter."); - int old_channels_count = _Channels.Size; - if (old_channels_count < channels_count) - { - _Channels.reserve(channels_count); // Avoid over reserving since this is likely to stay stable - _Channels.resize(channels_count); - } - _Count = channels_count; - - // Channels[] (24/32 bytes each) hold storage that we'll swap with draw_list->_CmdBuffer/_IdxBuffer - // The content of Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to. - // When we switch to the next channel, we'll copy draw_list->_CmdBuffer/_IdxBuffer into Channels[0] and then Channels[1] into draw_list->CmdBuffer/_IdxBuffer - memset(&_Channels[0], 0, sizeof(ImDrawChannel)); - for (int i = 1; i < channels_count; i++) - { - if (i >= old_channels_count) - { - IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel(); - } - else - { - _Channels[i]._CmdBuffer.resize(0); - _Channels[i]._IdxBuffer.resize(0); - } - } -} - -void ImDrawListSplitter::Merge(ImDrawList* draw_list) -{ - // Note that we never use or rely on _Channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. - if (_Count <= 1) - return; - - SetCurrentChannel(draw_list, 0); - draw_list->_PopUnusedDrawCmd(); - - // Calculate our final buffer sizes. Also fix the incorrect IdxOffset values in each command. - int new_cmd_buffer_count = 0; - int new_idx_buffer_count = 0; - ImDrawCmd* last_cmd = (_Count > 0 && draw_list->CmdBuffer.Size > 0) ? &draw_list->CmdBuffer.back() : NULL; - int idx_offset = last_cmd ? last_cmd->IdxOffset + last_cmd->ElemCount : 0; - for (int i = 1; i < _Count; i++) - { - ImDrawChannel& ch = _Channels[i]; - if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0 && ch._CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd() - ch._CmdBuffer.pop_back(); - - if (ch._CmdBuffer.Size > 0 && last_cmd != NULL) - { - // Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves. - // Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter. - ImDrawCmd* next_cmd = &ch._CmdBuffer[0]; - if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL) - { - // Merge previous channel last draw command with current channel first draw command if matching. - last_cmd->ElemCount += next_cmd->ElemCount; - idx_offset += next_cmd->ElemCount; - ch._CmdBuffer.erase(ch._CmdBuffer.Data); // FIXME-OPT: Improve for multiple merges. - } - } - if (ch._CmdBuffer.Size > 0) - last_cmd = &ch._CmdBuffer.back(); - new_cmd_buffer_count += ch._CmdBuffer.Size; - new_idx_buffer_count += ch._IdxBuffer.Size; - for (int cmd_n = 0; cmd_n < ch._CmdBuffer.Size; cmd_n++) - { - ch._CmdBuffer.Data[cmd_n].IdxOffset = idx_offset; - idx_offset += ch._CmdBuffer.Data[cmd_n].ElemCount; - } - } - draw_list->CmdBuffer.resize(draw_list->CmdBuffer.Size + new_cmd_buffer_count); - draw_list->IdxBuffer.resize(draw_list->IdxBuffer.Size + new_idx_buffer_count); - - // Write commands and indices in order (they are fairly small structures, we don't copy vertices only indices) - ImDrawCmd* cmd_write = draw_list->CmdBuffer.Data + draw_list->CmdBuffer.Size - new_cmd_buffer_count; - ImDrawIdx* idx_write = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size - new_idx_buffer_count; - for (int i = 1; i < _Count; i++) - { - ImDrawChannel& ch = _Channels[i]; - if (int sz = ch._CmdBuffer.Size) { memcpy(cmd_write, ch._CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; } - if (int sz = ch._IdxBuffer.Size) { memcpy(idx_write, ch._IdxBuffer.Data, sz * sizeof(ImDrawIdx)); idx_write += sz; } - } - draw_list->_IdxWritePtr = idx_write; - - // Ensure there's always a non-callback draw command trailing the command-buffer - if (draw_list->CmdBuffer.Size == 0 || draw_list->CmdBuffer.back().UserCallback != NULL) - draw_list->AddDrawCmd(); - - // If current command is used with different settings we need to add a new command - ImDrawCmd* curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1]; - if (curr_cmd->ElemCount == 0) - ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset - else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0) - draw_list->AddDrawCmd(); - - _Count = 1; -} - -void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx) -{ - IM_ASSERT(idx >= 0 && idx < _Count); - if (_Current == idx) - return; - - // Overwrite ImVector (12/16 bytes), four times. This is merely a silly optimization instead of doing .swap() - memcpy(&_Channels.Data[_Current]._CmdBuffer, &draw_list->CmdBuffer, sizeof(draw_list->CmdBuffer)); - memcpy(&_Channels.Data[_Current]._IdxBuffer, &draw_list->IdxBuffer, sizeof(draw_list->IdxBuffer)); - _Current = idx; - memcpy(&draw_list->CmdBuffer, &_Channels.Data[idx]._CmdBuffer, sizeof(draw_list->CmdBuffer)); - memcpy(&draw_list->IdxBuffer, &_Channels.Data[idx]._IdxBuffer, sizeof(draw_list->IdxBuffer)); - draw_list->_IdxWritePtr = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size; - - // If current command is used with different settings we need to add a new command - ImDrawCmd* curr_cmd = (draw_list->CmdBuffer.Size == 0) ? NULL : &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1]; - if (curr_cmd == NULL) - draw_list->AddDrawCmd(); - else if (curr_cmd->ElemCount == 0) - ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset - else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0) - draw_list->AddDrawCmd(); -} - -//----------------------------------------------------------------------------- -// [SECTION] ImDrawData -//----------------------------------------------------------------------------- - -// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! -void ImDrawData::DeIndexAllBuffers() -{ - ImVector new_vtx_buffer; - TotalVtxCount = TotalIdxCount = 0; - for (int i = 0; i < CmdListsCount; i++) - { - ImDrawList* cmd_list = CmdLists[i]; - if (cmd_list->IdxBuffer.empty()) - continue; - new_vtx_buffer.resize(cmd_list->IdxBuffer.Size); - for (int j = 0; j < cmd_list->IdxBuffer.Size; j++) - new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]]; - cmd_list->VtxBuffer.swap(new_vtx_buffer); - cmd_list->IdxBuffer.resize(0); - TotalVtxCount += cmd_list->VtxBuffer.Size; - } -} - -// Helper to scale the ClipRect field of each ImDrawCmd. -// Use if your final output buffer is at a different scale than draw_data->DisplaySize, -// or if there is a difference between your window resolution and framebuffer resolution. -void ImDrawData::ScaleClipRects(const ImVec2& fb_scale) -{ - for (int i = 0; i < CmdListsCount; i++) - { - ImDrawList* cmd_list = CmdLists[i]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i]; - cmd->ClipRect = ImVec4(cmd->ClipRect.x * fb_scale.x, cmd->ClipRect.y * fb_scale.y, cmd->ClipRect.z * fb_scale.x, cmd->ClipRect.w * fb_scale.y); - } - } -} - -//----------------------------------------------------------------------------- -// [SECTION] Helpers ShadeVertsXXX functions -//----------------------------------------------------------------------------- - -// Generic linear color gradient, write to RGB fields, leave A untouched. -void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1) -{ - ImVec2 gradient_extent = gradient_p1 - gradient_p0; - float gradient_inv_length2 = 1.0f / ImLengthSqr(gradient_extent); - ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx; - ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx; - const int col0_r = (int)(col0 >> IM_COL32_R_SHIFT) & 0xFF; - const int col0_g = (int)(col0 >> IM_COL32_G_SHIFT) & 0xFF; - const int col0_b = (int)(col0 >> IM_COL32_B_SHIFT) & 0xFF; - const int col_delta_r = ((int)(col1 >> IM_COL32_R_SHIFT) & 0xFF) - col0_r; - const int col_delta_g = ((int)(col1 >> IM_COL32_G_SHIFT) & 0xFF) - col0_g; - const int col_delta_b = ((int)(col1 >> IM_COL32_B_SHIFT) & 0xFF) - col0_b; - for (ImDrawVert* vert = vert_start; vert < vert_end; vert++) - { - float d = ImDot(vert->pos - gradient_p0, gradient_extent); - float t = ImClamp(d * gradient_inv_length2, 0.0f, 1.0f); - int r = (int)(col0_r + col_delta_r * t); - int g = (int)(col0_g + col_delta_g * t); - int b = (int)(col0_b + col_delta_b * t); - vert->col = (r << IM_COL32_R_SHIFT) | (g << IM_COL32_G_SHIFT) | (b << IM_COL32_B_SHIFT) | (vert->col & IM_COL32_A_MASK); - } -} - -// Distribute UV over (a, b) rectangle -void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp) -{ - const ImVec2 size = b - a; - const ImVec2 uv_size = uv_b - uv_a; - const ImVec2 scale = ImVec2( - size.x != 0.0f ? (uv_size.x / size.x) : 0.0f, - size.y != 0.0f ? (uv_size.y / size.y) : 0.0f); - - ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx; - ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx; - if (clamp) - { - const ImVec2 min = ImMin(uv_a, uv_b); - const ImVec2 max = ImMax(uv_a, uv_b); - for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex) - vertex->uv = ImClamp(uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale), min, max); - } - else - { - for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex) - vertex->uv = uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale); - } -} - -//----------------------------------------------------------------------------- -// [SECTION] ImFontConfig -//----------------------------------------------------------------------------- - -ImFontConfig::ImFontConfig() -{ - memset(this, 0, sizeof(*this)); - FontDataOwnedByAtlas = true; - OversampleH = 3; // FIXME: 2 may be a better default? - OversampleV = 1; - GlyphMaxAdvanceX = FLT_MAX; - RasterizerMultiply = 1.0f; - EllipsisChar = (ImWchar)-1; -} - -//----------------------------------------------------------------------------- -// [SECTION] ImFontAtlas -//----------------------------------------------------------------------------- - -// A work of art lies ahead! (. = white layer, X = black layer, others are blank) -// The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes. -// (This is used when io.MouseDrawCursor = true) -const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 122; // Actual texture will be 2 times that + 1 spacing. -const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27; -static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = -{ - "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX - XX XX " - "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X -X..X X..X" - "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X -X...X X...X" - "X - X.X - X.....X - X.....X -X...X - X...X- X..X - X...X X...X " - "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X - X...X...X " - "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX - X.....X " - "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX - X...X " - "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX - X.X " - "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X - X...X " - "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X- X.....X " - "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X- X...X...X " - "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X- X...X X...X " - "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X-X...X X...X" - "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X-X..X X..X" - "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X- XX XX " - "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X--------------" - "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X - " - "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X - " - "X.X X..X - -X.......X- X.......X - XX XX - - X..........X - " - "XX X..X - - X.....X - X.....X - X.X X.X - - X........X - " - " X..X - - X...X - X...X - X..X X..X - - X........X - " - " XX - - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX - " - "------------- - X - X -X.....................X- ------------------- " - " ----------------------------------- X...XXXXXXXXXXXXX...X - " - " - X..X X..X - " - " - X.X X.X - " - " - XX XX - " -}; - -static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] = -{ - // Pos ........ Size ......... Offset ...... - { ImVec2( 0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow - { ImVec2(13,0), ImVec2( 7,16), ImVec2( 1, 8) }, // ImGuiMouseCursor_TextInput - { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_ResizeAll - { ImVec2(21,0), ImVec2( 9,23), ImVec2( 4,11) }, // ImGuiMouseCursor_ResizeNS - { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 4) }, // ImGuiMouseCursor_ResizeEW - { ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW - { ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE - { ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand - { ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed -}; - -ImFontAtlas::ImFontAtlas() -{ - memset(this, 0, sizeof(*this)); - TexGlyphPadding = 1; - PackIdMouseCursors = PackIdLines = -1; -} - -ImFontAtlas::~ImFontAtlas() -{ - IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - Clear(); -} - -void ImFontAtlas::ClearInputData() -{ - IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - for (int i = 0; i < ConfigData.Size; i++) - if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas) - { - IM_FREE(ConfigData[i].FontData); - ConfigData[i].FontData = NULL; - } - - // When clearing this we lose access to the font name and other information used to build the font. - for (int i = 0; i < Fonts.Size; i++) - if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size) - { - Fonts[i]->ConfigData = NULL; - Fonts[i]->ConfigDataCount = 0; - } - ConfigData.clear(); - CustomRects.clear(); - PackIdMouseCursors = PackIdLines = -1; - // Important: we leave TexReady untouched -} - -void ImFontAtlas::ClearTexData() -{ - IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - if (TexPixelsAlpha8) - IM_FREE(TexPixelsAlpha8); - if (TexPixelsRGBA32) - IM_FREE(TexPixelsRGBA32); - TexPixelsAlpha8 = NULL; - TexPixelsRGBA32 = NULL; - TexPixelsUseColors = false; - // Important: we leave TexReady untouched -} - -void ImFontAtlas::ClearFonts() -{ - IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - Fonts.clear_delete(); - TexReady = false; -} - -void ImFontAtlas::Clear() -{ - ClearInputData(); - ClearTexData(); - ClearFonts(); -} - -void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) -{ - // Build atlas on demand - if (TexPixelsAlpha8 == NULL) - Build(); - - *out_pixels = TexPixelsAlpha8; - if (out_width) *out_width = TexWidth; - if (out_height) *out_height = TexHeight; - if (out_bytes_per_pixel) *out_bytes_per_pixel = 1; -} - -void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) -{ - // Convert to RGBA32 format on demand - // Although it is likely to be the most commonly used format, our font rendering is 1 channel / 8 bpp - if (!TexPixelsRGBA32) - { - unsigned char* pixels = NULL; - GetTexDataAsAlpha8(&pixels, NULL, NULL); - if (pixels) - { - TexPixelsRGBA32 = (unsigned int*)IM_ALLOC((size_t)TexWidth * (size_t)TexHeight * 4); - const unsigned char* src = pixels; - unsigned int* dst = TexPixelsRGBA32; - for (int n = TexWidth * TexHeight; n > 0; n--) - *dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++)); - } - } - - *out_pixels = (unsigned char*)TexPixelsRGBA32; - if (out_width) *out_width = TexWidth; - if (out_height) *out_height = TexHeight; - if (out_bytes_per_pixel) *out_bytes_per_pixel = 4; -} - -ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) -{ - IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0); - IM_ASSERT(font_cfg->SizePixels > 0.0f); - - // Create new font - if (!font_cfg->MergeMode) - Fonts.push_back(IM_NEW(ImFont)); - else - IM_ASSERT(!Fonts.empty() && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. - - ConfigData.push_back(*font_cfg); - ImFontConfig& new_font_cfg = ConfigData.back(); - if (new_font_cfg.DstFont == NULL) - new_font_cfg.DstFont = Fonts.back(); - if (!new_font_cfg.FontDataOwnedByAtlas) - { - new_font_cfg.FontData = IM_ALLOC(new_font_cfg.FontDataSize); - new_font_cfg.FontDataOwnedByAtlas = true; - memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize); - } - - if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1) - new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar; - - // Invalidate texture - TexReady = false; - ClearTexData(); - return new_font_cfg.DstFont; -} - -// Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder) -static unsigned int stb_decompress_length(const unsigned char* input); -static unsigned int stb_decompress(unsigned char* output, const unsigned char* input, unsigned int length); -static const char* GetDefaultCompressedFontDataTTFBase85(); -static unsigned int Decode85Byte(char c) { return c >= '\\' ? c-36 : c-35; } -static void Decode85(const unsigned char* src, unsigned char* dst) -{ - while (*src) - { - unsigned int tmp = Decode85Byte(src[0]) + 85 * (Decode85Byte(src[1]) + 85 * (Decode85Byte(src[2]) + 85 * (Decode85Byte(src[3]) + 85 * Decode85Byte(src[4])))); - dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF); // We can't assume little-endianness. - src += 5; - dst += 4; - } -} - -// Load embedded ProggyClean.ttf at size 13, disable oversampling -ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) -{ - ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); - if (!font_cfg_template) - { - font_cfg.OversampleH = font_cfg.OversampleV = 1; - font_cfg.PixelSnapH = true; - } - if (font_cfg.SizePixels <= 0.0f) - font_cfg.SizePixels = 13.0f * 1.0f; - if (font_cfg.Name[0] == '\0') - ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels); - font_cfg.EllipsisChar = (ImWchar)0x0085; - font_cfg.GlyphOffset.y = 1.0f * IM_FLOOR(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units - - const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); - const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault(); - ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, glyph_ranges); - return font; -} - -ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) -{ - IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - size_t data_size = 0; - void* data = ImFileLoadToMemory(filename, "rb", &data_size, 0); - if (!data) - { - IM_ASSERT_USER_ERROR(0, "Could not load font file!"); - return NULL; - } - ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); - if (font_cfg.Name[0] == '\0') - { - // Store a short copy of filename into into the font name for convenience - const char* p; - for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} - ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s, %.0fpx", p, size_pixels); - } - return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges); -} - -// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build(). -ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) -{ - IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); - IM_ASSERT(font_cfg.FontData == NULL); - font_cfg.FontData = ttf_data; - font_cfg.FontDataSize = ttf_size; - font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels; - if (glyph_ranges) - font_cfg.GlyphRanges = glyph_ranges; - return AddFont(&font_cfg); -} - -ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) -{ - const unsigned int buf_decompressed_size = stb_decompress_length((const unsigned char*)compressed_ttf_data); - unsigned char* buf_decompressed_data = (unsigned char*)IM_ALLOC(buf_decompressed_size); - stb_decompress(buf_decompressed_data, (const unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size); - - ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); - IM_ASSERT(font_cfg.FontData == NULL); - font_cfg.FontDataOwnedByAtlas = true; - return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, &font_cfg, glyph_ranges); -} - -ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges) -{ - int compressed_ttf_size = (((int)strlen(compressed_ttf_data_base85) + 4) / 5) * 4; - void* compressed_ttf = IM_ALLOC((size_t)compressed_ttf_size); - Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf); - ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges); - IM_FREE(compressed_ttf); - return font; -} - -int ImFontAtlas::AddCustomRectRegular(int width, int height) -{ - IM_ASSERT(width > 0 && width <= 0xFFFF); - IM_ASSERT(height > 0 && height <= 0xFFFF); - ImFontAtlasCustomRect r; - r.Width = (unsigned short)width; - r.Height = (unsigned short)height; - CustomRects.push_back(r); - return CustomRects.Size - 1; // Return index -} - -int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset) -{ -#ifdef IMGUI_USE_WCHAR32 - IM_ASSERT(id <= IM_UNICODE_CODEPOINT_MAX); -#endif - IM_ASSERT(font != NULL); - IM_ASSERT(width > 0 && width <= 0xFFFF); - IM_ASSERT(height > 0 && height <= 0xFFFF); - ImFontAtlasCustomRect r; - r.Width = (unsigned short)width; - r.Height = (unsigned short)height; - r.GlyphID = id; - r.GlyphAdvanceX = advance_x; - r.GlyphOffset = offset; - r.Font = font; - CustomRects.push_back(r); - return CustomRects.Size - 1; // Return index -} - -void ImFontAtlas::CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const -{ - IM_ASSERT(TexWidth > 0 && TexHeight > 0); // Font atlas needs to be built before we can calculate UV coordinates - IM_ASSERT(rect->IsPacked()); // Make sure the rectangle has been packed - *out_uv_min = ImVec2((float)rect->X * TexUvScale.x, (float)rect->Y * TexUvScale.y); - *out_uv_max = ImVec2((float)(rect->X + rect->Width) * TexUvScale.x, (float)(rect->Y + rect->Height) * TexUvScale.y); -} - -bool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]) -{ - if (cursor_type <= ImGuiMouseCursor_None || cursor_type >= ImGuiMouseCursor_COUNT) - return false; - if (Flags & ImFontAtlasFlags_NoMouseCursors) - return false; - - IM_ASSERT(PackIdMouseCursors != -1); - ImFontAtlasCustomRect* r = GetCustomRectByIndex(PackIdMouseCursors); - ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r->X, (float)r->Y); - ImVec2 size = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][1]; - *out_size = size; - *out_offset = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][2]; - out_uv_border[0] = (pos) * TexUvScale; - out_uv_border[1] = (pos + size) * TexUvScale; - pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W + 1; - out_uv_fill[0] = (pos) * TexUvScale; - out_uv_fill[1] = (pos + size) * TexUvScale; - return true; -} - -bool ImFontAtlas::Build() -{ - IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - - // Default font is none are specified - if (ConfigData.Size == 0) - AddFontDefault(); - - // Select builder - // - Note that we do not reassign to atlas->FontBuilderIO, since it is likely to point to static data which - // may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are - // using a hot-reloading scheme that messes up static data, store your own instance of ImFontBuilderIO somewhere - // and point to it instead of pointing directly to return value of the GetBuilderXXX functions. - const ImFontBuilderIO* builder_io = FontBuilderIO; - if (builder_io == NULL) - { -#ifdef IMGUI_ENABLE_FREETYPE - builder_io = ImGuiFreeType::GetBuilderForFreeType(); -#elif defined(IMGUI_ENABLE_STB_TRUETYPE) - builder_io = ImFontAtlasGetBuilderForStbTruetype(); -#else - IM_ASSERT(0); // Invalid Build function -#endif - } - - // Build - return builder_io->FontBuilder_Build(this); -} - -void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_brighten_factor) -{ - for (unsigned int i = 0; i < 256; i++) - { - unsigned int value = (unsigned int)(i * in_brighten_factor); - out_table[i] = value > 255 ? 255 : (value & 0xFF); - } -} - -void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride) -{ - unsigned char* data = pixels + x + y * stride; - for (int j = h; j > 0; j--, data += stride) - for (int i = 0; i < w; i++) - data[i] = table[data[i]]; -} - -#ifdef IMGUI_ENABLE_STB_TRUETYPE -// Temporary data for one source font (multiple source fonts can be merged into one destination ImFont) -// (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.) -struct ImFontBuildSrcData -{ - stbtt_fontinfo FontInfo; - stbtt_pack_range PackRange; // Hold the list of codepoints to pack (essentially points to Codepoints.Data) - stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position. - stbtt_packedchar* PackedChars; // Output glyphs - const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF) - int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[] - int GlyphsHighest; // Highest requested codepoint - int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font) - ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB) - ImVector GlyphsList; // Glyph codepoints list (flattened version of GlyphsMap) -}; - -// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont) -struct ImFontBuildDstData -{ - int SrcCount; // Number of source fonts targeting this destination font. - int GlyphsHighest; - int GlyphsCount; - ImBitVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font. -}; - -static void UnpackBitVectorToFlatIndexList(const ImBitVector* in, ImVector* out) -{ - IM_ASSERT(sizeof(in->Storage.Data[0]) == sizeof(int)); - const ImU32* it_begin = in->Storage.begin(); - const ImU32* it_end = in->Storage.end(); - for (const ImU32* it = it_begin; it < it_end; it++) - if (ImU32 entries_32 = *it) - for (ImU32 bit_n = 0; bit_n < 32; bit_n++) - if (entries_32 & ((ImU32)1 << bit_n)) - out->push_back((int)(((it - it_begin) << 5) + bit_n)); -} - -static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) -{ - IM_ASSERT(atlas->ConfigData.Size > 0); - - ImFontAtlasBuildInit(atlas); - - // Clear atlas - atlas->TexID = (ImTextureID)NULL; - atlas->TexWidth = atlas->TexHeight = 0; - atlas->TexUvScale = ImVec2(0.0f, 0.0f); - atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); - atlas->ClearTexData(); - - // Temporary storage for building - ImVector src_tmp_array; - ImVector dst_tmp_array; - src_tmp_array.resize(atlas->ConfigData.Size); - dst_tmp_array.resize(atlas->Fonts.Size); - memset(src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes()); - memset(dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes()); - - // 1. Initialize font loading structure, check font data validity - for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++) - { - ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; - ImFontConfig& cfg = atlas->ConfigData[src_i]; - IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas)); - - // Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices) - src_tmp.DstIndex = -1; - for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++) - if (cfg.DstFont == atlas->Fonts[output_i]) - src_tmp.DstIndex = output_i; - if (src_tmp.DstIndex == -1) - { - IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array? - return false; - } - // Initialize helper structure for font loading and verify that the TTF/OTF data is correct - const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); - IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found."); - if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) - return false; - - // Measure highest codepoints - ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex]; - src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault(); - for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) - src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); - dst_tmp.SrcCount++; - dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest); - } - - // 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs. - int total_glyphs_count = 0; - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - { - ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; - ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex]; - src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1); - if (dst_tmp.GlyphsSet.Storage.empty()) - dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1); - - for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) - for (unsigned int codepoint = src_range[0]; codepoint <= src_range[1]; codepoint++) - { - if (dst_tmp.GlyphsSet.TestBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option for MergeMode (e.g. MergeOverwrite==true) - continue; - if (!stbtt_FindGlyphIndex(&src_tmp.FontInfo, codepoint)) // It is actually in the font? - continue; - - // Add to avail set/counters - src_tmp.GlyphsCount++; - dst_tmp.GlyphsCount++; - src_tmp.GlyphsSet.SetBit(codepoint); - dst_tmp.GlyphsSet.SetBit(codepoint); - total_glyphs_count++; - } - } - - // 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another) - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - { - ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; - src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount); - UnpackBitVectorToFlatIndexList(&src_tmp.GlyphsSet, &src_tmp.GlyphsList); - src_tmp.GlyphsSet.Clear(); - IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount); - } - for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++) - dst_tmp_array[dst_i].GlyphsSet.Clear(); - dst_tmp_array.clear(); - - // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) - // (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity) - ImVector buf_rects; - ImVector buf_packedchars; - buf_rects.resize(total_glyphs_count); - buf_packedchars.resize(total_glyphs_count); - memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes()); - memset(buf_packedchars.Data, 0, (size_t)buf_packedchars.size_in_bytes()); - - // 4. Gather glyphs sizes so we can pack them in our virtual canvas. - int total_surface = 0; - int buf_rects_out_n = 0; - int buf_packedchars_out_n = 0; - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - { - ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; - if (src_tmp.GlyphsCount == 0) - continue; - - src_tmp.Rects = &buf_rects[buf_rects_out_n]; - src_tmp.PackedChars = &buf_packedchars[buf_packedchars_out_n]; - buf_rects_out_n += src_tmp.GlyphsCount; - buf_packedchars_out_n += src_tmp.GlyphsCount; - - // Convert our ranges in the format stb_truetype wants - ImFontConfig& cfg = atlas->ConfigData[src_i]; - src_tmp.PackRange.font_size = cfg.SizePixels; - src_tmp.PackRange.first_unicode_codepoint_in_range = 0; - src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data; - src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size; - src_tmp.PackRange.chardata_for_range = src_tmp.PackedChars; - src_tmp.PackRange.h_oversample = (unsigned char)cfg.OversampleH; - src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV; - - // Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects) - const float scale = (cfg.SizePixels > 0) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels); - const int padding = atlas->TexGlyphPadding; - for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++) - { - int x0, y0, x1, y1; - const int glyph_index_in_font = stbtt_FindGlyphIndex(&src_tmp.FontInfo, src_tmp.GlyphsList[glyph_i]); - IM_ASSERT(glyph_index_in_font != 0); - stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * cfg.OversampleH, scale * cfg.OversampleV, 0, 0, &x0, &y0, &x1, &y1); - src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + padding + cfg.OversampleH - 1); - src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + padding + cfg.OversampleV - 1); - total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h; - } - } - - // We need a width for the skyline algorithm, any width! - // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. - // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface. - const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1; - atlas->TexHeight = 0; - if (atlas->TexDesiredWidth > 0) - atlas->TexWidth = atlas->TexDesiredWidth; - else - atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512; - - // 5. Start packing - // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). - const int TEX_HEIGHT_MAX = 1024 * 32; - stbtt_pack_context spc = {}; - stbtt_PackBegin(&spc, NULL, atlas->TexWidth, TEX_HEIGHT_MAX, 0, atlas->TexGlyphPadding, NULL); - ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info); - - // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point. - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - { - ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; - if (src_tmp.GlyphsCount == 0) - continue; - - stbrp_pack_rects((stbrp_context*)spc.pack_info, src_tmp.Rects, src_tmp.GlyphsCount); - - // Extend texture height and mark missing glyphs as non-packed so we won't render them. - // FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?) - for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) - if (src_tmp.Rects[glyph_i].was_packed) - atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h); - } - - // 7. Allocate texture - atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight); - atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); - atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight); - memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight); - spc.pixels = atlas->TexPixelsAlpha8; - spc.height = atlas->TexHeight; - - // 8. Render/rasterize font characters into the texture - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - { - ImFontConfig& cfg = atlas->ConfigData[src_i]; - ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; - if (src_tmp.GlyphsCount == 0) - continue; - - stbtt_PackFontRangesRenderIntoRects(&spc, &src_tmp.FontInfo, &src_tmp.PackRange, 1, src_tmp.Rects); - - // Apply multiply operator - if (cfg.RasterizerMultiply != 1.0f) - { - unsigned char multiply_table[256]; - ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply); - stbrp_rect* r = &src_tmp.Rects[0]; - for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++, r++) - if (r->was_packed) - ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, atlas->TexPixelsAlpha8, r->x, r->y, r->w, r->h, atlas->TexWidth * 1); - } - src_tmp.Rects = NULL; - } - - // End packing - stbtt_PackEnd(&spc); - buf_rects.clear(); - - // 9. Setup ImFont and glyphs for runtime - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - { - ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; - if (src_tmp.GlyphsCount == 0) - continue; - - // When merging fonts with MergeMode=true: - // - We can have multiple input fonts writing into a same destination font. - // - dst_font->ConfigData is != from cfg which is our source configuration. - ImFontConfig& cfg = atlas->ConfigData[src_i]; - ImFont* dst_font = cfg.DstFont; - - const float font_scale = stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels); - int unscaled_ascent, unscaled_descent, unscaled_line_gap; - stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); - - const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1)); - const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1)); - ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); - const float font_off_x = cfg.GlyphOffset.x; - const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent); - - for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) - { - // Register glyph - const int codepoint = src_tmp.GlyphsList[glyph_i]; - const stbtt_packedchar& pc = src_tmp.PackedChars[glyph_i]; - stbtt_aligned_quad q; - float unused_x = 0.0f, unused_y = 0.0f; - stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0); - dst_font->AddGlyph(&cfg, (ImWchar)codepoint, q.x0 + font_off_x, q.y0 + font_off_y, q.x1 + font_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, pc.xadvance); - } - } - - // Cleanup - src_tmp_array.clear_destruct(); - - ImFontAtlasBuildFinish(atlas); - return true; -} - -const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype() -{ - static ImFontBuilderIO io; - io.FontBuilder_Build = ImFontAtlasBuildWithStbTruetype; - return &io; -} - -#endif // IMGUI_ENABLE_STB_TRUETYPE - -void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent) -{ - if (!font_config->MergeMode) - { - font->ClearOutputData(); - font->FontSize = font_config->SizePixels; - font->ConfigData = font_config; - font->ConfigDataCount = 0; - font->ContainerAtlas = atlas; - font->Ascent = ascent; - font->Descent = descent; - } - font->ConfigDataCount++; -} - -void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque) -{ - stbrp_context* pack_context = (stbrp_context*)stbrp_context_opaque; - IM_ASSERT(pack_context != NULL); - - ImVector& user_rects = atlas->CustomRects; - IM_ASSERT(user_rects.Size >= 1); // We expect at least the default custom rects to be registered, else something went wrong. - - ImVector pack_rects; - pack_rects.resize(user_rects.Size); - memset(pack_rects.Data, 0, (size_t)pack_rects.size_in_bytes()); - for (int i = 0; i < user_rects.Size; i++) - { - pack_rects[i].w = user_rects[i].Width; - pack_rects[i].h = user_rects[i].Height; - } - stbrp_pack_rects(pack_context, &pack_rects[0], pack_rects.Size); - for (int i = 0; i < pack_rects.Size; i++) - if (pack_rects[i].was_packed) - { - user_rects[i].X = (unsigned short)pack_rects[i].x; - user_rects[i].Y = (unsigned short)pack_rects[i].y; - IM_ASSERT(pack_rects[i].w == user_rects[i].Width && pack_rects[i].h == user_rects[i].Height); - atlas->TexHeight = ImMax(atlas->TexHeight, pack_rects[i].y + pack_rects[i].h); - } -} - -void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value) -{ - IM_ASSERT(x >= 0 && x + w <= atlas->TexWidth); - IM_ASSERT(y >= 0 && y + h <= atlas->TexHeight); - unsigned char* out_pixel = atlas->TexPixelsAlpha8 + x + (y * atlas->TexWidth); - for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexWidth, in_str += w) - for (int off_x = 0; off_x < w; off_x++) - out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : 0x00; -} - -void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value) -{ - IM_ASSERT(x >= 0 && x + w <= atlas->TexWidth); - IM_ASSERT(y >= 0 && y + h <= atlas->TexHeight); - unsigned int* out_pixel = atlas->TexPixelsRGBA32 + x + (y * atlas->TexWidth); - for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexWidth, in_str += w) - for (int off_x = 0; off_x < w; off_x++) - out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : IM_COL32_BLACK_TRANS; -} - -static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) -{ - ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdMouseCursors); - IM_ASSERT(r->IsPacked()); - - const int w = atlas->TexWidth; - if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors)) - { - // Render/copy pixels - IM_ASSERT(r->Width == FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1 && r->Height == FONT_ATLAS_DEFAULT_TEX_DATA_H); - const int x_for_white = r->X; - const int x_for_black = r->X + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1; - if (atlas->TexPixelsAlpha8 != NULL) - { - ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', 0xFF); - ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', 0xFF); - } - else - { - ImFontAtlasBuildRender32bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', IM_COL32_WHITE); - ImFontAtlasBuildRender32bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', IM_COL32_WHITE); - } - } - else - { - // Render 4 white pixels - IM_ASSERT(r->Width == 2 && r->Height == 2); - const int offset = (int)r->X + (int)r->Y * w; - if (atlas->TexPixelsAlpha8 != NULL) - { - atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF; - } - else - { - atlas->TexPixelsRGBA32[offset] = atlas->TexPixelsRGBA32[offset + 1] = atlas->TexPixelsRGBA32[offset + w] = atlas->TexPixelsRGBA32[offset + w + 1] = IM_COL32_WHITE; - } - } - atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); -} - -static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas) -{ - if (atlas->Flags & ImFontAtlasFlags_NoBakedLines) - return; - - // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them - ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdLines); - IM_ASSERT(r->IsPacked()); - for (unsigned int n = 0; n < IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row - { - // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle - unsigned int y = n; - unsigned int line_width = n; - unsigned int pad_left = (r->Width - line_width) / 2; - unsigned int pad_right = r->Width - (pad_left + line_width); - - // Write each slice - IM_ASSERT(pad_left + line_width + pad_right == r->Width && y < r->Height); // Make sure we're inside the texture bounds before we start writing pixels - if (atlas->TexPixelsAlpha8 != NULL) - { - unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)]; - for (unsigned int i = 0; i < pad_left; i++) - *(write_ptr + i) = 0x00; - - for (unsigned int i = 0; i < line_width; i++) - *(write_ptr + pad_left + i) = 0xFF; - - for (unsigned int i = 0; i < pad_right; i++) - *(write_ptr + pad_left + line_width + i) = 0x00; - } - else - { - unsigned int* write_ptr = &atlas->TexPixelsRGBA32[r->X + ((r->Y + y) * atlas->TexWidth)]; - for (unsigned int i = 0; i < pad_left; i++) - *(write_ptr + i) = IM_COL32(255, 255, 255, 0); - - for (unsigned int i = 0; i < line_width; i++) - *(write_ptr + pad_left + i) = IM_COL32_WHITE; - - for (unsigned int i = 0; i < pad_right; i++) - *(write_ptr + pad_left + line_width + i) = IM_COL32(255, 255, 255, 0); - } - - // Calculate UVs for this line - ImVec2 uv0 = ImVec2((float)(r->X + pad_left - 1), (float)(r->Y + y)) * atlas->TexUvScale; - ImVec2 uv1 = ImVec2((float)(r->X + pad_left + line_width + 1), (float)(r->Y + y + 1)) * atlas->TexUvScale; - float half_v = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts - atlas->TexUvLines[n] = ImVec4(uv0.x, half_v, uv1.x, half_v); - } -} - -// Note: this is called / shared by both the stb_truetype and the FreeType builder -void ImFontAtlasBuildInit(ImFontAtlas* atlas) -{ - // Register texture region for mouse cursors or standard white pixels - if (atlas->PackIdMouseCursors < 0) - { - if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors)) - atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H); - else - atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(2, 2); - } - - // Register texture region for thick lines - // The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row - if (atlas->PackIdLines < 0) - { - if (!(atlas->Flags & ImFontAtlasFlags_NoBakedLines)) - atlas->PackIdLines = atlas->AddCustomRectRegular(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1); - } -} - -// This is called/shared by both the stb_truetype and the FreeType builder. -void ImFontAtlasBuildFinish(ImFontAtlas* atlas) -{ - // Render into our custom data blocks - IM_ASSERT(atlas->TexPixelsAlpha8 != NULL || atlas->TexPixelsRGBA32 != NULL); - ImFontAtlasBuildRenderDefaultTexData(atlas); - ImFontAtlasBuildRenderLinesTexData(atlas); - - // Register custom rectangle glyphs - for (int i = 0; i < atlas->CustomRects.Size; i++) - { - const ImFontAtlasCustomRect* r = &atlas->CustomRects[i]; - if (r->Font == NULL || r->GlyphID == 0) - continue; - - // Will ignore ImFontConfig settings: GlyphMinAdvanceX, GlyphMinAdvanceY, GlyphExtraSpacing, PixelSnapH - IM_ASSERT(r->Font->ContainerAtlas == atlas); - ImVec2 uv0, uv1; - atlas->CalcCustomRectUV(r, &uv0, &uv1); - r->Font->AddGlyph(NULL, (ImWchar)r->GlyphID, r->GlyphOffset.x, r->GlyphOffset.y, r->GlyphOffset.x + r->Width, r->GlyphOffset.y + r->Height, uv0.x, uv0.y, uv1.x, uv1.y, r->GlyphAdvanceX); - } - - // Build all fonts lookup tables - for (int i = 0; i < atlas->Fonts.Size; i++) - if (atlas->Fonts[i]->DirtyLookupTables) - atlas->Fonts[i]->BuildLookupTable(); - - atlas->TexReady = true; -} - -// Retrieve list of range (2 int per range, values are inclusive) -const ImWchar* ImFontAtlas::GetGlyphRangesDefault() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0, - }; - return &ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesKorean() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3131, 0x3163, // Korean alphabets - 0xAC00, 0xD7A3, // Korean characters - 0xFFFD, 0xFFFD, // Invalid - 0, - }; - return &ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x2000, 0x206F, // General Punctuation - 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana - 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF, // Half-width characters - 0xFFFD, 0xFFFD, // Invalid - 0x4e00, 0x9FAF, // CJK Ideograms - 0, - }; - return &ranges[0]; -} - -static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short* accumulative_offsets, int accumulative_offsets_count, ImWchar* out_ranges) -{ - for (int n = 0; n < accumulative_offsets_count; n++, out_ranges += 2) - { - out_ranges[0] = out_ranges[1] = (ImWchar)(base_codepoint + accumulative_offsets[n]); - base_codepoint += accumulative_offsets[n]; - } - out_ranges[0] = 0; -} - -//------------------------------------------------------------------------- -// [SECTION] ImFontAtlas glyph ranges helpers -//------------------------------------------------------------------------- - -const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() -{ - // Store 2500 regularly used characters for Simplified Chinese. - // Sourced from https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8 - // This table covers 97.97% of all characters used during the month in July, 1987. - // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. - // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) - static const short accumulative_offsets_from_0x4E00[] = - { - 0,1,2,4,1,1,1,1,2,1,3,2,1,2,2,1,1,1,1,1,5,2,1,2,3,3,3,2,2,4,1,1,1,2,1,5,2,3,1,2,1,2,1,1,2,1,1,2,2,1,4,1,1,1,1,5,10,1,2,19,2,1,2,1,2,1,2,1,2, - 1,5,1,6,3,2,1,2,2,1,1,1,4,8,5,1,1,4,1,1,3,1,2,1,5,1,2,1,1,1,10,1,1,5,2,4,6,1,4,2,2,2,12,2,1,1,6,1,1,1,4,1,1,4,6,5,1,4,2,2,4,10,7,1,1,4,2,4, - 2,1,4,3,6,10,12,5,7,2,14,2,9,1,1,6,7,10,4,7,13,1,5,4,8,4,1,1,2,28,5,6,1,1,5,2,5,20,2,2,9,8,11,2,9,17,1,8,6,8,27,4,6,9,20,11,27,6,68,2,2,1,1, - 1,2,1,2,2,7,6,11,3,3,1,1,3,1,2,1,1,1,1,1,3,1,1,8,3,4,1,5,7,2,1,4,4,8,4,2,1,2,1,1,4,5,6,3,6,2,12,3,1,3,9,2,4,3,4,1,5,3,3,1,3,7,1,5,1,1,1,1,2, - 3,4,5,2,3,2,6,1,1,2,1,7,1,7,3,4,5,15,2,2,1,5,3,22,19,2,1,1,1,1,2,5,1,1,1,6,1,1,12,8,2,9,18,22,4,1,1,5,1,16,1,2,7,10,15,1,1,6,2,4,1,2,4,1,6, - 1,1,3,2,4,1,6,4,5,1,2,1,1,2,1,10,3,1,3,2,1,9,3,2,5,7,2,19,4,3,6,1,1,1,1,1,4,3,2,1,1,1,2,5,3,1,1,1,2,2,1,1,2,1,1,2,1,3,1,1,1,3,7,1,4,1,1,2,1, - 1,2,1,2,4,4,3,8,1,1,1,2,1,3,5,1,3,1,3,4,6,2,2,14,4,6,6,11,9,1,15,3,1,28,5,2,5,5,3,1,3,4,5,4,6,14,3,2,3,5,21,2,7,20,10,1,2,19,2,4,28,28,2,3, - 2,1,14,4,1,26,28,42,12,40,3,52,79,5,14,17,3,2,2,11,3,4,6,3,1,8,2,23,4,5,8,10,4,2,7,3,5,1,1,6,3,1,2,2,2,5,28,1,1,7,7,20,5,3,29,3,17,26,1,8,4, - 27,3,6,11,23,5,3,4,6,13,24,16,6,5,10,25,35,7,3,2,3,3,14,3,6,2,6,1,4,2,3,8,2,1,1,3,3,3,4,1,1,13,2,2,4,5,2,1,14,14,1,2,2,1,4,5,2,3,1,14,3,12, - 3,17,2,16,5,1,2,1,8,9,3,19,4,2,2,4,17,25,21,20,28,75,1,10,29,103,4,1,2,1,1,4,2,4,1,2,3,24,2,2,2,1,1,2,1,3,8,1,1,1,2,1,1,3,1,1,1,6,1,5,3,1,1, - 1,3,4,1,1,5,2,1,5,6,13,9,16,1,1,1,1,3,2,3,2,4,5,2,5,2,2,3,7,13,7,2,2,1,1,1,1,2,3,3,2,1,6,4,9,2,1,14,2,14,2,1,18,3,4,14,4,11,41,15,23,15,23, - 176,1,3,4,1,1,1,1,5,3,1,2,3,7,3,1,1,2,1,2,4,4,6,2,4,1,9,7,1,10,5,8,16,29,1,1,2,2,3,1,3,5,2,4,5,4,1,1,2,2,3,3,7,1,6,10,1,17,1,44,4,6,2,1,1,6, - 5,4,2,10,1,6,9,2,8,1,24,1,2,13,7,8,8,2,1,4,1,3,1,3,3,5,2,5,10,9,4,9,12,2,1,6,1,10,1,1,7,7,4,10,8,3,1,13,4,3,1,6,1,3,5,2,1,2,17,16,5,2,16,6, - 1,4,2,1,3,3,6,8,5,11,11,1,3,3,2,4,6,10,9,5,7,4,7,4,7,1,1,4,2,1,3,6,8,7,1,6,11,5,5,3,24,9,4,2,7,13,5,1,8,82,16,61,1,1,1,4,2,2,16,10,3,8,1,1, - 6,4,2,1,3,1,1,1,4,3,8,4,2,2,1,1,1,1,1,6,3,5,1,1,4,6,9,2,1,1,1,2,1,7,2,1,6,1,5,4,4,3,1,8,1,3,3,1,3,2,2,2,2,3,1,6,1,2,1,2,1,3,7,1,8,2,1,2,1,5, - 2,5,3,5,10,1,2,1,1,3,2,5,11,3,9,3,5,1,1,5,9,1,2,1,5,7,9,9,8,1,3,3,3,6,8,2,3,2,1,1,32,6,1,2,15,9,3,7,13,1,3,10,13,2,14,1,13,10,2,1,3,10,4,15, - 2,15,15,10,1,3,9,6,9,32,25,26,47,7,3,2,3,1,6,3,4,3,2,8,5,4,1,9,4,2,2,19,10,6,2,3,8,1,2,2,4,2,1,9,4,4,4,6,4,8,9,2,3,1,1,1,1,3,5,5,1,3,8,4,6, - 2,1,4,12,1,5,3,7,13,2,5,8,1,6,1,2,5,14,6,1,5,2,4,8,15,5,1,23,6,62,2,10,1,1,8,1,2,2,10,4,2,2,9,2,1,1,3,2,3,1,5,3,3,2,1,3,8,1,1,1,11,3,1,1,4, - 3,7,1,14,1,2,3,12,5,2,5,1,6,7,5,7,14,11,1,3,1,8,9,12,2,1,11,8,4,4,2,6,10,9,13,1,1,3,1,5,1,3,2,4,4,1,18,2,3,14,11,4,29,4,2,7,1,3,13,9,2,2,5, - 3,5,20,7,16,8,5,72,34,6,4,22,12,12,28,45,36,9,7,39,9,191,1,1,1,4,11,8,4,9,2,3,22,1,1,1,1,4,17,1,7,7,1,11,31,10,2,4,8,2,3,2,1,4,2,16,4,32,2, - 3,19,13,4,9,1,5,2,14,8,1,1,3,6,19,6,5,1,16,6,2,10,8,5,1,2,3,1,5,5,1,11,6,6,1,3,3,2,6,3,8,1,1,4,10,7,5,7,7,5,8,9,2,1,3,4,1,1,3,1,3,3,2,6,16, - 1,4,6,3,1,10,6,1,3,15,2,9,2,10,25,13,9,16,6,2,2,10,11,4,3,9,1,2,6,6,5,4,30,40,1,10,7,12,14,33,6,3,6,7,3,1,3,1,11,14,4,9,5,12,11,49,18,51,31, - 140,31,2,2,1,5,1,8,1,10,1,4,4,3,24,1,10,1,3,6,6,16,3,4,5,2,1,4,2,57,10,6,22,2,22,3,7,22,6,10,11,36,18,16,33,36,2,5,5,1,1,1,4,10,1,4,13,2,7, - 5,2,9,3,4,1,7,43,3,7,3,9,14,7,9,1,11,1,1,3,7,4,18,13,1,14,1,3,6,10,73,2,2,30,6,1,11,18,19,13,22,3,46,42,37,89,7,3,16,34,2,2,3,9,1,7,1,1,1,2, - 2,4,10,7,3,10,3,9,5,28,9,2,6,13,7,3,1,3,10,2,7,2,11,3,6,21,54,85,2,1,4,2,2,1,39,3,21,2,2,5,1,1,1,4,1,1,3,4,15,1,3,2,4,4,2,3,8,2,20,1,8,7,13, - 4,1,26,6,2,9,34,4,21,52,10,4,4,1,5,12,2,11,1,7,2,30,12,44,2,30,1,1,3,6,16,9,17,39,82,2,2,24,7,1,7,3,16,9,14,44,2,1,2,1,2,3,5,2,4,1,6,7,5,3, - 2,6,1,11,5,11,2,1,18,19,8,1,3,24,29,2,1,3,5,2,2,1,13,6,5,1,46,11,3,5,1,1,5,8,2,10,6,12,6,3,7,11,2,4,16,13,2,5,1,1,2,2,5,2,28,5,2,23,10,8,4, - 4,22,39,95,38,8,14,9,5,1,13,5,4,3,13,12,11,1,9,1,27,37,2,5,4,4,63,211,95,2,2,2,1,3,5,2,1,1,2,2,1,1,1,3,2,4,1,2,1,1,5,2,2,1,1,2,3,1,3,1,1,1, - 3,1,4,2,1,3,6,1,1,3,7,15,5,3,2,5,3,9,11,4,2,22,1,6,3,8,7,1,4,28,4,16,3,3,25,4,4,27,27,1,4,1,2,2,7,1,3,5,2,28,8,2,14,1,8,6,16,25,3,3,3,14,3, - 3,1,1,2,1,4,6,3,8,4,1,1,1,2,3,6,10,6,2,3,18,3,2,5,5,4,3,1,5,2,5,4,23,7,6,12,6,4,17,11,9,5,1,1,10,5,12,1,1,11,26,33,7,3,6,1,17,7,1,5,12,1,11, - 2,4,1,8,14,17,23,1,2,1,7,8,16,11,9,6,5,2,6,4,16,2,8,14,1,11,8,9,1,1,1,9,25,4,11,19,7,2,15,2,12,8,52,7,5,19,2,16,4,36,8,1,16,8,24,26,4,6,2,9, - 5,4,36,3,28,12,25,15,37,27,17,12,59,38,5,32,127,1,2,9,17,14,4,1,2,1,1,8,11,50,4,14,2,19,16,4,17,5,4,5,26,12,45,2,23,45,104,30,12,8,3,10,2,2, - 3,3,1,4,20,7,2,9,6,15,2,20,1,3,16,4,11,15,6,134,2,5,59,1,2,2,2,1,9,17,3,26,137,10,211,59,1,2,4,1,4,1,1,1,2,6,2,3,1,1,2,3,2,3,1,3,4,4,2,3,3, - 1,4,3,1,7,2,2,3,1,2,1,3,3,3,2,2,3,2,1,3,14,6,1,3,2,9,6,15,27,9,34,145,1,1,2,1,1,1,1,2,1,1,1,1,2,2,2,3,1,2,1,1,1,2,3,5,8,3,5,2,4,1,3,2,2,2,12, - 4,1,1,1,10,4,5,1,20,4,16,1,15,9,5,12,2,9,2,5,4,2,26,19,7,1,26,4,30,12,15,42,1,6,8,172,1,1,4,2,1,1,11,2,2,4,2,1,2,1,10,8,1,2,1,4,5,1,2,5,1,8, - 4,1,3,4,2,1,6,2,1,3,4,1,2,1,1,1,1,12,5,7,2,4,3,1,1,1,3,3,6,1,2,2,3,3,3,2,1,2,12,14,11,6,6,4,12,2,8,1,7,10,1,35,7,4,13,15,4,3,23,21,28,52,5, - 26,5,6,1,7,10,2,7,53,3,2,1,1,1,2,163,532,1,10,11,1,3,3,4,8,2,8,6,2,2,23,22,4,2,2,4,2,1,3,1,3,3,5,9,8,2,1,2,8,1,10,2,12,21,20,15,105,2,3,1,1, - 3,2,3,1,1,2,5,1,4,15,11,19,1,1,1,1,5,4,5,1,1,2,5,3,5,12,1,2,5,1,11,1,1,15,9,1,4,5,3,26,8,2,1,3,1,1,15,19,2,12,1,2,5,2,7,2,19,2,20,6,26,7,5, - 2,2,7,34,21,13,70,2,128,1,1,2,1,1,2,1,1,3,2,2,2,15,1,4,1,3,4,42,10,6,1,49,85,8,1,2,1,1,4,4,2,3,6,1,5,7,4,3,211,4,1,2,1,2,5,1,2,4,2,2,6,5,6, - 10,3,4,48,100,6,2,16,296,5,27,387,2,2,3,7,16,8,5,38,15,39,21,9,10,3,7,59,13,27,21,47,5,21,6 - }; - static ImWchar base_ranges[] = // not zero-terminated - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x2000, 0x206F, // General Punctuation - 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana - 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF, // Half-width characters - 0xFFFD, 0xFFFD // Invalid - }; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; - if (!full_ranges[0]) - { - memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); - } - return &full_ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() -{ - // 2999 ideograms code points for Japanese - // - 2136 Joyo (meaning "for regular use" or "for common use") Kanji code points - // - 863 Jinmeiyo (meaning "for personal name") Kanji code points - // - Sourced from the character information database of the Information-technology Promotion Agency, Japan - // - https://mojikiban.ipa.go.jp/mji/ - // - Available under the terms of the Creative Commons Attribution-ShareAlike 2.1 Japan (CC BY-SA 2.1 JP). - // - https://creativecommons.org/licenses/by-sa/2.1/jp/deed.en - // - https://creativecommons.org/licenses/by-sa/2.1/jp/legalcode - // - You can generate this code by the script at: - // - https://github.com/vaiorabbit/everyday_use_kanji - // - References: - // - List of Joyo Kanji - // - (Official list by the Agency for Cultural Affairs) https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kakuki/14/tosin02/index.html - // - (Wikipedia) https://en.wikipedia.org/wiki/List_of_j%C5%8Dy%C5%8D_kanji - // - List of Jinmeiyo Kanji - // - (Official list by the Ministry of Justice) http://www.moj.go.jp/MINJI/minji86.html - // - (Wikipedia) https://en.wikipedia.org/wiki/Jinmeiy%C5%8D_kanji - // - Missing 1 Joyo Kanji: U+20B9F (Kun'yomi: Shikaru, On'yomi: Shitsu,shichi), see https://github.com/ocornut/imgui/pull/3627 for details. - // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. - // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) - static const short accumulative_offsets_from_0x4E00[] = - { - 0,1,2,4,1,1,1,1,2,1,3,3,2,2,1,5,3,5,7,5,6,1,2,1,7,2,6,3,1,8,1,1,4,1,1,18,2,11,2,6,2,1,2,1,5,1,2,1,3,1,2,1,2,3,3,1,1,2,3,1,1,1,12,7,9,1,4,5,1, - 1,2,1,10,1,1,9,2,2,4,5,6,9,3,1,1,1,1,9,3,18,5,2,2,2,2,1,6,3,7,1,1,1,1,2,2,4,2,1,23,2,10,4,3,5,2,4,10,2,4,13,1,6,1,9,3,1,1,6,6,7,6,3,1,2,11,3, - 2,2,3,2,15,2,2,5,4,3,6,4,1,2,5,2,12,16,6,13,9,13,2,1,1,7,16,4,7,1,19,1,5,1,2,2,7,7,8,2,6,5,4,9,18,7,4,5,9,13,11,8,15,2,1,1,1,2,1,2,2,1,2,2,8, - 2,9,3,3,1,1,4,4,1,1,1,4,9,1,4,3,5,5,2,7,5,3,4,8,2,1,13,2,3,3,1,14,1,1,4,5,1,3,6,1,5,2,1,1,3,3,3,3,1,1,2,7,6,6,7,1,4,7,6,1,1,1,1,1,12,3,3,9,5, - 2,6,1,5,6,1,2,3,18,2,4,14,4,1,3,6,1,1,6,3,5,5,3,2,2,2,2,12,3,1,4,2,3,2,3,11,1,7,4,1,2,1,3,17,1,9,1,24,1,1,4,2,2,4,1,2,7,1,1,1,3,1,2,2,4,15,1, - 1,2,1,1,2,1,5,2,5,20,2,5,9,1,10,8,7,6,1,1,1,1,1,1,6,2,1,2,8,1,1,1,1,5,1,1,3,1,1,1,1,3,1,1,12,4,1,3,1,1,1,1,1,10,3,1,7,5,13,1,2,3,4,6,1,1,30, - 2,9,9,1,15,38,11,3,1,8,24,7,1,9,8,10,2,1,9,31,2,13,6,2,9,4,49,5,2,15,2,1,10,2,1,1,1,2,2,6,15,30,35,3,14,18,8,1,16,10,28,12,19,45,38,1,3,2,3, - 13,2,1,7,3,6,5,3,4,3,1,5,7,8,1,5,3,18,5,3,6,1,21,4,24,9,24,40,3,14,3,21,3,2,1,2,4,2,3,1,15,15,6,5,1,1,3,1,5,6,1,9,7,3,3,2,1,4,3,8,21,5,16,4, - 5,2,10,11,11,3,6,3,2,9,3,6,13,1,2,1,1,1,1,11,12,6,6,1,4,2,6,5,2,1,1,3,3,6,13,3,1,1,5,1,2,3,3,14,2,1,2,2,2,5,1,9,5,1,1,6,12,3,12,3,4,13,2,14, - 2,8,1,17,5,1,16,4,2,2,21,8,9,6,23,20,12,25,19,9,38,8,3,21,40,25,33,13,4,3,1,4,1,2,4,1,2,5,26,2,1,1,2,1,3,6,2,1,1,1,1,1,1,2,3,1,1,1,9,2,3,1,1, - 1,3,6,3,2,1,1,6,6,1,8,2,2,2,1,4,1,2,3,2,7,3,2,4,1,2,1,2,2,1,1,1,1,1,3,1,2,5,4,10,9,4,9,1,1,1,1,1,1,5,3,2,1,6,4,9,6,1,10,2,31,17,8,3,7,5,40,1, - 7,7,1,6,5,2,10,7,8,4,15,39,25,6,28,47,18,10,7,1,3,1,1,2,1,1,1,3,3,3,1,1,1,3,4,2,1,4,1,3,6,10,7,8,6,2,2,1,3,3,2,5,8,7,9,12,2,15,1,1,4,1,2,1,1, - 1,3,2,1,3,3,5,6,2,3,2,10,1,4,2,8,1,1,1,11,6,1,21,4,16,3,1,3,1,4,2,3,6,5,1,3,1,1,3,3,4,6,1,1,10,4,2,7,10,4,7,4,2,9,4,3,1,1,1,4,1,8,3,4,1,3,1, - 6,1,4,2,1,4,7,2,1,8,1,4,5,1,1,2,2,4,6,2,7,1,10,1,1,3,4,11,10,8,21,4,6,1,3,5,2,1,2,28,5,5,2,3,13,1,2,3,1,4,2,1,5,20,3,8,11,1,3,3,3,1,8,10,9,2, - 10,9,2,3,1,1,2,4,1,8,3,6,1,7,8,6,11,1,4,29,8,4,3,1,2,7,13,1,4,1,6,2,6,12,12,2,20,3,2,3,6,4,8,9,2,7,34,5,1,18,6,1,1,4,4,5,7,9,1,2,2,4,3,4,1,7, - 2,2,2,6,2,3,25,5,3,6,1,4,6,7,4,2,1,4,2,13,6,4,4,3,1,5,3,4,4,3,2,1,1,4,1,2,1,1,3,1,11,1,6,3,1,7,3,6,2,8,8,6,9,3,4,11,3,2,10,12,2,5,11,1,6,4,5, - 3,1,8,5,4,6,6,3,5,1,1,3,2,1,2,2,6,17,12,1,10,1,6,12,1,6,6,19,9,6,16,1,13,4,4,15,7,17,6,11,9,15,12,6,7,2,1,2,2,15,9,3,21,4,6,49,18,7,3,2,3,1, - 6,8,2,2,6,2,9,1,3,6,4,4,1,2,16,2,5,2,1,6,2,3,5,3,1,2,5,1,2,1,9,3,1,8,6,4,8,11,3,1,1,1,1,3,1,13,8,4,1,3,2,2,1,4,1,11,1,5,2,1,5,2,5,8,6,1,1,7, - 4,3,8,3,2,7,2,1,5,1,5,2,4,7,6,2,8,5,1,11,4,5,3,6,18,1,2,13,3,3,1,21,1,1,4,1,4,1,1,1,8,1,2,2,7,1,2,4,2,2,9,2,1,1,1,4,3,6,3,12,5,1,1,1,5,6,3,2, - 4,8,2,2,4,2,7,1,8,9,5,2,3,2,1,3,2,13,7,14,6,5,1,1,2,1,4,2,23,2,1,1,6,3,1,4,1,15,3,1,7,3,9,14,1,3,1,4,1,1,5,8,1,3,8,3,8,15,11,4,14,4,4,2,5,5, - 1,7,1,6,14,7,7,8,5,15,4,8,6,5,6,2,1,13,1,20,15,11,9,2,5,6,2,11,2,6,2,5,1,5,8,4,13,19,25,4,1,1,11,1,34,2,5,9,14,6,2,2,6,1,1,14,1,3,14,13,1,6, - 12,21,14,14,6,32,17,8,32,9,28,1,2,4,11,8,3,1,14,2,5,15,1,1,1,1,3,6,4,1,3,4,11,3,1,1,11,30,1,5,1,4,1,5,8,1,1,3,2,4,3,17,35,2,6,12,17,3,1,6,2, - 1,1,12,2,7,3,3,2,1,16,2,8,3,6,5,4,7,3,3,8,1,9,8,5,1,2,1,3,2,8,1,2,9,12,1,1,2,3,8,3,24,12,4,3,7,5,8,3,3,3,3,3,3,1,23,10,3,1,2,2,6,3,1,16,1,16, - 22,3,10,4,11,6,9,7,7,3,6,2,2,2,4,10,2,1,1,2,8,7,1,6,4,1,3,3,3,5,10,12,12,2,3,12,8,15,1,1,16,6,6,1,5,9,11,4,11,4,2,6,12,1,17,5,13,1,4,9,5,1,11, - 2,1,8,1,5,7,28,8,3,5,10,2,17,3,38,22,1,2,18,12,10,4,38,18,1,4,44,19,4,1,8,4,1,12,1,4,31,12,1,14,7,75,7,5,10,6,6,13,3,2,11,11,3,2,5,28,15,6,18, - 18,5,6,4,3,16,1,7,18,7,36,3,5,3,1,7,1,9,1,10,7,2,4,2,6,2,9,7,4,3,32,12,3,7,10,2,23,16,3,1,12,3,31,4,11,1,3,8,9,5,1,30,15,6,12,3,2,2,11,19,9, - 14,2,6,2,3,19,13,17,5,3,3,25,3,14,1,1,1,36,1,3,2,19,3,13,36,9,13,31,6,4,16,34,2,5,4,2,3,3,5,1,1,1,4,3,1,17,3,2,3,5,3,1,3,2,3,5,6,3,12,11,1,3, - 1,2,26,7,12,7,2,14,3,3,7,7,11,25,25,28,16,4,36,1,2,1,6,2,1,9,3,27,17,4,3,4,13,4,1,3,2,2,1,10,4,2,4,6,3,8,2,1,18,1,1,24,2,2,4,33,2,3,63,7,1,6, - 40,7,3,4,4,2,4,15,18,1,16,1,1,11,2,41,14,1,3,18,13,3,2,4,16,2,17,7,15,24,7,18,13,44,2,2,3,6,1,1,7,5,1,7,1,4,3,3,5,10,8,2,3,1,8,1,1,27,4,2,1, - 12,1,2,1,10,6,1,6,7,5,2,3,7,11,5,11,3,6,6,2,3,15,4,9,1,1,2,1,2,11,2,8,12,8,5,4,2,3,1,5,2,2,1,14,1,12,11,4,1,11,17,17,4,3,2,5,5,7,3,1,5,9,9,8, - 2,5,6,6,13,13,2,1,2,6,1,2,2,49,4,9,1,2,10,16,7,8,4,3,2,23,4,58,3,29,1,14,19,19,11,11,2,7,5,1,3,4,6,2,18,5,12,12,17,17,3,3,2,4,1,6,2,3,4,3,1, - 1,1,1,5,1,1,9,1,3,1,3,6,1,8,1,1,2,6,4,14,3,1,4,11,4,1,3,32,1,2,4,13,4,1,2,4,2,1,3,1,11,1,4,2,1,4,4,6,3,5,1,6,5,7,6,3,23,3,5,3,5,3,3,13,3,9,10, - 1,12,10,2,3,18,13,7,160,52,4,2,2,3,2,14,5,4,12,4,6,4,1,20,4,11,6,2,12,27,1,4,1,2,2,7,4,5,2,28,3,7,25,8,3,19,3,6,10,2,2,1,10,2,5,4,1,3,4,1,5, - 3,2,6,9,3,6,2,16,3,3,16,4,5,5,3,2,1,2,16,15,8,2,6,21,2,4,1,22,5,8,1,1,21,11,2,1,11,11,19,13,12,4,2,3,2,3,6,1,8,11,1,4,2,9,5,2,1,11,2,9,1,1,2, - 14,31,9,3,4,21,14,4,8,1,7,2,2,2,5,1,4,20,3,3,4,10,1,11,9,8,2,1,4,5,14,12,14,2,17,9,6,31,4,14,1,20,13,26,5,2,7,3,6,13,2,4,2,19,6,2,2,18,9,3,5, - 12,12,14,4,6,2,3,6,9,5,22,4,5,25,6,4,8,5,2,6,27,2,35,2,16,3,7,8,8,6,6,5,9,17,2,20,6,19,2,13,3,1,1,1,4,17,12,2,14,7,1,4,18,12,38,33,2,10,1,1, - 2,13,14,17,11,50,6,33,20,26,74,16,23,45,50,13,38,33,6,6,7,4,4,2,1,3,2,5,8,7,8,9,3,11,21,9,13,1,3,10,6,7,1,2,2,18,5,5,1,9,9,2,68,9,19,13,2,5, - 1,4,4,7,4,13,3,9,10,21,17,3,26,2,1,5,2,4,5,4,1,7,4,7,3,4,2,1,6,1,1,20,4,1,9,2,2,1,3,3,2,3,2,1,1,1,20,2,3,1,6,2,3,6,2,4,8,1,3,2,10,3,5,3,4,4, - 3,4,16,1,6,1,10,2,4,2,1,1,2,10,11,2,2,3,1,24,31,4,10,10,2,5,12,16,164,15,4,16,7,9,15,19,17,1,2,1,1,5,1,1,1,1,1,3,1,4,3,1,3,1,3,1,2,1,1,3,3,7, - 2,8,1,2,2,2,1,3,4,3,7,8,12,92,2,10,3,1,3,14,5,25,16,42,4,7,7,4,2,21,5,27,26,27,21,25,30,31,2,1,5,13,3,22,5,6,6,11,9,12,1,5,9,7,5,5,22,60,3,5, - 13,1,1,8,1,1,3,3,2,1,9,3,3,18,4,1,2,3,7,6,3,1,2,3,9,1,3,1,3,2,1,3,1,1,1,2,1,11,3,1,6,9,1,3,2,3,1,2,1,5,1,1,4,3,4,1,2,2,4,4,1,7,2,1,2,2,3,5,13, - 18,3,4,14,9,9,4,16,3,7,5,8,2,6,48,28,3,1,1,4,2,14,8,2,9,2,1,15,2,4,3,2,10,16,12,8,7,1,1,3,1,1,1,2,7,4,1,6,4,38,39,16,23,7,15,15,3,2,12,7,21, - 37,27,6,5,4,8,2,10,8,8,6,5,1,2,1,3,24,1,16,17,9,23,10,17,6,1,51,55,44,13,294,9,3,6,2,4,2,2,15,1,1,1,13,21,17,68,14,8,9,4,1,4,9,3,11,7,1,1,1, - 5,6,3,2,1,1,1,2,3,8,1,2,2,4,1,5,5,2,1,4,3,7,13,4,1,4,1,3,1,1,1,5,5,10,1,6,1,5,2,1,5,2,4,1,4,5,7,3,18,2,9,11,32,4,3,3,2,4,7,11,16,9,11,8,13,38, - 32,8,4,2,1,1,2,1,2,4,4,1,1,1,4,1,21,3,11,1,16,1,1,6,1,3,2,4,9,8,57,7,44,1,3,3,13,3,10,1,1,7,5,2,7,21,47,63,3,15,4,7,1,16,1,1,2,8,2,3,42,15,4, - 1,29,7,22,10,3,78,16,12,20,18,4,67,11,5,1,3,15,6,21,31,32,27,18,13,71,35,5,142,4,10,1,2,50,19,33,16,35,37,16,19,27,7,1,133,19,1,4,8,7,20,1,4, - 4,1,10,3,1,6,1,2,51,5,40,15,24,43,22928,11,1,13,154,70,3,1,1,7,4,10,1,2,1,1,2,1,2,1,2,2,1,1,2,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1, - 3,2,1,1,1,1,2,1,1, - }; - static ImWchar base_ranges[] = // not zero-terminated - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana - 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF, // Half-width characters - 0xFFFD, 0xFFFD // Invalid - }; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; - if (!full_ranges[0]) - { - memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); - } - return &full_ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesCyrillic() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x0400, 0x052F, // Cyrillic + Cyrillic Supplement - 0x2DE0, 0x2DFF, // Cyrillic Extended-A - 0xA640, 0xA69F, // Cyrillic Extended-B - 0, - }; - return &ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesThai() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin - 0x2010, 0x205E, // Punctuations - 0x0E00, 0x0E7F, // Thai - 0, - }; - return &ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesVietnamese() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin - 0x0102, 0x0103, - 0x0110, 0x0111, - 0x0128, 0x0129, - 0x0168, 0x0169, - 0x01A0, 0x01A1, - 0x01AF, 0x01B0, - 0x1EA0, 0x1EF9, - 0, - }; - return &ranges[0]; -} - -//----------------------------------------------------------------------------- -// [SECTION] ImFontGlyphRangesBuilder -//----------------------------------------------------------------------------- - -void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) -{ - while (text_end ? (text < text_end) : *text) - { - unsigned int c = 0; - int c_len = ImTextCharFromUtf8(&c, text, text_end); - text += c_len; - if (c_len == 0) - break; - AddChar((ImWchar)c); - } -} - -void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) -{ - for (; ranges[0]; ranges += 2) - for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560 - AddChar((ImWchar)c); -} - -void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) -{ - const int max_codepoint = IM_UNICODE_CODEPOINT_MAX; - for (int n = 0; n <= max_codepoint; n++) - if (GetBit(n)) - { - out_ranges->push_back((ImWchar)n); - while (n < max_codepoint && GetBit(n + 1)) - n++; - out_ranges->push_back((ImWchar)n); - } - out_ranges->push_back(0); -} - -//----------------------------------------------------------------------------- -// [SECTION] ImFont -//----------------------------------------------------------------------------- - -ImFont::ImFont() -{ - FontSize = 0.0f; - FallbackAdvanceX = 0.0f; - FallbackChar = (ImWchar)-1; - EllipsisChar = (ImWchar)-1; - DotChar = (ImWchar)-1; - FallbackGlyph = NULL; - ContainerAtlas = NULL; - ConfigData = NULL; - ConfigDataCount = 0; - DirtyLookupTables = false; - Scale = 1.0f; - Ascent = Descent = 0.0f; - MetricsTotalSurface = 0; - memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap)); -} - -ImFont::~ImFont() -{ - ClearOutputData(); -} - -void ImFont::ClearOutputData() -{ - FontSize = 0.0f; - FallbackAdvanceX = 0.0f; - Glyphs.clear(); - IndexAdvanceX.clear(); - IndexLookup.clear(); - FallbackGlyph = NULL; - ContainerAtlas = NULL; - DirtyLookupTables = true; - Ascent = Descent = 0.0f; - MetricsTotalSurface = 0; -} - -static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count) -{ - for (int n = 0; n < candidate_chars_count; n++) - if (font->FindGlyphNoFallback(candidate_chars[n]) != NULL) - return candidate_chars[n]; - return (ImWchar)-1; -} - -void ImFont::BuildLookupTable() -{ - int max_codepoint = 0; - for (int i = 0; i != Glyphs.Size; i++) - max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); - - // Build lookup table - IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved - IndexAdvanceX.clear(); - IndexLookup.clear(); - DirtyLookupTables = false; - memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap)); - GrowIndex(max_codepoint + 1); - for (int i = 0; i < Glyphs.Size; i++) - { - int codepoint = (int)Glyphs[i].Codepoint; - IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX; - IndexLookup[codepoint] = (ImWchar)i; - - // Mark 4K page as used - const int page_n = codepoint / 4096; - Used4kPagesMap[page_n >> 3] |= 1 << (page_n & 7); - } - - // Create a glyph to handle TAB - // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?) - if (FindGlyph((ImWchar)' ')) - { - if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times (FIXME: Flaky) - Glyphs.resize(Glyphs.Size + 1); - ImFontGlyph& tab_glyph = Glyphs.back(); - tab_glyph = *FindGlyph((ImWchar)' '); - tab_glyph.Codepoint = '\t'; - tab_glyph.AdvanceX *= IM_TABSIZE; - IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX; - IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size - 1); - } - - // Mark special glyphs as not visible (note that AddGlyph already mark as non-visible glyphs with zero-size polygons) - SetGlyphVisible((ImWchar)' ', false); - SetGlyphVisible((ImWchar)'\t', false); - - // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). - // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. - // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots. - const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 }; - const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E }; - if (EllipsisChar == (ImWchar)-1) - EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars)); - if (DotChar == (ImWchar)-1) - DotChar = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars)); - - // Setup fallback character - const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' }; - FallbackGlyph = FindGlyphNoFallback(FallbackChar); - if (FallbackGlyph == NULL) - { - FallbackChar = FindFirstExistingGlyph(this, fallback_chars, IM_ARRAYSIZE(fallback_chars)); - FallbackGlyph = FindGlyphNoFallback(FallbackChar); - if (FallbackGlyph == NULL) - { - FallbackGlyph = &Glyphs.back(); - FallbackChar = (ImWchar)FallbackGlyph->Codepoint; - } - } - - FallbackAdvanceX = FallbackGlyph->AdvanceX; - for (int i = 0; i < max_codepoint + 1; i++) - if (IndexAdvanceX[i] < 0.0f) - IndexAdvanceX[i] = FallbackAdvanceX; -} - -// API is designed this way to avoid exposing the 4K page size -// e.g. use with IsGlyphRangeUnused(0, 255) -bool ImFont::IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last) -{ - unsigned int page_begin = (c_begin / 4096); - unsigned int page_last = (c_last / 4096); - for (unsigned int page_n = page_begin; page_n <= page_last; page_n++) - if ((page_n >> 3) < sizeof(Used4kPagesMap)) - if (Used4kPagesMap[page_n >> 3] & (1 << (page_n & 7))) - return false; - return true; -} - -void ImFont::SetGlyphVisible(ImWchar c, bool visible) -{ - if (ImFontGlyph* glyph = (ImFontGlyph*)(void*)FindGlyph((ImWchar)c)) - glyph->Visible = visible ? 1 : 0; -} - -void ImFont::GrowIndex(int new_size) -{ - IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size); - if (new_size <= IndexLookup.Size) - return; - IndexAdvanceX.resize(new_size, -1.0f); - IndexLookup.resize(new_size, (ImWchar)-1); -} - -// x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero. -// Not to be mistaken with texture coordinates, which are held by u0/v0/u1/v1 in normalized format (0.0..1.0 on each texture axis). -// 'cfg' is not necessarily == 'this->ConfigData' because multiple source fonts+configs can be used to build one target font. -void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x) -{ - if (cfg != NULL) - { - // Clamp & recenter if needed - const float advance_x_original = advance_x; - advance_x = ImClamp(advance_x, cfg->GlyphMinAdvanceX, cfg->GlyphMaxAdvanceX); - if (advance_x != advance_x_original) - { - float char_off_x = cfg->PixelSnapH ? ImFloor((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f; - x0 += char_off_x; - x1 += char_off_x; - } - - // Snap to pixel - if (cfg->PixelSnapH) - advance_x = IM_ROUND(advance_x); - - // Bake spacing - advance_x += cfg->GlyphExtraSpacing.x; - } - - Glyphs.resize(Glyphs.Size + 1); - ImFontGlyph& glyph = Glyphs.back(); - glyph.Codepoint = (unsigned int)codepoint; - glyph.Visible = (x0 != x1) && (y0 != y1); - glyph.Colored = false; - glyph.X0 = x0; - glyph.Y0 = y0; - glyph.X1 = x1; - glyph.Y1 = y1; - glyph.U0 = u0; - glyph.V0 = v0; - glyph.U1 = u1; - glyph.V1 = v1; - glyph.AdvanceX = advance_x; - - // Compute rough surface usage metrics (+1 to account for average padding, +0.99 to round) - // We use (U1-U0)*TexWidth instead of X1-X0 to account for oversampling. - float pad = ContainerAtlas->TexGlyphPadding + 0.99f; - DirtyLookupTables = true; - MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * ContainerAtlas->TexWidth + pad) * (int)((glyph.V1 - glyph.V0) * ContainerAtlas->TexHeight + pad); -} - -void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) -{ - IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. - unsigned int index_size = (unsigned int)IndexLookup.Size; - - if (dst < index_size && IndexLookup.Data[dst] == (ImWchar)-1 && !overwrite_dst) // 'dst' already exists - return; - if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op - return; - - GrowIndex(dst + 1); - IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (ImWchar)-1; - IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f; -} - -const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const -{ - if (c >= (size_t)IndexLookup.Size) - return FallbackGlyph; - const ImWchar i = IndexLookup.Data[c]; - if (i == (ImWchar)-1) - return FallbackGlyph; - return &Glyphs.Data[i]; -} - -const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const -{ - if (c >= (size_t)IndexLookup.Size) - return NULL; - const ImWchar i = IndexLookup.Data[c]; - if (i == (ImWchar)-1) - return NULL; - return &Glyphs.Data[i]; -} - -const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const -{ - // Simple word-wrapping for English, not full-featured. Please submit failing cases! - // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) - - // For references, possible wrap point marked with ^ - // "aaa bbb, ccc,ddd. eee fff. ggg!" - // ^ ^ ^ ^ ^__ ^ ^ - - // List of hardcoded separators: .,;!?'" - - // Skip extra blanks after a line returns (that includes not counting them in width computation) - // e.g. "Hello world" --> "Hello" "World" - - // Cut words that cannot possibly fit within one line. - // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" - - float line_width = 0.0f; - float word_width = 0.0f; - float blank_width = 0.0f; - wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters - - const char* word_end = text; - const char* prev_word_end = NULL; - bool inside_word = true; - - const char* s = text; - while (s < text_end) - { - unsigned int c = (unsigned int)*s; - const char* next_s; - if (c < 0x80) - next_s = s + 1; - else - next_s = s + ImTextCharFromUtf8(&c, s, text_end); - if (c == 0) - break; - - if (c < 32) - { - if (c == '\n') - { - line_width = word_width = blank_width = 0.0f; - inside_word = true; - s = next_s; - continue; - } - if (c == '\r') - { - s = next_s; - continue; - } - } - - const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX); - if (ImCharIsBlankW(c)) - { - if (inside_word) - { - line_width += blank_width; - blank_width = 0.0f; - word_end = s; - } - blank_width += char_width; - inside_word = false; - } - else - { - word_width += char_width; - if (inside_word) - { - word_end = next_s; - } - else - { - prev_word_end = word_end; - line_width += word_width + blank_width; - word_width = blank_width = 0.0f; - } - - // Allow wrapping after punctuation. - inside_word = (c != '.' && c != ',' && c != ';' && c != '!' && c != '?' && c != '\"'); - } - - // We ignore blank width at the end of the line (they can be skipped) - if (line_width + word_width > wrap_width) - { - // Words that cannot possibly fit within an entire line will be cut anywhere. - if (word_width < wrap_width) - s = prev_word_end ? prev_word_end : word_end; - break; - } - - s = next_s; - } - - return s; -} - -ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) const -{ - if (!text_end) - text_end = text_begin + strlen(text_begin); // FIXME-OPT: Need to avoid this. - - const float line_height = size; - const float scale = size / FontSize; - - ImVec2 text_size = ImVec2(0, 0); - float line_width = 0.0f; - - const bool word_wrap_enabled = (wrap_width > 0.0f); - const char* word_wrap_eol = NULL; - - const char* s = text_begin; - while (s < text_end) - { - if (word_wrap_enabled) - { - // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. - if (!word_wrap_eol) - { - word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width); - if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. - word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below - } - - if (s >= word_wrap_eol) - { - if (text_size.x < line_width) - text_size.x = line_width; - text_size.y += line_height; - line_width = 0.0f; - word_wrap_eol = NULL; - - // Wrapping skips upcoming blanks - while (s < text_end) - { - const char c = *s; - if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } - } - continue; - } - } - - // Decode and advance source - const char* prev_s = s; - unsigned int c = (unsigned int)*s; - if (c < 0x80) - { - s += 1; - } - else - { - s += ImTextCharFromUtf8(&c, s, text_end); - if (c == 0) // Malformed UTF-8? - break; - } - - if (c < 32) - { - if (c == '\n') - { - text_size.x = ImMax(text_size.x, line_width); - text_size.y += line_height; - line_width = 0.0f; - continue; - } - if (c == '\r') - continue; - } - - const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX) * scale; - if (line_width + char_width >= max_width) - { - s = prev_s; - break; - } - - line_width += char_width; - } - - if (text_size.x < line_width) - text_size.x = line_width; - - if (line_width > 0 || text_size.y == 0.0f) - text_size.y += line_height; - - if (remaining) - *remaining = s; - - return text_size; -} - -// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. -void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const -{ - const ImFontGlyph* glyph = FindGlyph(c); - if (!glyph || !glyph->Visible) - return; - if (glyph->Colored) - col |= ~IM_COL32_A_MASK; - float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; - float x = IM_FLOOR(pos.x); - float y = IM_FLOOR(pos.y); - draw_list->PrimReserve(6, 4); - draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); -} - -// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. -void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const -{ - if (!text_end) - text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. - - // Align to be pixel perfect - float x = IM_FLOOR(pos.x); - float y = IM_FLOOR(pos.y); - if (y > clip_rect.w) - return; - - const float start_x = x; - const float scale = size / FontSize; - const float line_height = FontSize * scale; - const bool word_wrap_enabled = (wrap_width > 0.0f); - const char* word_wrap_eol = NULL; - - // Fast-forward to first visible line - const char* s = text_begin; - if (y + line_height < clip_rect.y && !word_wrap_enabled) - while (y + line_height < clip_rect.y && s < text_end) - { - s = (const char*)memchr(s, '\n', text_end - s); - s = s ? s + 1 : text_end; - y += line_height; - } - - // For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve() - // Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm) - if (text_end - s > 10000 && !word_wrap_enabled) - { - const char* s_end = s; - float y_end = y; - while (y_end < clip_rect.w && s_end < text_end) - { - s_end = (const char*)memchr(s_end, '\n', text_end - s_end); - s_end = s_end ? s_end + 1 : text_end; - y_end += line_height; - } - text_end = s_end; - } - if (s == text_end) - return; - - // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) - const int vtx_count_max = (int)(text_end - s) * 4; - const int idx_count_max = (int)(text_end - s) * 6; - const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max; - draw_list->PrimReserve(idx_count_max, vtx_count_max); - - ImDrawVert* vtx_write = draw_list->_VtxWritePtr; - ImDrawIdx* idx_write = draw_list->_IdxWritePtr; - unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx; - - const ImU32 col_untinted = col | ~IM_COL32_A_MASK; - - while (s < text_end) - { - if (word_wrap_enabled) - { - // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. - if (!word_wrap_eol) - { - word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x)); - if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. - word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below - } - - if (s >= word_wrap_eol) - { - x = start_x; - y += line_height; - word_wrap_eol = NULL; - - // Wrapping skips upcoming blanks - while (s < text_end) - { - const char c = *s; - if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } - } - continue; - } - } - - // Decode and advance source - unsigned int c = (unsigned int)*s; - if (c < 0x80) - { - s += 1; - } - else - { - s += ImTextCharFromUtf8(&c, s, text_end); - if (c == 0) // Malformed UTF-8? - break; - } - - if (c < 32) - { - if (c == '\n') - { - x = start_x; - y += line_height; - if (y > clip_rect.w) - break; // break out of main loop - continue; - } - if (c == '\r') - continue; - } - - const ImFontGlyph* glyph = FindGlyph((ImWchar)c); - if (glyph == NULL) - continue; - - float char_width = glyph->AdvanceX * scale; - if (glyph->Visible) - { - // We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w - float x1 = x + glyph->X0 * scale; - float x2 = x + glyph->X1 * scale; - float y1 = y + glyph->Y0 * scale; - float y2 = y + glyph->Y1 * scale; - if (x1 <= clip_rect.z && x2 >= clip_rect.x) - { - // Render a character - float u1 = glyph->U0; - float v1 = glyph->V0; - float u2 = glyph->U1; - float v2 = glyph->V1; - - // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads. - if (cpu_fine_clip) - { - if (x1 < clip_rect.x) - { - u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1); - x1 = clip_rect.x; - } - if (y1 < clip_rect.y) - { - v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1); - y1 = clip_rect.y; - } - if (x2 > clip_rect.z) - { - u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1); - x2 = clip_rect.z; - } - if (y2 > clip_rect.w) - { - v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1); - y2 = clip_rect.w; - } - if (y1 >= y2) - { - x += char_width; - continue; - } - } - - // Support for untinted glyphs - ImU32 glyph_col = glyph->Colored ? col_untinted : col; - - // We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here: - { - idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2); - idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3); - vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1; - vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1; - vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2; - vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2; - vtx_write += 4; - vtx_current_idx += 4; - idx_write += 6; - } - } - } - x += char_width; - } - - // Give back unused vertices (clipped ones, blanks) ~ this is essentially a PrimUnreserve() action. - draw_list->VtxBuffer.Size = (int)(vtx_write - draw_list->VtxBuffer.Data); // Same as calling shrink() - draw_list->IdxBuffer.Size = (int)(idx_write - draw_list->IdxBuffer.Data); - draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size); - draw_list->_VtxWritePtr = vtx_write; - draw_list->_IdxWritePtr = idx_write; - draw_list->_VtxCurrentIdx = vtx_current_idx; -} - -//----------------------------------------------------------------------------- -// [SECTION] ImGui Internal Render Helpers -//----------------------------------------------------------------------------- -// Vaguely redesigned to stop accessing ImGui global state: -// - RenderArrow() -// - RenderBullet() -// - RenderCheckMark() -// - RenderArrowPointingAt() -// - RenderRectFilledRangeH() -// - RenderRectFilledWithHole() -//----------------------------------------------------------------------------- -// Function in need of a redesign (legacy mess) -// - RenderColorRectWithAlphaCheckerboard() -//----------------------------------------------------------------------------- - -// Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state -void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale) -{ - const float h = draw_list->_Data->FontSize * 1.00f; - float r = h * 0.40f * scale; - ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale); - - ImVec2 a, b, c; - switch (dir) - { - case ImGuiDir_Up: - case ImGuiDir_Down: - if (dir == ImGuiDir_Up) r = -r; - a = ImVec2(+0.000f, +0.750f) * r; - b = ImVec2(-0.866f, -0.750f) * r; - c = ImVec2(+0.866f, -0.750f) * r; - break; - case ImGuiDir_Left: - case ImGuiDir_Right: - if (dir == ImGuiDir_Left) r = -r; - a = ImVec2(+0.750f, +0.000f) * r; - b = ImVec2(-0.750f, +0.866f) * r; - c = ImVec2(-0.750f, -0.866f) * r; - break; - case ImGuiDir_None: - case ImGuiDir_COUNT: - IM_ASSERT(0); - break; - } - draw_list->AddTriangleFilled(center + a, center + b, center + c, col); -} - -void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col) -{ - draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8); -} - -void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz) -{ - float thickness = ImMax(sz / 5.0f, 1.0f); - sz -= thickness * 0.5f; - pos += ImVec2(thickness * 0.25f, thickness * 0.25f); - - float third = sz / 3.0f; - float bx = pos.x + third; - float by = pos.y + sz - third * 0.5f; - draw_list->PathLineTo(ImVec2(bx - third, by - third)); - draw_list->PathLineTo(ImVec2(bx, by)); - draw_list->PathLineTo(ImVec2(bx + third * 2.0f, by - third * 2.0f)); - draw_list->PathStroke(col, 0, thickness); -} - -// Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side. -void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col) -{ - switch (direction) - { - case ImGuiDir_Left: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return; - case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return; - case ImGuiDir_Up: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return; - case ImGuiDir_Down: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return; - case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings - } -} - -static inline float ImAcos01(float x) -{ - if (x <= 0.0f) return IM_PI * 0.5f; - if (x >= 1.0f) return 0.0f; - return ImAcos(x); - //return (-0.69813170079773212f * x * x - 0.87266462599716477f) * x + 1.5707963267948966f; // Cheap approximation, may be enough for what we do. -} - -// FIXME: Cleanup and move code to ImDrawList. -void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding) -{ - if (x_end_norm == x_start_norm) - return; - if (x_start_norm > x_end_norm) - ImSwap(x_start_norm, x_end_norm); - - ImVec2 p0 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_start_norm), rect.Min.y); - ImVec2 p1 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_end_norm), rect.Max.y); - if (rounding == 0.0f) - { - draw_list->AddRectFilled(p0, p1, col, 0.0f); - return; - } - - rounding = ImClamp(ImMin((rect.Max.x - rect.Min.x) * 0.5f, (rect.Max.y - rect.Min.y) * 0.5f) - 1.0f, 0.0f, rounding); - const float inv_rounding = 1.0f / rounding; - const float arc0_b = ImAcos01(1.0f - (p0.x - rect.Min.x) * inv_rounding); - const float arc0_e = ImAcos01(1.0f - (p1.x - rect.Min.x) * inv_rounding); - const float half_pi = IM_PI * 0.5f; // We will == compare to this because we know this is the exact value ImAcos01 can return. - const float x0 = ImMax(p0.x, rect.Min.x + rounding); - if (arc0_b == arc0_e) - { - draw_list->PathLineTo(ImVec2(x0, p1.y)); - draw_list->PathLineTo(ImVec2(x0, p0.y)); - } - else if (arc0_b == 0.0f && arc0_e == half_pi) - { - draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6); // BL - draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9); // TR - } - else - { - draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b, 3); // BL - draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR - } - if (p1.x > rect.Min.x + rounding) - { - const float arc1_b = ImAcos01(1.0f - (rect.Max.x - p1.x) * inv_rounding); - const float arc1_e = ImAcos01(1.0f - (rect.Max.x - p0.x) * inv_rounding); - const float x1 = ImMin(p1.x, rect.Max.x - rounding); - if (arc1_b == arc1_e) - { - draw_list->PathLineTo(ImVec2(x1, p0.y)); - draw_list->PathLineTo(ImVec2(x1, p1.y)); - } - else if (arc1_b == 0.0f && arc1_e == half_pi) - { - draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12); // TR - draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3); // BR - } - else - { - draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b, 3); // TR - draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e, 3); // BR - } - } - draw_list->PathFillConvex(col); -} - -void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding) -{ - const bool fill_L = (inner.Min.x > outer.Min.x); - const bool fill_R = (inner.Max.x < outer.Max.x); - const bool fill_U = (inner.Min.y > outer.Min.y); - const bool fill_D = (inner.Max.y < outer.Max.y); - if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft)); - if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight)); - if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight)); - if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight)); - if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft); - if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight); - if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft); - if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight); -} - -// Helper for ColorPicker4() -// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. -// Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether. -// FIXME: uses ImGui::GetColorU32 -void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, ImDrawFlags flags) -{ - if ((flags & ImDrawFlags_RoundCornersMask_) == 0) - flags = ImDrawFlags_RoundCornersDefault_; - if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) - { - ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col)); - ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col)); - draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, flags); - - int yi = 0; - for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++) - { - float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); - if (y2 <= y1) - continue; - for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) - { - float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); - if (x2 <= x1) - continue; - ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone; - if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersTopLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersTopRight; } - if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersBottomLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersBottomRight; } - - // Combine flags - cell_flags = (flags == ImDrawFlags_RoundCornersNone || cell_flags == ImDrawFlags_RoundCornersNone) ? ImDrawFlags_RoundCornersNone : (cell_flags & flags); - draw_list->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg2, rounding, cell_flags); - } - } - } - else - { - draw_list->AddRectFilled(p_min, p_max, col, rounding, flags); - } -} - -//----------------------------------------------------------------------------- -// [SECTION] Decompression code -//----------------------------------------------------------------------------- -// Compressed with stb_compress() then converted to a C array and encoded as base85. -// Use the program in misc/fonts/binary_to_compressed_c.cpp to create the array from a TTF file. -// The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size. -// Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h -//----------------------------------------------------------------------------- - -static unsigned int stb_decompress_length(const unsigned char *input) -{ - return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; -} - -static unsigned char *stb__barrier_out_e, *stb__barrier_out_b; -static const unsigned char *stb__barrier_in_b; -static unsigned char *stb__dout; -static void stb__match(const unsigned char *data, unsigned int length) -{ - // INVERSE of memmove... write each byte before copying the next... - IM_ASSERT(stb__dout + length <= stb__barrier_out_e); - if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; } - if (data < stb__barrier_out_b) { stb__dout = stb__barrier_out_e+1; return; } - while (length--) *stb__dout++ = *data++; -} - -static void stb__lit(const unsigned char *data, unsigned int length) -{ - IM_ASSERT(stb__dout + length <= stb__barrier_out_e); - if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; } - if (data < stb__barrier_in_b) { stb__dout = stb__barrier_out_e+1; return; } - memcpy(stb__dout, data, length); - stb__dout += length; -} - -#define stb__in2(x) ((i[x] << 8) + i[(x)+1]) -#define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) -#define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) - -static const unsigned char *stb_decompress_token(const unsigned char *i) -{ - if (*i >= 0x20) { // use fewer if's for cases that expand small - if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; - else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; - else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); - } else { // more ifs for cases that expand large, since overhead is amortized - if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; - else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; - else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1); - else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1); - else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; - else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; - } - return i; -} - -static unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) -{ - const unsigned long ADLER_MOD = 65521; - unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; - unsigned long blocklen = buflen % 5552; - - unsigned long i; - while (buflen) { - for (i=0; i + 7 < blocklen; i += 8) { - s1 += buffer[0], s2 += s1; - s1 += buffer[1], s2 += s1; - s1 += buffer[2], s2 += s1; - s1 += buffer[3], s2 += s1; - s1 += buffer[4], s2 += s1; - s1 += buffer[5], s2 += s1; - s1 += buffer[6], s2 += s1; - s1 += buffer[7], s2 += s1; - - buffer += 8; - } - - for (; i < blocklen; ++i) - s1 += *buffer++, s2 += s1; - - s1 %= ADLER_MOD, s2 %= ADLER_MOD; - buflen -= blocklen; - blocklen = 5552; - } - return (unsigned int)(s2 << 16) + (unsigned int)s1; -} - -static unsigned int stb_decompress(unsigned char *output, const unsigned char *i, unsigned int /*length*/) -{ - if (stb__in4(0) != 0x57bC0000) return 0; - if (stb__in4(4) != 0) return 0; // error! stream is > 4GB - const unsigned int olen = stb_decompress_length(i); - stb__barrier_in_b = i; - stb__barrier_out_e = output + olen; - stb__barrier_out_b = output; - i += 16; - - stb__dout = output; - for (;;) { - const unsigned char *old_i = i; - i = stb_decompress_token(i); - if (i == old_i) { - if (*i == 0x05 && i[1] == 0xfa) { - IM_ASSERT(stb__dout == output + olen); - if (stb__dout != output + olen) return 0; - if (stb_adler32(1, output, olen) != (unsigned int) stb__in4(2)) - return 0; - return olen; - } else { - IM_ASSERT(0); /* NOTREACHED */ - return 0; - } - } - IM_ASSERT(stb__dout <= output + olen); - if (stb__dout > output + olen) - return 0; - } -} - -//----------------------------------------------------------------------------- -// [SECTION] Default font data (ProggyClean.ttf) -//----------------------------------------------------------------------------- -// ProggyClean.ttf -// Copyright (c) 2004, 2005 Tristan Grimmer -// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) -// Download and more information at http://upperbounds.net -//----------------------------------------------------------------------------- -// File: 'ProggyClean.ttf' (41208 bytes) -// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding). -// The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size. -//----------------------------------------------------------------------------- -static const char proggy_clean_ttf_compressed_data_base85[11980 + 1] = - "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" - "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N" - "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc." - "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G" - "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)" - "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#" - "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM" - "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu" - "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/" - "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO" - "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%" - "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]" - "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et" - "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:" - "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M" - "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(" - "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs" - "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q" - "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-" - "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i" - "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7" - ".m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@" - "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" - "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" - "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" - "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" - "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" - "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" - "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" - "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" - "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" - "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" - "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" - "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" - "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" - "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" - "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" - ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" - "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" - "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" - "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" - "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" - "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" - "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" - ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" - "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" - "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" - "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" - "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" - "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; - -static const char* GetDefaultCompressedFontDataTTFBase85() -{ - return proggy_clean_ttf_compressed_data_base85; -} - -#endif // #ifndef IMGUI_DISABLE diff --git a/libs/zgui/libs/imgui/imgui_impl_glfw.cpp b/libs/zgui/libs/imgui/imgui_impl_glfw.cpp deleted file mode 100644 index 43992e917..000000000 --- a/libs/zgui/libs/imgui/imgui_impl_glfw.cpp +++ /dev/null @@ -1,699 +0,0 @@ -// dear imgui: Platform Backend for GLFW -// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) -// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (Requires: GLFW 3.1+) - -// Implemented features: -// [X] Platform: Clipboard support. -// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] -// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX. -// 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11. -// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend. -// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. -// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. -// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). -// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates. -// 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. -// 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. -// 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). -// 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). -// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). -// 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors. -// 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor). -// 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown. -// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. -// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). -// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. -// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. -// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. -// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. -// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. -// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. -// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. -// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. -// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). -// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. - -#include "imgui.h" - -// Clang warnings with -Weverything -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast -#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" -#endif -#endif - -// GLFW -#include -#ifdef _WIN32 -#undef APIENTRY -#define GLFW_EXPOSE_NATIVE_WIN32 -#include // for glfwGetWin32Window -#endif -#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? -#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR -#else -#define GLFW_HAS_NEW_CURSORS (0) -#endif -#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetGamepadState() new api -#define GLFW_HAS_GET_KEY_NAME (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwGetKeyName() - -#include - -extern "C" { - -bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); -bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); -bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); -void ImGui_ImplGlfw_Shutdown(); -void ImGui_ImplGlfw_NewFrame(); - -// GLFW callbacks (installer) -// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any. -// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks. -void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window); -void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window); - -// GLFW callbacks (individual callbacks to call if you didn't install callbacks) -void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84 -void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84 -void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87 -void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); -void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); -void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); -void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); -void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); - -} // extern "C" - -// GLFW data -enum GlfwClientApi -{ - GlfwClientApi_Unknown, - GlfwClientApi_OpenGL, - GlfwClientApi_Vulkan -}; - -struct ImGui_ImplGlfw_Data -{ - GLFWwindow* Window; - GlfwClientApi ClientApi; - double Time; - GLFWwindow* MouseWindow; - GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; - ImVec2 LastValidMousePos; - bool InstalledCallbacks; - - ImVec2 DpiScale; // mziulek: We scale mouse coords so that we have pixel coords on all OSes. - // This also lets us set `FramebufferScale` to 1.0 and always work with pixel coords - // even on macOS Retina displays. - - // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. - GLFWwindowfocusfun PrevUserCallbackWindowFocus; - GLFWcursorposfun PrevUserCallbackCursorPos; - GLFWcursorenterfun PrevUserCallbackCursorEnter; - GLFWmousebuttonfun PrevUserCallbackMousebutton; - GLFWscrollfun PrevUserCallbackScroll; - GLFWkeyfun PrevUserCallbackKey; - GLFWcharfun PrevUserCallbackChar; - GLFWmonitorfun PrevUserCallbackMonitor; - - ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); } -}; - -// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts -// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. -// FIXME: multi-context support is not well tested and probably dysfunctional in this backend. -// - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks -// (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks. -// - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it. -// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. -static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() -{ - return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : NULL; -} - -// Functions -static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) -{ - return glfwGetClipboardString((GLFWwindow*)user_data); -} - -static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) -{ - glfwSetClipboardString((GLFWwindow*)user_data, text); -} - -static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) -{ - switch (key) - { - case GLFW_KEY_TAB: return ImGuiKey_Tab; - case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; - case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow; - case GLFW_KEY_UP: return ImGuiKey_UpArrow; - case GLFW_KEY_DOWN: return ImGuiKey_DownArrow; - case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp; - case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown; - case GLFW_KEY_HOME: return ImGuiKey_Home; - case GLFW_KEY_END: return ImGuiKey_End; - case GLFW_KEY_INSERT: return ImGuiKey_Insert; - case GLFW_KEY_DELETE: return ImGuiKey_Delete; - case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace; - case GLFW_KEY_SPACE: return ImGuiKey_Space; - case GLFW_KEY_ENTER: return ImGuiKey_Enter; - case GLFW_KEY_ESCAPE: return ImGuiKey_Escape; - case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe; - case GLFW_KEY_COMMA: return ImGuiKey_Comma; - case GLFW_KEY_MINUS: return ImGuiKey_Minus; - case GLFW_KEY_PERIOD: return ImGuiKey_Period; - case GLFW_KEY_SLASH: return ImGuiKey_Slash; - case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon; - case GLFW_KEY_EQUAL: return ImGuiKey_Equal; - case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; - case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; - case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; - case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; - case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; - case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock; - case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock; - case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen; - case GLFW_KEY_PAUSE: return ImGuiKey_Pause; - case GLFW_KEY_KP_0: return ImGuiKey_Keypad0; - case GLFW_KEY_KP_1: return ImGuiKey_Keypad1; - case GLFW_KEY_KP_2: return ImGuiKey_Keypad2; - case GLFW_KEY_KP_3: return ImGuiKey_Keypad3; - case GLFW_KEY_KP_4: return ImGuiKey_Keypad4; - case GLFW_KEY_KP_5: return ImGuiKey_Keypad5; - case GLFW_KEY_KP_6: return ImGuiKey_Keypad6; - case GLFW_KEY_KP_7: return ImGuiKey_Keypad7; - case GLFW_KEY_KP_8: return ImGuiKey_Keypad8; - case GLFW_KEY_KP_9: return ImGuiKey_Keypad9; - case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal; - case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide; - case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; - case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; - case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd; - case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; - case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; - case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; - case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl; - case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; - case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; - case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; - case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl; - case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; - case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; - case GLFW_KEY_MENU: return ImGuiKey_Menu; - case GLFW_KEY_0: return ImGuiKey_0; - case GLFW_KEY_1: return ImGuiKey_1; - case GLFW_KEY_2: return ImGuiKey_2; - case GLFW_KEY_3: return ImGuiKey_3; - case GLFW_KEY_4: return ImGuiKey_4; - case GLFW_KEY_5: return ImGuiKey_5; - case GLFW_KEY_6: return ImGuiKey_6; - case GLFW_KEY_7: return ImGuiKey_7; - case GLFW_KEY_8: return ImGuiKey_8; - case GLFW_KEY_9: return ImGuiKey_9; - case GLFW_KEY_A: return ImGuiKey_A; - case GLFW_KEY_B: return ImGuiKey_B; - case GLFW_KEY_C: return ImGuiKey_C; - case GLFW_KEY_D: return ImGuiKey_D; - case GLFW_KEY_E: return ImGuiKey_E; - case GLFW_KEY_F: return ImGuiKey_F; - case GLFW_KEY_G: return ImGuiKey_G; - case GLFW_KEY_H: return ImGuiKey_H; - case GLFW_KEY_I: return ImGuiKey_I; - case GLFW_KEY_J: return ImGuiKey_J; - case GLFW_KEY_K: return ImGuiKey_K; - case GLFW_KEY_L: return ImGuiKey_L; - case GLFW_KEY_M: return ImGuiKey_M; - case GLFW_KEY_N: return ImGuiKey_N; - case GLFW_KEY_O: return ImGuiKey_O; - case GLFW_KEY_P: return ImGuiKey_P; - case GLFW_KEY_Q: return ImGuiKey_Q; - case GLFW_KEY_R: return ImGuiKey_R; - case GLFW_KEY_S: return ImGuiKey_S; - case GLFW_KEY_T: return ImGuiKey_T; - case GLFW_KEY_U: return ImGuiKey_U; - case GLFW_KEY_V: return ImGuiKey_V; - case GLFW_KEY_W: return ImGuiKey_W; - case GLFW_KEY_X: return ImGuiKey_X; - case GLFW_KEY_Y: return ImGuiKey_Y; - case GLFW_KEY_Z: return ImGuiKey_Z; - case GLFW_KEY_F1: return ImGuiKey_F1; - case GLFW_KEY_F2: return ImGuiKey_F2; - case GLFW_KEY_F3: return ImGuiKey_F3; - case GLFW_KEY_F4: return ImGuiKey_F4; - case GLFW_KEY_F5: return ImGuiKey_F5; - case GLFW_KEY_F6: return ImGuiKey_F6; - case GLFW_KEY_F7: return ImGuiKey_F7; - case GLFW_KEY_F8: return ImGuiKey_F8; - case GLFW_KEY_F9: return ImGuiKey_F9; - case GLFW_KEY_F10: return ImGuiKey_F10; - case GLFW_KEY_F11: return ImGuiKey_F11; - case GLFW_KEY_F12: return ImGuiKey_F12; - default: return ImGuiKey_None; - } -} - -static int ImGui_ImplGlfw_KeyToModifier(int key) -{ - if (key == GLFW_KEY_LEFT_CONTROL || key == GLFW_KEY_RIGHT_CONTROL) - return GLFW_MOD_CONTROL; - if (key == GLFW_KEY_LEFT_SHIFT || key == GLFW_KEY_RIGHT_SHIFT) - return GLFW_MOD_SHIFT; - if (key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_RIGHT_ALT) - return GLFW_MOD_ALT; - if (key == GLFW_KEY_LEFT_SUPER || key == GLFW_KEY_RIGHT_SUPER) - return GLFW_MOD_SUPER; - return 0; -} - -static void ImGui_ImplGlfw_UpdateKeyModifiers(int mods) -{ - ImGuiIO& io = ImGui::GetIO(); - io.AddKeyEvent(ImGuiKey_ModCtrl, (mods & GLFW_MOD_CONTROL) != 0); - io.AddKeyEvent(ImGuiKey_ModShift, (mods & GLFW_MOD_SHIFT) != 0); - io.AddKeyEvent(ImGuiKey_ModAlt, (mods & GLFW_MOD_ALT) != 0); - io.AddKeyEvent(ImGuiKey_ModSuper, (mods & GLFW_MOD_SUPER) != 0); -} - -void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackMousebutton != NULL && window == bd->Window) - bd->PrevUserCallbackMousebutton(window, button, action, mods); - - ImGui_ImplGlfw_UpdateKeyModifiers(mods); - - ImGuiIO& io = ImGui::GetIO(); - if (button >= 0 && button < ImGuiMouseButton_COUNT) - io.AddMouseButtonEvent(button, action == GLFW_PRESS); -} - -void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackScroll != NULL && window == bd->Window) - bd->PrevUserCallbackScroll(window, xoffset, yoffset); - - ImGuiIO& io = ImGui::GetIO(); - io.AddMouseWheelEvent((float)xoffset, (float)yoffset); -} - -static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) -{ -#if GLFW_HAS_GET_KEY_NAME && !defined(__EMSCRIPTEN__) - // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. - // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) - // See https://github.com/glfw/glfw/issues/1502 for details. - // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process). - // This won't cover edge cases but this is at least going to cover common cases. - if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL) - return key; - const char* key_name = glfwGetKeyName(key, scancode); - if (key_name && key_name[0] != 0 && key_name[1] == 0) - { - const char char_names[] = "`-=[]\\,;\'./"; - const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 }; - IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys)); - if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); } - else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); } - else if (key_name[0] >= 'a' && key_name[0] <= 'z') { key = GLFW_KEY_A + (key_name[0] - 'a'); } - else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; } - } - // if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name); -#else - IM_UNUSED(scancode); -#endif - return key; -} - -void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods) -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackKey != NULL && window == bd->Window) - bd->PrevUserCallbackKey(window, keycode, scancode, action, mods); - - if (action != GLFW_PRESS && action != GLFW_RELEASE) - return; - - // Workaround: X11 does not include current pressed/released modifier key in 'mods' flags. https://github.com/glfw/glfw/issues/1630 - if (int keycode_to_mod = ImGui_ImplGlfw_KeyToModifier(keycode)) - mods = (action == GLFW_PRESS) ? (mods | keycode_to_mod) : (mods & ~keycode_to_mod); - ImGui_ImplGlfw_UpdateKeyModifiers(mods); - - // mziulek: Below line causes `InvalidValue` error and then crash in mach-glfw - //keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); - - ImGuiIO& io = ImGui::GetIO(); - ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode); - io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); - io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) -} - -void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackWindowFocus != NULL && window == bd->Window) - bd->PrevUserCallbackWindowFocus(window, focused); - - ImGuiIO& io = ImGui::GetIO(); - io.AddFocusEvent(focused != 0); -} - -void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackCursorPos != NULL && window == bd->Window) - bd->PrevUserCallbackCursorPos(window, x * bd->DpiScale.x, y * bd->DpiScale.y); - - ImGuiIO& io = ImGui::GetIO(); - io.AddMousePosEvent((float)x * bd->DpiScale.x, (float)y * bd->DpiScale.y); - bd->LastValidMousePos = ImVec2((float)x * bd->DpiScale.x, (float)y * bd->DpiScale.y); -} - -// Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position, -// so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) -void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackCursorEnter != NULL && window == bd->Window) - bd->PrevUserCallbackCursorEnter(window, entered); - - ImGuiIO& io = ImGui::GetIO(); - if (entered) - { - bd->MouseWindow = window; - io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y); - } - else if (!entered && bd->MouseWindow == window) - { - bd->LastValidMousePos = io.MousePos; - bd->MouseWindow = NULL; - io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); - } -} - -void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackChar != NULL && window == bd->Window) - bd->PrevUserCallbackChar(window, c); - - ImGuiIO& io = ImGui::GetIO(); - io.AddInputCharacter(c); -} - -void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) -{ - // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. -} - -void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!"); - IM_ASSERT(bd->Window == window); - - bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback); - bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback); - bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); - bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); - bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); - bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); - bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); - bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); - bd->InstalledCallbacks = true; -} - -void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window) -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!"); - IM_ASSERT(bd->Window == window); - - glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus); - glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter); - glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos); - glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton); - glfwSetScrollCallback(window, bd->PrevUserCallbackScroll); - glfwSetKeyCallback(window, bd->PrevUserCallbackKey); - glfwSetCharCallback(window, bd->PrevUserCallbackChar); - glfwSetMonitorCallback(bd->PrevUserCallbackMonitor); - bd->InstalledCallbacks = false; - bd->PrevUserCallbackWindowFocus = NULL; - bd->PrevUserCallbackCursorEnter = NULL; - bd->PrevUserCallbackCursorPos = NULL; - bd->PrevUserCallbackMousebutton = NULL; - bd->PrevUserCallbackScroll = NULL; - bd->PrevUserCallbackKey = NULL; - bd->PrevUserCallbackChar = NULL; - bd->PrevUserCallbackMonitor = NULL; -} - -static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) -{ - ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); - - // Setup backend capabilities flags - ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)(); - io.BackendPlatformUserData = (void*)bd; - io.BackendPlatformName = "imgui_impl_glfw"; - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - - bd->Window = window; - bd->Time = 0.0; - bd->DpiScale = ImVec2{ 1.0, 1.0 }; - - io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; - io.ClipboardUserData = bd->Window; - - // Set platform dependent data in viewport -#if defined(_WIN32) - ImGui::GetMainViewport()->PlatformHandleRaw = (void*)glfwGetWin32Window(bd->Window); -#endif - - // Create mouse cursors - // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, - // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. - // Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.) - GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL); - bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); - bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); - bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); - bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); -#if GLFW_HAS_NEW_CURSORS - bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); - bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); - bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); - bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); -#else - bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); -#endif - glfwSetErrorCallback(prev_error_callback); - glfwGetError(NULL); // Pop potential error from the stack. - - // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. - if (install_callbacks) - ImGui_ImplGlfw_InstallCallbacks(window); - - bd->ClientApi = client_api; - return true; -} - -bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) -{ - return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); -} - -bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) -{ - return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); -} - -bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) -{ - return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown); -} - -void ImGui_ImplGlfw_Shutdown() -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - - if (bd->InstalledCallbacks) - ImGui_ImplGlfw_RestoreCallbacks(bd->Window); - - for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) - glfwDestroyCursor(bd->MouseCursors[cursor_n]); - - io.BackendPlatformName = NULL; - io.BackendPlatformUserData = NULL; - IM_DELETE(bd); -} - -static void ImGui_ImplGlfw_UpdateMouseData() -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); - -#ifdef __EMSCRIPTEN__ - const bool is_app_focused = true; -#else - const bool is_app_focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0; -#endif - if (is_app_focused) - { - // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - glfwSetCursorPos(bd->Window, (double)io.MousePos.x, (double)io.MousePos.y); - - // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured) - if (is_app_focused && bd->MouseWindow == NULL) - { - double mouse_x, mouse_y; - glfwGetCursorPos(bd->Window, &mouse_x, &mouse_y); - io.AddMousePosEvent((float)mouse_x * bd->DpiScale.x, (float)mouse_y * bd->DpiScale.y); - bd->LastValidMousePos = ImVec2((float)mouse_x * bd->DpiScale.x, (float)mouse_y * bd->DpiScale.y); - } - } -} - -static void ImGui_ImplGlfw_UpdateMouseCursor() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) - return; - - ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) - { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); - } - else - { - // Show OS mouse cursor - // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. - glfwSetCursor(bd->Window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]); - glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - } -} - -// Update gamepad inputs -static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } -static void ImGui_ImplGlfw_UpdateGamepads() -{ - ImGuiIO& io = ImGui::GetIO(); - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) - return; - - io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; -#if GLFW_HAS_GAMEPAD_API - GLFWgamepadstate gamepad; - if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) - return; - #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0) - #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) -#else - int axes_count = 0, buttons_count = 0; - const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); - const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); - if (axes_count == 0 || buttons_count == 0) - return; - #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0) - #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) -#endif - io.BackendFlags |= ImGuiBackendFlags_HasGamepad; - MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7); - MAP_BUTTON(ImGuiKey_GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6); - MAP_BUTTON(ImGuiKey_GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross - MAP_BUTTON(ImGuiKey_GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle - MAP_BUTTON(ImGuiKey_GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square - MAP_BUTTON(ImGuiKey_GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle - MAP_BUTTON(ImGuiKey_GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13); - MAP_BUTTON(ImGuiKey_GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11); - MAP_BUTTON(ImGuiKey_GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10); - MAP_BUTTON(ImGuiKey_GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12); - MAP_BUTTON(ImGuiKey_GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4); - MAP_BUTTON(ImGuiKey_GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5); - MAP_ANALOG(ImGuiKey_GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f); - MAP_ANALOG(ImGuiKey_GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f); - MAP_BUTTON(ImGuiKey_GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8); - MAP_BUTTON(ImGuiKey_GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9); - MAP_ANALOG(ImGuiKey_GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f); - MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f); - MAP_ANALOG(ImGuiKey_GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f); - MAP_ANALOG(ImGuiKey_GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f); - MAP_ANALOG(ImGuiKey_GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f); - MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f); - MAP_ANALOG(ImGuiKey_GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f); - MAP_ANALOG(ImGuiKey_GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f); - #undef MAP_BUTTON - #undef MAP_ANALOG -} - -void ImGui_ImplGlfw_NewFrame() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - IM_ASSERT(bd != NULL && "Did you call ImGui_ImplGlfw_InitForXXX()?"); - - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - glfwGetWindowSize(bd->Window, &w, &h); - glfwGetFramebufferSize(bd->Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - if (w > 0 && h > 0) - io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); - - bd->DpiScale.x = ceil(io.DisplayFramebufferScale.x); - bd->DpiScale.y = ceil(io.DisplayFramebufferScale.y); - - // Setup time step - double current_time = glfwGetTime(); - io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); - bd->Time = current_time; - - ImGui_ImplGlfw_UpdateMouseData(); - ImGui_ImplGlfw_UpdateMouseCursor(); - - // Update game controllers (if enabled and available) - ImGui_ImplGlfw_UpdateGamepads(); -} - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif diff --git a/libs/zgui/libs/imgui/imgui_impl_wgpu.cpp b/libs/zgui/libs/imgui/imgui_impl_wgpu.cpp deleted file mode 100644 index 1ba25db27..000000000 --- a/libs/zgui/libs/imgui/imgui_impl_wgpu.cpp +++ /dev/null @@ -1,732 +0,0 @@ -// dear imgui: Renderer for WebGPU -// This needs to be used along with a Platform Binding (e.g. GLFW) -// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2021-11-29: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer()/wgpuRenderPassEncoderSetIndexBuffer(). -// 2021-08-24: Fix for latest specs. -// 2021-05-24: Add support for draw_data->FramebufferScale. -// 2021-05-19: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) -// 2021-05-16: Update to latest WebGPU specs (compatible with Emscripten 2.0.20 and Chrome Canary 92). -// 2021-02-18: Change blending equation to preserve alpha in output buffer. -// 2021-01-28: Initial version. - -#include "imgui.h" -#include -#include - -#define HAS_EMSCRIPTEN_VERSION(major, minor, tiny) (__EMSCRIPTEN_major__ > (major) || (__EMSCRIPTEN_major__ == (major) && __EMSCRIPTEN_minor__ > (minor)) || (__EMSCRIPTEN_major__ == (major) && __EMSCRIPTEN_minor__ == (minor) && __EMSCRIPTEN_tiny__ >= (tiny))) - -#if defined(__EMSCRIPTEN__) && !HAS_EMSCRIPTEN_VERSION(2, 0, 20) -#error "Requires at least emscripten 2.0.20" -#endif - -// Dear ImGui prototypes from imgui_internal.h -extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0); - -// mziulek: We removed header file and declare all our external functions here. -extern "C" { - -bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format); -void ImGui_ImplWGPU_Shutdown(void); -void ImGui_ImplWGPU_NewFrame(void); -void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder); - -// Use if you want to reset your rendering device without losing Dear ImGui state. -void ImGui_ImplWGPU_InvalidateDeviceObjects(void); -bool ImGui_ImplWGPU_CreateDeviceObjects(void); - -} // extern "C" - -// WebGPU data -static WGPUDevice g_wgpuDevice = NULL; -static WGPUQueue g_defaultQueue = NULL; -static WGPUTextureFormat g_renderTargetFormat = WGPUTextureFormat_Undefined; -static WGPURenderPipeline g_pipelineState = NULL; - -struct RenderResources -{ - WGPUTexture FontTexture; // Font texture - WGPUTextureView FontTextureView; // Texture view for font texture - WGPUSampler Sampler; // Sampler for the font texture - WGPUBuffer Uniforms; // Shader uniforms - WGPUBindGroup CommonBindGroup; // Resources bind-group to bind the common resources to pipeline - ImGuiStorage ImageBindGroups; // Resources bind-group to bind the font/image resources to pipeline (this is a key->value map) - WGPUBindGroup ImageBindGroup; // Default font-resource of Dear ImGui - WGPUBindGroupLayout ImageBindGroupLayout; // Cache layout used for the image bind group. Avoids allocating unnecessary JS objects when working with WebASM -}; -static RenderResources g_resources; - -struct FrameResources -{ - WGPUBuffer IndexBuffer; - WGPUBuffer VertexBuffer; - ImDrawIdx* IndexBufferHost; - ImDrawVert* VertexBufferHost; - int IndexBufferSize; - int VertexBufferSize; -}; -static FrameResources* g_pFrameResources = NULL; -static unsigned int g_numFramesInFlight = 0; -static unsigned int g_frameIndex = UINT_MAX; - -struct Uniforms -{ - float MVP[4][4]; -}; - -//----------------------------------------------------------------------------- -// SHADERS -//----------------------------------------------------------------------------- - -// glsl_shader.vert, compiled with: -// # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert -/* -#version 450 core -layout(location = 0) in vec2 aPos; -layout(location = 1) in vec2 aUV; -layout(location = 2) in vec4 aColor; -layout(set=0, binding = 0) uniform transform { mat4 mvp; }; - -out gl_PerVertex { vec4 gl_Position; }; -layout(location = 0) out struct { vec4 Color; vec2 UV; } Out; - -void main() -{ - Out.Color = aColor; - Out.UV = aUV; - gl_Position = mvp * vec4(aPos, 0, 1); -} -*/ -static uint32_t __glsl_shader_vert_spv[] = -{ - 0x07230203,0x00010000,0x00080007,0x0000002c,0x00000000,0x00020011,0x00000001,0x0006000b, - 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, - 0x000a000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000000b,0x0000000f,0x00000015, - 0x0000001b,0x00000023,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, - 0x00000000,0x00030005,0x00000009,0x00000000,0x00050006,0x00000009,0x00000000,0x6f6c6f43, - 0x00000072,0x00040006,0x00000009,0x00000001,0x00005655,0x00030005,0x0000000b,0x0074754f, - 0x00040005,0x0000000f,0x6c6f4361,0x0000726f,0x00030005,0x00000015,0x00565561,0x00060005, - 0x00000019,0x505f6c67,0x65567265,0x78657472,0x00000000,0x00060006,0x00000019,0x00000000, - 0x505f6c67,0x7469736f,0x006e6f69,0x00030005,0x0000001b,0x00000000,0x00050005,0x0000001d, - 0x6e617274,0x726f6673,0x0000006d,0x00040006,0x0000001d,0x00000000,0x0070766d,0x00030005, - 0x0000001f,0x00000000,0x00040005,0x00000023,0x736f5061,0x00000000,0x00040047,0x0000000b, - 0x0000001e,0x00000000,0x00040047,0x0000000f,0x0000001e,0x00000002,0x00040047,0x00000015, - 0x0000001e,0x00000001,0x00050048,0x00000019,0x00000000,0x0000000b,0x00000000,0x00030047, - 0x00000019,0x00000002,0x00040048,0x0000001d,0x00000000,0x00000005,0x00050048,0x0000001d, - 0x00000000,0x00000023,0x00000000,0x00050048,0x0000001d,0x00000000,0x00000007,0x00000010, - 0x00030047,0x0000001d,0x00000002,0x00040047,0x0000001f,0x00000022,0x00000000,0x00040047, - 0x0000001f,0x00000021,0x00000000,0x00040047,0x00000023,0x0000001e,0x00000000,0x00020013, - 0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017, - 0x00000007,0x00000006,0x00000004,0x00040017,0x00000008,0x00000006,0x00000002,0x0004001e, - 0x00000009,0x00000007,0x00000008,0x00040020,0x0000000a,0x00000003,0x00000009,0x0004003b, - 0x0000000a,0x0000000b,0x00000003,0x00040015,0x0000000c,0x00000020,0x00000001,0x0004002b, - 0x0000000c,0x0000000d,0x00000000,0x00040020,0x0000000e,0x00000001,0x00000007,0x0004003b, - 0x0000000e,0x0000000f,0x00000001,0x00040020,0x00000011,0x00000003,0x00000007,0x0004002b, - 0x0000000c,0x00000013,0x00000001,0x00040020,0x00000014,0x00000001,0x00000008,0x0004003b, - 0x00000014,0x00000015,0x00000001,0x00040020,0x00000017,0x00000003,0x00000008,0x0003001e, - 0x00000019,0x00000007,0x00040020,0x0000001a,0x00000003,0x00000019,0x0004003b,0x0000001a, - 0x0000001b,0x00000003,0x00040018,0x0000001c,0x00000007,0x00000004,0x0003001e,0x0000001d, - 0x0000001c,0x00040020,0x0000001e,0x00000002,0x0000001d,0x0004003b,0x0000001e,0x0000001f, - 0x00000002,0x00040020,0x00000020,0x00000002,0x0000001c,0x0004003b,0x00000014,0x00000023, - 0x00000001,0x0004002b,0x00000006,0x00000025,0x00000000,0x0004002b,0x00000006,0x00000026, - 0x3f800000,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005, - 0x0004003d,0x00000007,0x00000010,0x0000000f,0x00050041,0x00000011,0x00000012,0x0000000b, - 0x0000000d,0x0003003e,0x00000012,0x00000010,0x0004003d,0x00000008,0x00000016,0x00000015, - 0x00050041,0x00000017,0x00000018,0x0000000b,0x00000013,0x0003003e,0x00000018,0x00000016, - 0x00050041,0x00000020,0x00000021,0x0000001f,0x0000000d,0x0004003d,0x0000001c,0x00000022, - 0x00000021,0x0004003d,0x00000008,0x00000024,0x00000023,0x00050051,0x00000006,0x00000027, - 0x00000024,0x00000000,0x00050051,0x00000006,0x00000028,0x00000024,0x00000001,0x00070050, - 0x00000007,0x00000029,0x00000027,0x00000028,0x00000025,0x00000026,0x00050091,0x00000007, - 0x0000002a,0x00000022,0x00000029,0x00050041,0x00000011,0x0000002b,0x0000001b,0x0000000d, - 0x0003003e,0x0000002b,0x0000002a,0x000100fd,0x00010038 -}; - -// glsl_shader.frag, compiled with: -// # glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag -/* -#version 450 core -layout(location = 0) out vec4 fColor; -layout(set=0, binding=1) uniform sampler s; -layout(set=1, binding=0) uniform texture2D t; -layout(location = 0) in struct { vec4 Color; vec2 UV; } In; -void main() -{ - fColor = In.Color * texture(sampler2D(t, s), In.UV.st); -} -*/ -static uint32_t __glsl_shader_frag_spv[] = -{ - 0x07230203,0x00010000,0x00080007,0x00000023,0x00000000,0x00020011,0x00000001,0x0006000b, - 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, - 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000d,0x00030010, - 0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, - 0x00000000,0x00040005,0x00000009,0x6c6f4366,0x0000726f,0x00030005,0x0000000b,0x00000000, - 0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,0x0000000b,0x00000001, - 0x00005655,0x00030005,0x0000000d,0x00006e49,0x00030005,0x00000015,0x00000074,0x00030005, - 0x00000019,0x00000073,0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000d, - 0x0000001e,0x00000000,0x00040047,0x00000015,0x00000022,0x00000001,0x00040047,0x00000015, - 0x00000021,0x00000000,0x00040047,0x00000019,0x00000022,0x00000000,0x00040047,0x00000019, - 0x00000021,0x00000001,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016, - 0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008, - 0x00000003,0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,0x00040017,0x0000000a, - 0x00000006,0x00000002,0x0004001e,0x0000000b,0x00000007,0x0000000a,0x00040020,0x0000000c, - 0x00000001,0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040015,0x0000000e, - 0x00000020,0x00000001,0x0004002b,0x0000000e,0x0000000f,0x00000000,0x00040020,0x00000010, - 0x00000001,0x00000007,0x00090019,0x00000013,0x00000006,0x00000001,0x00000000,0x00000000, - 0x00000000,0x00000001,0x00000000,0x00040020,0x00000014,0x00000000,0x00000013,0x0004003b, - 0x00000014,0x00000015,0x00000000,0x0002001a,0x00000017,0x00040020,0x00000018,0x00000000, - 0x00000017,0x0004003b,0x00000018,0x00000019,0x00000000,0x0003001b,0x0000001b,0x00000013, - 0x0004002b,0x0000000e,0x0000001d,0x00000001,0x00040020,0x0000001e,0x00000001,0x0000000a, - 0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x00050041, - 0x00000010,0x00000011,0x0000000d,0x0000000f,0x0004003d,0x00000007,0x00000012,0x00000011, - 0x0004003d,0x00000013,0x00000016,0x00000015,0x0004003d,0x00000017,0x0000001a,0x00000019, - 0x00050056,0x0000001b,0x0000001c,0x00000016,0x0000001a,0x00050041,0x0000001e,0x0000001f, - 0x0000000d,0x0000001d,0x0004003d,0x0000000a,0x00000020,0x0000001f,0x00050057,0x00000007, - 0x00000021,0x0000001c,0x00000020,0x00050085,0x00000007,0x00000022,0x00000012,0x00000021, - 0x0003003e,0x00000009,0x00000022,0x000100fd,0x00010038 -}; - -static void SafeRelease(ImDrawIdx*& res) -{ - if (res) - delete[] res; - res = NULL; -} -static void SafeRelease(ImDrawVert*& res) -{ - if (res) - delete[] res; - res = NULL; -} -static void SafeRelease(WGPUBindGroupLayout& res) -{ - if (res) - wgpuBindGroupLayoutRelease(res); - res = NULL; -} -static void SafeRelease(WGPUBindGroup& res) -{ - if (res) - wgpuBindGroupRelease(res); - res = NULL; -} -static void SafeRelease(WGPUBuffer& res) -{ - if (res) - wgpuBufferRelease(res); - res = NULL; -} -static void SafeRelease(WGPURenderPipeline& res) -{ - if (res) - wgpuRenderPipelineRelease(res); - res = NULL; -} -static void SafeRelease(WGPUSampler& res) -{ - if (res) - wgpuSamplerRelease(res); - res = NULL; -} -static void SafeRelease(WGPUShaderModule& res) -{ - if (res) - wgpuShaderModuleRelease(res); - res = NULL; -} -static void SafeRelease(WGPUTextureView& res) -{ - if (res) - wgpuTextureViewRelease(res); - res = NULL; -} -static void SafeRelease(WGPUTexture& res) -{ - if (res) - wgpuTextureRelease(res); - res = NULL; -} - -static void SafeRelease(RenderResources& res) -{ - SafeRelease(res.FontTexture); - SafeRelease(res.FontTextureView); - SafeRelease(res.Sampler); - SafeRelease(res.Uniforms); - SafeRelease(res.CommonBindGroup); - SafeRelease(res.ImageBindGroup); - SafeRelease(res.ImageBindGroupLayout); -}; - -static void SafeRelease(FrameResources& res) -{ - SafeRelease(res.IndexBuffer); - SafeRelease(res.VertexBuffer); - SafeRelease(res.IndexBufferHost); - SafeRelease(res.VertexBufferHost); -} - -static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(uint32_t* binary_data, uint32_t binary_data_size) -{ - WGPUShaderModuleSPIRVDescriptor spirv_desc = {}; - spirv_desc.chain.sType = WGPUSType_ShaderModuleSPIRVDescriptor; - spirv_desc.codeSize = binary_data_size; - spirv_desc.code = binary_data; - - WGPUShaderModuleDescriptor desc = {}; - desc.nextInChain = reinterpret_cast(&spirv_desc); - - WGPUProgrammableStageDescriptor stage_desc = {}; - stage_desc.module = wgpuDeviceCreateShaderModule(g_wgpuDevice, &desc); - stage_desc.entryPoint = "main"; - return stage_desc; -} - -static WGPUBindGroup ImGui_ImplWGPU_CreateImageBindGroup(WGPUBindGroupLayout layout, WGPUTextureView texture) -{ - WGPUBindGroupEntry image_bg_entries[] = { { nullptr, 0, 0, 0, 0, 0, texture } }; - - WGPUBindGroupDescriptor image_bg_descriptor = {}; - image_bg_descriptor.layout = layout; - image_bg_descriptor.entryCount = sizeof(image_bg_entries) / sizeof(WGPUBindGroupEntry); - image_bg_descriptor.entries = image_bg_entries; - return wgpuDeviceCreateBindGroup(g_wgpuDevice, &image_bg_descriptor); -} - -static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPassEncoder ctx, FrameResources* fr) -{ - // Setup orthographic projection matrix into our constant buffer - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). - { - float L = draw_data->DisplayPos.x; - float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; - float T = draw_data->DisplayPos.y; - float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; - float mvp[4][4] = - { - { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.5f, 0.0f }, - { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, - }; - wgpuQueueWriteBuffer(g_defaultQueue, g_resources.Uniforms, 0, mvp, sizeof(mvp)); - } - - // Setup viewport - wgpuRenderPassEncoderSetViewport(ctx, 0, 0, draw_data->FramebufferScale.x * draw_data->DisplaySize.x, draw_data->FramebufferScale.y * draw_data->DisplaySize.y, 0, 1); - - // Bind shader and vertex buffers - wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, 0, fr->VertexBufferSize * sizeof(ImDrawVert)); - wgpuRenderPassEncoderSetIndexBuffer(ctx, fr->IndexBuffer, sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32, 0, fr->IndexBufferSize * sizeof(ImDrawIdx)); - wgpuRenderPassEncoderSetPipeline(ctx, g_pipelineState); - wgpuRenderPassEncoderSetBindGroup(ctx, 0, g_resources.CommonBindGroup, 0, NULL); - - // Setup blend factor - WGPUColor blend_color = { 0.f, 0.f, 0.f, 0.f }; - wgpuRenderPassEncoderSetBlendConstant(ctx, &blend_color); -} - -// Render function -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder) -{ - // Avoid rendering when minimized - if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) - return; - - // FIXME: Assuming that this only gets called once per frame! - // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. - g_frameIndex = g_frameIndex + 1; - FrameResources* fr = &g_pFrameResources[g_frameIndex % g_numFramesInFlight]; - - // Create and grow vertex/index buffers if needed - if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount) - { - if (fr->VertexBuffer) - { - wgpuBufferDestroy(fr->VertexBuffer); - wgpuBufferRelease(fr->VertexBuffer); - } - SafeRelease(fr->VertexBufferHost); - fr->VertexBufferSize = draw_data->TotalVtxCount + 5000; - - WGPUBufferDescriptor vb_desc = - { - NULL, - "Dear ImGui Vertex buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, - fr->VertexBufferSize * sizeof(ImDrawVert), - false - }; - fr->VertexBuffer = wgpuDeviceCreateBuffer(g_wgpuDevice, &vb_desc); - if (!fr->VertexBuffer) - return; - - fr->VertexBufferHost = new ImDrawVert[fr->VertexBufferSize]; - } - if (fr->IndexBuffer == NULL || fr->IndexBufferSize < draw_data->TotalIdxCount) - { - if (fr->IndexBuffer) - { - wgpuBufferDestroy(fr->IndexBuffer); - wgpuBufferRelease(fr->IndexBuffer); - } - SafeRelease(fr->IndexBufferHost); - fr->IndexBufferSize = draw_data->TotalIdxCount + 10000; - - WGPUBufferDescriptor ib_desc = - { - NULL, - "Dear ImGui Index buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, - fr->IndexBufferSize * sizeof(ImDrawIdx), - false - }; - fr->IndexBuffer = wgpuDeviceCreateBuffer(g_wgpuDevice, &ib_desc); - if (!fr->IndexBuffer) - return; - - fr->IndexBufferHost = new ImDrawIdx[fr->IndexBufferSize]; - } - - // Upload vertex/index data into a single contiguous GPU buffer - ImDrawVert* vtx_dst = (ImDrawVert*)fr->VertexBufferHost; - ImDrawIdx* idx_dst = (ImDrawIdx*)fr->IndexBufferHost; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); - memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); - vtx_dst += cmd_list->VtxBuffer.Size; - idx_dst += cmd_list->IdxBuffer.Size; - } - int64_t vb_write_size = ((char*)vtx_dst - (char*)fr->VertexBufferHost + 3) & ~3; - int64_t ib_write_size = ((char*)idx_dst - (char*)fr->IndexBufferHost + 3) & ~3; - wgpuQueueWriteBuffer(g_defaultQueue, fr->VertexBuffer, 0, fr->VertexBufferHost, vb_write_size); - wgpuQueueWriteBuffer(g_defaultQueue, fr->IndexBuffer, 0, fr->IndexBufferHost, ib_write_size); - - // Setup desired render state - ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr); - - // Render command lists - // (Because we merged all buffers into a single one, we maintain our own offset into them) - int global_vtx_offset = 0; - int global_idx_offset = 0; - ImVec2 clip_scale = draw_data->FramebufferScale; - ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback != NULL) - { - // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr); - else - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - // Bind custom texture - ImTextureID tex_id = pcmd->GetTexID(); - ImGuiID tex_id_hash = ImHashData(&tex_id, sizeof(tex_id)); - auto bind_group = g_resources.ImageBindGroups.GetVoidPtr(tex_id_hash); - if (bind_group) - { - wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, (WGPUBindGroup)bind_group, 0, NULL); - } - else - { - WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(g_resources.ImageBindGroupLayout, (WGPUTextureView)tex_id); - g_resources.ImageBindGroups.SetVoidPtr(tex_id_hash, image_bind_group); - wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, image_bind_group, 0, NULL); - } - - // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); - ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); - if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) - continue; - - // Apply scissor/clipping rectangle, Draw - wgpuRenderPassEncoderSetScissorRect(pass_encoder, (uint32_t)clip_min.x, (uint32_t)clip_min.y, (uint32_t)(clip_max.x - clip_min.x), (uint32_t)(clip_max.y - clip_min.y)); - wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); - } - } - global_idx_offset += cmd_list->IdxBuffer.Size; - global_vtx_offset += cmd_list->VtxBuffer.Size; - } -} - -static void ImGui_ImplWGPU_CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height, size_pp; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &size_pp); - - // Upload texture to graphics system - { - WGPUTextureDescriptor tex_desc = {}; - tex_desc.label = "Dear ImGui Font Texture"; - tex_desc.dimension = WGPUTextureDimension_2D; - tex_desc.size.width = width; - tex_desc.size.height = height; - tex_desc.size.depthOrArrayLayers = 1; - tex_desc.sampleCount = 1; - tex_desc.format = WGPUTextureFormat_RGBA8Unorm; - tex_desc.mipLevelCount = 1; - tex_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding; - g_resources.FontTexture = wgpuDeviceCreateTexture(g_wgpuDevice, &tex_desc); - - WGPUTextureViewDescriptor tex_view_desc = {}; - tex_view_desc.format = WGPUTextureFormat_RGBA8Unorm; - tex_view_desc.dimension = WGPUTextureViewDimension_2D; - tex_view_desc.baseMipLevel = 0; - tex_view_desc.mipLevelCount = 1; - tex_view_desc.baseArrayLayer = 0; - tex_view_desc.arrayLayerCount = 1; - tex_view_desc.aspect = WGPUTextureAspect_All; - g_resources.FontTextureView = wgpuTextureCreateView(g_resources.FontTexture, &tex_view_desc); - } - - // Upload texture data - { - WGPUImageCopyTexture dst_view = {}; - dst_view.texture = g_resources.FontTexture; - dst_view.mipLevel = 0; - dst_view.origin = { 0, 0, 0 }; - dst_view.aspect = WGPUTextureAspect_All; - WGPUTextureDataLayout layout = {}; - layout.offset = 0; - layout.bytesPerRow = width * size_pp; - layout.rowsPerImage = height; - WGPUExtent3D size = { (uint32_t)width, (uint32_t)height, 1 }; - wgpuQueueWriteTexture(g_defaultQueue, &dst_view, pixels, (uint32_t)(width * size_pp * height), &layout, &size); - } - - // Create the associated sampler - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) - { - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.mipmapFilter = WGPUFilterMode_Linear; - sampler_desc.addressModeU = WGPUAddressMode_Repeat; - sampler_desc.addressModeV = WGPUAddressMode_Repeat; - sampler_desc.addressModeW = WGPUAddressMode_Repeat; - sampler_desc.maxAnisotropy = 1; - g_resources.Sampler = wgpuDeviceCreateSampler(g_wgpuDevice, &sampler_desc); - } - - // Store our identifier - static_assert(sizeof(ImTextureID) >= sizeof(g_resources.FontTexture), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); - io.Fonts->SetTexID((ImTextureID)g_resources.FontTextureView); -} - -static void ImGui_ImplWGPU_CreateUniformBuffer() -{ - WGPUBufferDescriptor ub_desc = - { - NULL, - "Dear ImGui Uniform buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, - sizeof(Uniforms), - false - }; - g_resources.Uniforms = wgpuDeviceCreateBuffer(g_wgpuDevice, &ub_desc); -} - -bool ImGui_ImplWGPU_CreateDeviceObjects(void) -{ - if (!g_wgpuDevice) - return false; - if (g_pipelineState) - ImGui_ImplWGPU_InvalidateDeviceObjects(); - - // Create render pipeline - WGPURenderPipelineDescriptor graphics_pipeline_desc = {}; - graphics_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; - graphics_pipeline_desc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined; - graphics_pipeline_desc.primitive.frontFace = WGPUFrontFace_CW; - graphics_pipeline_desc.primitive.cullMode = WGPUCullMode_None; - graphics_pipeline_desc.multisample.count = 1; - graphics_pipeline_desc.multisample.mask = UINT_MAX; - graphics_pipeline_desc.multisample.alphaToCoverageEnabled = false; - graphics_pipeline_desc.layout = nullptr; // Use automatic layout generation - - // Create the vertex shader - WGPUProgrammableStageDescriptor vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__glsl_shader_vert_spv, sizeof(__glsl_shader_vert_spv) / sizeof(uint32_t)); - graphics_pipeline_desc.vertex.module = vertex_shader_desc.module; - graphics_pipeline_desc.vertex.entryPoint = vertex_shader_desc.entryPoint; - - // Vertex input configuration - WGPUVertexAttribute attribute_desc[] = - { - { WGPUVertexFormat_Float32x2, (uint64_t)IM_OFFSETOF(ImDrawVert, pos), 0 }, - { WGPUVertexFormat_Float32x2, (uint64_t)IM_OFFSETOF(ImDrawVert, uv), 1 }, - { WGPUVertexFormat_Unorm8x4, (uint64_t)IM_OFFSETOF(ImDrawVert, col), 2 }, - }; - - WGPUVertexBufferLayout buffer_layouts[1]; - buffer_layouts[0].arrayStride = sizeof(ImDrawVert); - buffer_layouts[0].stepMode = WGPUVertexStepMode_Vertex; - buffer_layouts[0].attributeCount = 3; - buffer_layouts[0].attributes = attribute_desc; - - graphics_pipeline_desc.vertex.bufferCount = 1; - graphics_pipeline_desc.vertex.buffers = buffer_layouts; - - // Create the pixel shader - WGPUProgrammableStageDescriptor pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__glsl_shader_frag_spv, sizeof(__glsl_shader_frag_spv) / sizeof(uint32_t)); - - // Create the blending setup - WGPUBlendState blend_state = {}; - blend_state.alpha.operation = WGPUBlendOperation_Add; - blend_state.alpha.srcFactor = WGPUBlendFactor_One; - blend_state.alpha.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; - blend_state.color.operation = WGPUBlendOperation_Add; - blend_state.color.srcFactor = WGPUBlendFactor_SrcAlpha; - blend_state.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; - - WGPUColorTargetState color_state = {}; - color_state.format = g_renderTargetFormat; - color_state.blend = &blend_state; - color_state.writeMask = WGPUColorWriteMask_All; - - WGPUFragmentState fragment_state = {}; - fragment_state.module = pixel_shader_desc.module; - fragment_state.entryPoint = pixel_shader_desc.entryPoint; - fragment_state.targetCount = 1; - fragment_state.targets = &color_state; - - graphics_pipeline_desc.fragment = &fragment_state; - - // Create depth-stencil State - WGPUDepthStencilState depth_stencil_state = {}; - depth_stencil_state.depthBias = 0; - depth_stencil_state.depthBiasClamp = 0; - depth_stencil_state.depthBiasSlopeScale = 0; - - // Configure disabled depth-stencil state - graphics_pipeline_desc.depthStencil = nullptr; - - g_pipelineState = wgpuDeviceCreateRenderPipeline(g_wgpuDevice, &graphics_pipeline_desc); - - ImGui_ImplWGPU_CreateFontsTexture(); - ImGui_ImplWGPU_CreateUniformBuffer(); - - // Create resource bind group - WGPUBindGroupLayout bg_layouts[2]; - bg_layouts[0] = wgpuRenderPipelineGetBindGroupLayout(g_pipelineState, 0); - bg_layouts[1] = wgpuRenderPipelineGetBindGroupLayout(g_pipelineState, 1); - - WGPUBindGroupEntry common_bg_entries[] = - { - { nullptr, 0, g_resources.Uniforms, 0, sizeof(Uniforms), 0, 0 }, - { nullptr, 1, 0, 0, 0, g_resources.Sampler, 0 }, - }; - - WGPUBindGroupDescriptor common_bg_descriptor = {}; - common_bg_descriptor.layout = bg_layouts[0]; - common_bg_descriptor.entryCount = sizeof(common_bg_entries) / sizeof(WGPUBindGroupEntry); - common_bg_descriptor.entries = common_bg_entries; - g_resources.CommonBindGroup = wgpuDeviceCreateBindGroup(g_wgpuDevice, &common_bg_descriptor); - - WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bg_layouts[1], g_resources.FontTextureView); - g_resources.ImageBindGroup = image_bind_group; - g_resources.ImageBindGroupLayout = bg_layouts[1]; - g_resources.ImageBindGroups.SetVoidPtr(ImHashData(&g_resources.FontTextureView, sizeof(ImTextureID)), image_bind_group); - - SafeRelease(vertex_shader_desc.module); - SafeRelease(pixel_shader_desc.module); - SafeRelease(bg_layouts[0]); - - return true; -} - -void ImGui_ImplWGPU_InvalidateDeviceObjects(void) -{ - if (!g_wgpuDevice) - return; - - SafeRelease(g_pipelineState); - SafeRelease(g_resources); - - ImGuiIO& io = ImGui::GetIO(); - io.Fonts->SetTexID(NULL); // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. - - for (unsigned int i = 0; i < g_numFramesInFlight; i++) - SafeRelease(g_pFrameResources[i]); -} - -bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format) -{ - // Setup backend capabilities flags - ImGuiIO& io = ImGui::GetIO(); - io.BackendRendererName = "imgui_impl_webgpu"; - io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. - - g_wgpuDevice = device; - g_defaultQueue = wgpuDeviceGetQueue(g_wgpuDevice); - g_renderTargetFormat = rt_format; - g_pFrameResources = new FrameResources[num_frames_in_flight]; - g_numFramesInFlight = num_frames_in_flight; - g_frameIndex = UINT_MAX; - - g_resources.FontTexture = NULL; - g_resources.FontTextureView = NULL; - g_resources.Sampler = NULL; - g_resources.Uniforms = NULL; - g_resources.CommonBindGroup = NULL; - g_resources.ImageBindGroups.Data.reserve(100); - g_resources.ImageBindGroup = NULL; - g_resources.ImageBindGroupLayout = NULL; - - // Create buffers with a default size (they will later be grown as needed) - for (int i = 0; i < num_frames_in_flight; i++) - { - FrameResources* fr = &g_pFrameResources[i]; - fr->IndexBuffer = NULL; - fr->VertexBuffer = NULL; - fr->IndexBufferHost = NULL; - fr->VertexBufferHost = NULL; - fr->IndexBufferSize = 10000; - fr->VertexBufferSize = 5000; - } - - return true; -} - -void ImGui_ImplWGPU_Shutdown(void) -{ - ImGui_ImplWGPU_InvalidateDeviceObjects(); - delete[] g_pFrameResources; - g_pFrameResources = NULL; - wgpuQueueRelease(g_defaultQueue); - g_wgpuDevice = NULL; - g_numFramesInFlight = 0; - g_frameIndex = UINT_MAX; -} - -void ImGui_ImplWGPU_NewFrame(void) -{ - if (!g_pipelineState) - ImGui_ImplWGPU_CreateDeviceObjects(); -} diff --git a/libs/zgui/libs/imgui/imgui_internal.h b/libs/zgui/libs/imgui/imgui_internal.h deleted file mode 100644 index 81d41fd7b..000000000 --- a/libs/zgui/libs/imgui/imgui_internal.h +++ /dev/null @@ -1,2975 +0,0 @@ -// dear imgui, v1.88 -// (internal structures/api) - -// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! -// Set: -// #define IMGUI_DEFINE_MATH_OPERATORS -// To implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) - -/* - -Index of this file: - -// [SECTION] Header mess -// [SECTION] Forward declarations -// [SECTION] Context pointer -// [SECTION] STB libraries includes -// [SECTION] Macros -// [SECTION] Generic helpers -// [SECTION] ImDrawList support -// [SECTION] Widgets support: flags, enums, data structures -// [SECTION] Inputs support -// [SECTION] Clipper support -// [SECTION] Navigation support -// [SECTION] Columns support -// [SECTION] Multi-select support -// [SECTION] Docking support -// [SECTION] Viewport support -// [SECTION] Settings support -// [SECTION] Metrics, Debug tools -// [SECTION] Generic context hooks -// [SECTION] ImGuiContext (main imgui context) -// [SECTION] ImGuiWindowTempData, ImGuiWindow -// [SECTION] Tab bar, Tab item support -// [SECTION] Table support -// [SECTION] ImGui internal API -// [SECTION] ImFontAtlas internal API -// [SECTION] Test Engine specific hooks (imgui_test_engine) - -*/ - -#pragma once -#ifndef IMGUI_DISABLE - -//----------------------------------------------------------------------------- -// [SECTION] Header mess -//----------------------------------------------------------------------------- - -#ifndef IMGUI_VERSION -#include "imgui.h" -#endif - -#include // FILE*, sscanf -#include // NULL, malloc, free, qsort, atoi, atof -#include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf -#include // INT_MIN, INT_MAX - -// Enable SSE intrinsics if available -#if (defined __SSE__ || defined __x86_64__ || defined _M_X64) && !defined(IMGUI_DISABLE_SSE) -#define IMGUI_ENABLE_SSE -#include -#endif - -// Visual Studio warnings -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) -#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) -#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). -#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later -#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types -#endif -#endif - -// Clang/GCC warnings with -Weverything -#if defined(__clang__) -#pragma clang diagnostic push -#if __has_warning("-Wunknown-warning-option") -#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' -#endif -#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' -#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloorSigned() -#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h -#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h -#pragma clang diagnostic ignored "-Wold-style-cast" -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" -#pragma clang diagnostic ignored "-Wdouble-promotion" -#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision -#pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn' -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind -#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead -#endif - -// Legacy defines -#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Renamed in 1.74 -#error Use IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS -#endif -#ifdef IMGUI_DISABLE_MATH_FUNCTIONS // Renamed in 1.74 -#error Use IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS -#endif - -// Enable stb_truetype by default unless FreeType is enabled. -// You can compile with both by defining both IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE together. -#ifndef IMGUI_ENABLE_FREETYPE -#define IMGUI_ENABLE_STB_TRUETYPE -#endif - -//----------------------------------------------------------------------------- -// [SECTION] Forward declarations -//----------------------------------------------------------------------------- - -struct ImBitVector; // Store 1-bit per value -struct ImRect; // An axis-aligned rectangle (2 points) -struct ImDrawDataBuilder; // Helper to build a ImDrawData instance -struct ImDrawListSharedData; // Data shared between all ImDrawList instances -struct ImGuiColorMod; // Stacked color modifier, backup of modified data so we can restore it -struct ImGuiContext; // Main Dear ImGui context -struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine -struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum -struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() -struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box -struct ImGuiLastItemData; // Status storage for last submitted items -struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only -struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result -struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions -struct ImGuiNextWindowData; // Storage for SetNextWindow** functions -struct ImGuiNextItemData; // Storage for SetNextItem** functions -struct ImGuiOldColumnData; // Storage data for a single column for legacy Columns() api -struct ImGuiOldColumns; // Storage data for a columns set for legacy Columns() api -struct ImGuiPopupData; // Storage for current popup stack -struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file -struct ImGuiStackSizes; // Storage of stack sizes for debugging/asserting -struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it -struct ImGuiTabBar; // Storage for a tab bar -struct ImGuiTabItem; // Storage for a tab item (within a tab bar) -struct ImGuiTable; // Storage for a table -struct ImGuiTableColumn; // Storage for one column of a table -struct ImGuiTableInstanceData; // Storage for one instance of a same table -struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. -struct ImGuiTableSettings; // Storage for a table .ini settings -struct ImGuiTableColumnsSettings; // Storage for a column .ini settings -struct ImGuiWindow; // Storage for one window -struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) -struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) - -// Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. -typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical -typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) -typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags -typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() -typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags -typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() -typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() -typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // Flags: for GetNavInputAmount2d() -typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests -typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions -typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions -typedef int ImGuiScrollFlags; // -> enum ImGuiScrollFlags_ // Flags: for ScrollToItem() and navigation requests -typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx() -typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() -typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx() - -typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...); - -//----------------------------------------------------------------------------- -// [SECTION] Context pointer -// See implementation of this variable in imgui.cpp for comments and details. -//----------------------------------------------------------------------------- - -#ifndef GImGui -extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer -#endif - -//------------------------------------------------------------------------- -// [SECTION] STB libraries includes -//------------------------------------------------------------------------- - -namespace ImStb -{ - -#undef STB_TEXTEDIT_STRING -#undef STB_TEXTEDIT_CHARTYPE -#define STB_TEXTEDIT_STRING ImGuiInputTextState -#define STB_TEXTEDIT_CHARTYPE ImWchar -#define STB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f) -#define STB_TEXTEDIT_UNDOSTATECOUNT 99 -#define STB_TEXTEDIT_UNDOCHARCOUNT 999 -#include "imstb_textedit.h" - -} // namespace ImStb - -//----------------------------------------------------------------------------- -// [SECTION] Macros -//----------------------------------------------------------------------------- - -// Debug Printing Into TTY -// (since IMGUI_VERSION_NUM >= 18729: IMGUI_DEBUG_LOG was reworked into IMGUI_DEBUG_PRINTF (and removed framecount from it). If you were using a #define IMGUI_DEBUG_LOG please rename) -#ifndef IMGUI_DEBUG_PRINTF -#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS -#define IMGUI_DEBUG_PRINTF(_FMT,...) printf(_FMT, __VA_ARGS__) -#else -#define IMGUI_DEBUG_PRINTF(_FMT,...) -#endif -#endif - -// Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam. -#define IMGUI_DEBUG_LOG(...) ImGui::DebugLog(__VA_ARGS__); -#define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) -#define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) -#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) -#define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) -#define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) - -// Static Asserts -#define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") - -// "Paranoid" Debug Asserts are meant to only be enabled during specific debugging/work, otherwise would slow down the code too much. -// We currently don't have many of those so the effect is currently negligible, but onward intent to add more aggressive ones in the code. -//#define IMGUI_DEBUG_PARANOID -#ifdef IMGUI_DEBUG_PARANOID -#define IM_ASSERT_PARANOID(_EXPR) IM_ASSERT(_EXPR) -#else -#define IM_ASSERT_PARANOID(_EXPR) -#endif - -// Error handling -// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults. -#ifndef IM_ASSERT_USER_ERROR -#define IM_ASSERT_USER_ERROR(_EXP,_MSG) IM_ASSERT((_EXP) && _MSG) // Recoverable User Error -#endif - -// Misc Macros -#define IM_PI 3.14159265358979323846f -#ifdef _WIN32 -#define IM_NEWLINE "\r\n" // Play it nice with Windows users (Update: since 2018-05, Notepad finally appears to support Unix-style carriage returns!) -#else -#define IM_NEWLINE "\n" -#endif -#define IM_TABSIZE (4) -#define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8 -#define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose -#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 -#define IM_FLOOR(_VAL) ((float)(int)(_VAL)) // ImFloor() is not inlined in MSVC debug builds -#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // - -// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall -#ifdef _MSC_VER -#define IMGUI_CDECL __cdecl -#else -#define IMGUI_CDECL -#endif - -// Warnings -#if defined(_MSC_VER) && !defined(__clang__) -#define IM_MSVC_WARNING_SUPPRESS(XXXX) __pragma(warning(suppress: XXXX)) -#else -#define IM_MSVC_WARNING_SUPPRESS(XXXX) -#endif - -// Debug Tools -// Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item. -// This will call IM_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference. -#ifndef IM_DEBUG_BREAK -#if defined (_MSC_VER) -#define IM_DEBUG_BREAK() __debugbreak() -#elif defined(__clang__) -#define IM_DEBUG_BREAK() __builtin_debugtrap() -#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define IM_DEBUG_BREAK() __asm__ volatile("int $0x03") -#elif defined(__GNUC__) && defined(__thumb__) -#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01") -#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) -#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0"); -#else -#define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger! -#endif -#endif // #ifndef IM_DEBUG_BREAK - -//----------------------------------------------------------------------------- -// [SECTION] Generic helpers -// Note that the ImXXX helpers functions are lower-level than ImGui functions. -// ImGui functions or the ImGui context are never called/used from other ImXXX functions. -//----------------------------------------------------------------------------- -// - Helpers: Hashing -// - Helpers: Sorting -// - Helpers: Bit manipulation -// - Helpers: String -// - Helpers: Formatting -// - Helpers: UTF-8 <> wchar conversions -// - Helpers: ImVec2/ImVec4 operators -// - Helpers: Maths -// - Helpers: Geometry -// - Helper: ImVec1 -// - Helper: ImVec2ih -// - Helper: ImRect -// - Helper: ImBitArray -// - Helper: ImBitVector -// - Helper: ImSpan<>, ImSpanAllocator<> -// - Helper: ImPool<> -// - Helper: ImChunkStream<> -//----------------------------------------------------------------------------- - -// Helpers: Hashing -IMGUI_API ImGuiID ImHashData(const void* data, size_t data_size, ImU32 seed = 0); -IMGUI_API ImGuiID ImHashStr(const char* data, size_t data_size = 0, ImU32 seed = 0); - -// Helpers: Sorting -#ifndef ImQsort -static inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); } -#endif - -// Helpers: Color Blending -IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b); - -// Helpers: Bit manipulation -static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; } -static inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & (v - 1)) == 0; } -static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } - -// Helpers: String -IMGUI_API int ImStricmp(const char* str1, const char* str2); -IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); -IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); -IMGUI_API char* ImStrdup(const char* str); -IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); -IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); -IMGUI_API int ImStrlenW(const ImWchar* str); -IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line -IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line -IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); -IMGUI_API void ImStrTrimBlanks(char* str); -IMGUI_API const char* ImStrSkipBlank(const char* str); -static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } -static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } - -// Helpers: Formatting -IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3); -IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3); -IMGUI_API void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) IM_FMTARGS(3); -IMGUI_API void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) IM_FMTLIST(3); -IMGUI_API const char* ImParseFormatFindStart(const char* format); -IMGUI_API const char* ImParseFormatFindEnd(const char* format); -IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size); -IMGUI_API void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size); -IMGUI_API const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size); -IMGUI_API int ImParseFormatPrecision(const char* format, int default_value); - -// Helpers: UTF-8 <> wchar conversions -IMGUI_API const char* ImTextCharToUtf8(char out_buf[5], unsigned int c); // return out_buf -IMGUI_API int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count -IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count -IMGUI_API int ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count -IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) -IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 -IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 - -// Helpers: ImVec2/ImVec4 operators -// We are keeping those disabled by default so they don't leak in user space, to allow user enabling implicit cast operators between ImVec2 and their own types (using IM_VEC2_CLASS_EXTRA etc.) -// We unfortunately don't have a unary- operator for ImVec2 because this would needs to be defined inside the class itself. -#ifdef IMGUI_DEFINE_MATH_OPERATORS -IM_MSVC_RUNTIME_CHECKS_OFF -static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); } -static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); } -static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } -static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); } -static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } -static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x / rhs.x, lhs.y / rhs.y); } -static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; } -static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; } -static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; } -static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; } -static inline ImVec2& operator*=(ImVec2& lhs, const ImVec2& rhs) { lhs.x *= rhs.x; lhs.y *= rhs.y; return lhs; } -static inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs) { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; } -static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); } -static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); } -static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); } -IM_MSVC_RUNTIME_CHECKS_RESTORE -#endif - -// Helpers: File System -#ifdef IMGUI_DISABLE_FILE_FUNCTIONS -#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS -typedef void* ImFileHandle; -static inline ImFileHandle ImFileOpen(const char*, const char*) { return NULL; } -static inline bool ImFileClose(ImFileHandle) { return false; } -static inline ImU64 ImFileGetSize(ImFileHandle) { return (ImU64)-1; } -static inline ImU64 ImFileRead(void*, ImU64, ImU64, ImFileHandle) { return 0; } -static inline ImU64 ImFileWrite(const void*, ImU64, ImU64, ImFileHandle) { return 0; } -#endif -#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS -typedef FILE* ImFileHandle; -IMGUI_API ImFileHandle ImFileOpen(const char* filename, const char* mode); -IMGUI_API bool ImFileClose(ImFileHandle file); -IMGUI_API ImU64 ImFileGetSize(ImFileHandle file); -IMGUI_API ImU64 ImFileRead(void* data, ImU64 size, ImU64 count, ImFileHandle file); -IMGUI_API ImU64 ImFileWrite(const void* data, ImU64 size, ImU64 count, ImFileHandle file); -#else -#define IMGUI_DISABLE_TTY_FUNCTIONS // Can't use stdout, fflush if we are not using default file functions -#endif -IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0); - -// Helpers: Maths -IM_MSVC_RUNTIME_CHECKS_OFF -// - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy) -#ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS -#define ImFabs(X) fabsf(X) -#define ImSqrt(X) sqrtf(X) -#define ImFmod(X, Y) fmodf((X), (Y)) -#define ImCos(X) cosf(X) -#define ImSin(X) sinf(X) -#define ImAcos(X) acosf(X) -#define ImAtan2(Y, X) atan2f((Y), (X)) -#define ImAtof(STR) atof(STR) -//#define ImFloorStd(X) floorf(X) // We use our own, see ImFloor() and ImFloorSigned() -#define ImCeil(X) ceilf(X) -static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision -static inline double ImPow(double x, double y) { return pow(x, y); } -static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision -static inline double ImLog(double x) { return log(x); } -static inline int ImAbs(int x) { return x < 0 ? -x : x; } -static inline float ImAbs(float x) { return fabsf(x); } -static inline double ImAbs(double x) { return fabs(x); } -static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument -static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; } -#ifdef IMGUI_ENABLE_SSE -static inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } -#else -static inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); } -#endif -static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } -#endif -// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double -// (Exceptionally using templates here but we could also redefine them for those types) -template static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } -template static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } -template static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } -template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } -template static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } -template static inline T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; } -template static inline T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; } -// - Misc maths helpers -static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); } -static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); } -static inline ImVec2 ImClamp(const ImVec2& v, const ImVec2& mn, ImVec2 mx) { return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); } -static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); } -static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } -static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); } -static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } -static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } -static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } -static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } -static inline float ImFloor(float f) { return (float)(int)(f); } -static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() -static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } -static inline ImVec2 ImFloorSigned(const ImVec2& v) { return ImVec2(ImFloorSigned(v.x), ImFloorSigned(v.y)); } -static inline int ImModPositive(int a, int b) { return (a + b) % b; } -static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } -static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } -static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } -static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } -static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } -IM_MSVC_RUNTIME_CHECKS_RESTORE - -// Helpers: Geometry -IMGUI_API ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); -IMGUI_API ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments); // For curves with explicit number of segments -IMGUI_API ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol);// For auto-tessellated curves you can use tess_tol = style.CurveTessellationTol -IMGUI_API ImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t); -IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); -IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); -IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); -IMGUI_API void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w); -inline float ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; } -IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy); - -// Helper: ImVec1 (1D vector) -// (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) -IM_MSVC_RUNTIME_CHECKS_OFF -struct ImVec1 -{ - float x; - constexpr ImVec1() : x(0.0f) { } - constexpr ImVec1(float _x) : x(_x) { } -}; - -// Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage) -struct ImVec2ih -{ - short x, y; - constexpr ImVec2ih() : x(0), y(0) {} - constexpr ImVec2ih(short _x, short _y) : x(_x), y(_y) {} - constexpr explicit ImVec2ih(const ImVec2& rhs) : x((short)rhs.x), y((short)rhs.y) {} -}; - -// Helper: ImRect (2D axis aligned bounding-box) -// NB: we can't rely on ImVec2 math operators being available here! -struct IMGUI_API ImRect -{ - ImVec2 Min; // Upper-left - ImVec2 Max; // Lower-right - - constexpr ImRect() : Min(0.0f, 0.0f), Max(0.0f, 0.0f) {} - constexpr ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {} - constexpr ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {} - constexpr ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {} - - ImVec2 GetCenter() const { return ImVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); } - ImVec2 GetSize() const { return ImVec2(Max.x - Min.x, Max.y - Min.y); } - float GetWidth() const { return Max.x - Min.x; } - float GetHeight() const { return Max.y - Min.y; } - float GetArea() const { return (Max.x - Min.x) * (Max.y - Min.y); } - ImVec2 GetTL() const { return Min; } // Top-left - ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right - ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left - ImVec2 GetBR() const { return Max; } // Bottom-right - bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } - bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; } - bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } - void Add(const ImVec2& p) { if (Min.x > p.x) Min.x = p.x; if (Min.y > p.y) Min.y = p.y; if (Max.x < p.x) Max.x = p.x; if (Max.y < p.y) Max.y = p.y; } - void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; } - void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } - void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } - void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; } - void TranslateX(float dx) { Min.x += dx; Max.x += dx; } - void TranslateY(float dy) { Min.y += dy; Max.y += dy; } - void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. - void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. - void Floor() { Min.x = IM_FLOOR(Min.x); Min.y = IM_FLOOR(Min.y); Max.x = IM_FLOOR(Max.x); Max.y = IM_FLOOR(Max.y); } - bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } - ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } -}; -IM_MSVC_RUNTIME_CHECKS_RESTORE - -// Helper: ImBitArray -inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; } -inline void ImBitArrayClearBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; } -inline void ImBitArraySetBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; } -inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on range [n..n2) -{ - n2--; - while (n <= n2) - { - int a_mod = (n & 31); - int b_mod = (n2 > (n | 31) ? 31 : (n2 & 31)) + 1; - ImU32 mask = (ImU32)(((ImU64)1 << b_mod) - 1) & ~(ImU32)(((ImU64)1 << a_mod) - 1); - arr[n >> 5] |= mask; - n = (n + 32) & ~31; - } -} - -// Helper: ImBitArray class (wrapper over ImBitArray functions) -// Store 1-bit per value. -template -struct ImBitArray -{ - ImU32 Storage[(BITCOUNT + 31) >> 5]; - ImBitArray() { ClearAllBits(); } - void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } - void SetAllBits() { memset(Storage, 255, sizeof(Storage)); } - bool TestBit(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } - void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); } - void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Storage, n); } - void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2) - bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } -}; - -// Helper: ImBitVector -// Store 1-bit per value. -struct IMGUI_API ImBitVector -{ - ImVector Storage; - void Create(int sz) { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); } - void Clear() { Storage.clear(); } - bool TestBit(int n) const { IM_ASSERT(n < (Storage.Size << 5)); return ImBitArrayTestBit(Storage.Data, n); } - void SetBit(int n) { IM_ASSERT(n < (Storage.Size << 5)); ImBitArraySetBit(Storage.Data, n); } - void ClearBit(int n) { IM_ASSERT(n < (Storage.Size << 5)); ImBitArrayClearBit(Storage.Data, n); } -}; - -// Helper: ImSpan<> -// Pointing to a span of data we don't own. -template -struct ImSpan -{ - T* Data; - T* DataEnd; - - // Constructors, destructor - inline ImSpan() { Data = DataEnd = NULL; } - inline ImSpan(T* data, int size) { Data = data; DataEnd = data + size; } - inline ImSpan(T* data, T* data_end) { Data = data; DataEnd = data_end; } - - inline void set(T* data, int size) { Data = data; DataEnd = data + size; } - inline void set(T* data, T* data_end) { Data = data; DataEnd = data_end; } - inline int size() const { return (int)(ptrdiff_t)(DataEnd - Data); } - inline int size_in_bytes() const { return (int)(ptrdiff_t)(DataEnd - Data) * (int)sizeof(T); } - inline T& operator[](int i) { T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; } - inline const T& operator[](int i) const { const T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; } - - inline T* begin() { return Data; } - inline const T* begin() const { return Data; } - inline T* end() { return DataEnd; } - inline const T* end() const { return DataEnd; } - - // Utilities - inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < DataEnd); const ptrdiff_t off = it - Data; return (int)off; } -}; - -// Helper: ImSpanAllocator<> -// Facilitate storing multiple chunks into a single large block (the "arena") -// - Usage: call Reserve() N times, allocate GetArenaSizeInBytes() worth, pass it to SetArenaBasePtr(), call GetSpan() N times to retrieve the aligned ranges. -template -struct ImSpanAllocator -{ - char* BasePtr; - int CurrOff; - int CurrIdx; - int Offsets[CHUNKS]; - int Sizes[CHUNKS]; - - ImSpanAllocator() { memset(this, 0, sizeof(*this)); } - inline void Reserve(int n, size_t sz, int a=4) { IM_ASSERT(n == CurrIdx && n < CHUNKS); CurrOff = IM_MEMALIGN(CurrOff, a); Offsets[n] = CurrOff; Sizes[n] = (int)sz; CurrIdx++; CurrOff += (int)sz; } - inline int GetArenaSizeInBytes() { return CurrOff; } - inline void SetArenaBasePtr(void* base_ptr) { BasePtr = (char*)base_ptr; } - inline void* GetSpanPtrBegin(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n]); } - inline void* GetSpanPtrEnd(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n] + Sizes[n]); } - template - inline void GetSpan(int n, ImSpan* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); } -}; - -// Helper: ImPool<> -// Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer, -// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object. -typedef int ImPoolIdx; -template -struct ImPool -{ - ImVector Buf; // Contiguous data - ImGuiStorage Map; // ID->Index - ImPoolIdx FreeIdx; // Next free idx to use - ImPoolIdx AliveCount; // Number of active/alive items (for display purpose) - - ImPool() { FreeIdx = AliveCount = 0; } - ~ImPool() { Clear(); } - T* GetByKey(ImGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Buf[idx] : NULL; } - T* GetByIndex(ImPoolIdx n) { return &Buf[n]; } - ImPoolIdx GetIndex(const T* p) const { IM_ASSERT(p >= Buf.Data && p < Buf.Data + Buf.Size); return (ImPoolIdx)(p - Buf.Data); } - T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Buf[*p_idx]; *p_idx = FreeIdx; return Add(); } - bool Contains(const T* p) const { return (p >= Buf.Data && p < Buf.Data + Buf.Size); } - void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; } - T* Add() { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); AliveCount++; return &Buf[idx]; } - void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } - void Remove(ImGuiID key, ImPoolIdx idx) { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); AliveCount--; } - void Reserve(int capacity) { Buf.reserve(capacity); Map.Data.reserve(capacity); } - - // To iterate a ImPool: for (int n = 0; n < pool.GetMapSize(); n++) if (T* t = pool.TryGetMapData(n)) { ... } - // Can be avoided if you know .Remove() has never been called on the pool, or AliveCount == GetMapSize() - int GetAliveCount() const { return AliveCount; } // Number of active/alive items in the pool (for display purpose) - int GetBufSize() const { return Buf.Size; } - int GetMapSize() const { return Map.Data.Size; } // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere - T* TryGetMapData(ImPoolIdx n) { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); } -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - int GetSize() { return GetMapSize(); } // For ImPlot: should use GetMapSize() from (IMGUI_VERSION_NUM >= 18304) -#endif -}; - -// Helper: ImChunkStream<> -// Build and iterate a contiguous stream of variable-sized structures. -// This is used by Settings to store persistent data while reducing allocation count. -// We store the chunk size first, and align the final size on 4 bytes boundaries. -// The tedious/zealous amount of casting is to avoid -Wcast-align warnings. -template -struct ImChunkStream -{ - ImVector Buf; - - void clear() { Buf.clear(); } - bool empty() const { return Buf.Size == 0; } - int size() const { return Buf.Size; } - T* alloc_chunk(size_t sz) { size_t HDR_SZ = 4; sz = IM_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); } - T* begin() { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); } - T* next_chunk(T* p) { size_t HDR_SZ = 4; IM_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; IM_ASSERT(p < end()); return p; } - int chunk_size(const T* p) { return ((const int*)p)[-1]; } - T* end() { return (T*)(void*)(Buf.Data + Buf.Size); } - int offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; } - T* ptr_from_offset(int off) { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); } - void swap(ImChunkStream& rhs) { rhs.Buf.swap(Buf); } - -}; - -//----------------------------------------------------------------------------- -// [SECTION] ImDrawList support -//----------------------------------------------------------------------------- - -// ImDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value. -// Estimation of number of circle segment based on error is derived using method described in https://stackoverflow.com/a/2244088/15194693 -// Number of segments (N) is calculated using equation: -// N = ceil ( pi / acos(1 - error / r) ) where r > 0, error <= r -// Our equation is significantly simpler that one in the post thanks for choosing segment that is -// perpendicular to X axis. Follow steps in the article from this starting condition and you will -// will get this result. -// -// Rendering circles with an odd number of segments, while mathematically correct will produce -// asymmetrical results on the raster grid. Therefore we're rounding N to next even number (7->8, 8->8, 9->10 etc.) -#define IM_ROUNDUP_TO_EVEN(_V) ((((_V) + 1) / 2) * 2) -#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN 4 -#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX 512 -#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR) ImClamp(IM_ROUNDUP_TO_EVEN((int)ImCeil(IM_PI / ImAcos(1 - ImMin((_MAXERROR), (_RAD)) / (_RAD)))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX) - -// Raw equation from IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC rewritten for 'r' and 'error'. -#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N,_MAXERROR) ((_MAXERROR) / (1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI)))) -#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N,_RAD) ((1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))) / (_RAD)) - -// ImDrawList: Lookup table size for adaptive arc drawing, cover full circle. -#ifndef IM_DRAWLIST_ARCFAST_TABLE_SIZE -#define IM_DRAWLIST_ARCFAST_TABLE_SIZE 48 // Number of samples in lookup table. -#endif -#define IM_DRAWLIST_ARCFAST_SAMPLE_MAX IM_DRAWLIST_ARCFAST_TABLE_SIZE // Sample index _PathArcToFastEx() for 360 angle. - -// Data shared between all ImDrawList instances -// You may want to create your own instance of this if you want to use ImDrawList completely without ImGui. In that case, watch out for future changes to this structure. -struct IMGUI_API ImDrawListSharedData -{ - ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas - ImFont* Font; // Current/default font (optional, for simplified AddText overload) - float FontSize; // Current/default font size (optional, for simplified AddText overload) - float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() - float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc - ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() - ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) - - // [Internal] Lookup tables - ImVec2 ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle. - float ArcFastRadiusCutoff; // Cutoff radius after which arc drawing will fallback to slower PathArcTo() - ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead) - const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas - - ImDrawListSharedData(); - void SetCircleTessellationMaxError(float max_error); -}; - -struct ImDrawDataBuilder -{ - ImVector Layers[2]; // Global layers for: regular, tooltip - - void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } - void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } - int GetDrawListCount() const { int count = 0; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) count += Layers[n].Size; return count; } - IMGUI_API void FlattenIntoSingleLayer(); -}; - -//----------------------------------------------------------------------------- -// [SECTION] Widgets support: flags, enums, data structures -//----------------------------------------------------------------------------- - -// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). -// This is going to be exposed in imgui.h when stabilized enough. -enum ImGuiItemFlags_ -{ - ImGuiItemFlags_None = 0, - ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing (FIXME: should merge with _NoNav) - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // Disable interactions but doesn't affect visuals. See BeginDisabled()/EndDisabled(). See github.com/ocornut/imgui/issues/211 - ImGuiItemFlags_NoNav = 1 << 3, // false // Disable keyboard/gamepad directional navigation (FIXME: should merge with _NoTabStop) - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false // Disable item being a candidate for default focus (e.g. used by title bar items) - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window - ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) - ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. -}; - -// Storage for LastItem data -enum ImGuiItemStatusFlags_ -{ - ImGuiItemStatusFlags_None = 0, - ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test) - ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // g.LastItemData.DisplayRect is valid - ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) - ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues. - ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state. - ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. - ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. - ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. - ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8 // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon) - -#ifdef IMGUI_ENABLE_TEST_ENGINE - , // [imgui_tests only] - ImGuiItemStatusFlags_Openable = 1 << 20, // Item is an openable (e.g. TreeNode) - ImGuiItemStatusFlags_Opened = 1 << 21, // - ImGuiItemStatusFlags_Checkable = 1 << 22, // Item is a checkable (e.g. CheckBox, MenuItem) - ImGuiItemStatusFlags_Checked = 1 << 23 // -#endif -}; - -// Extend ImGuiInputTextFlags_ -enum ImGuiInputTextFlagsPrivate_ -{ - // [Internal] - ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() - ImGuiInputTextFlags_NoMarkEdited = 1 << 27, // For internal use by functions using InputText() before reformatting data - ImGuiInputTextFlags_MergedItem = 1 << 28 // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. -}; - -// Extend ImGuiButtonFlags_ -enum ImGuiButtonFlagsPrivate_ -{ - ImGuiButtonFlags_PressedOnClick = 1 << 4, // return true on click (mouse down event) - ImGuiButtonFlags_PressedOnClickRelease = 1 << 5, // [Default] return true on click + release on same item <-- this is what the majority of Button are using - ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6, // return true on click + release even if the release event is not done while hovering the item - ImGuiButtonFlags_PressedOnRelease = 1 << 7, // return true on release (default requires click+release) - ImGuiButtonFlags_PressedOnDoubleClick = 1 << 8, // return true on double-click (default requires click+release) - ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9, // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers) - ImGuiButtonFlags_Repeat = 1 << 10, // hold to repeat - ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping - ImGuiButtonFlags_AllowItemOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap() - ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press // [UNUSED] - //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled - ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine - ImGuiButtonFlags_NoKeyModifiers = 1 << 16, // disable mouse interaction if a key modifier is held - ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) - ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated - ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item - ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold, - ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease -}; - -// Extend ImGuiComboFlags_ -enum ImGuiComboFlagsPrivate_ -{ - ImGuiComboFlags_CustomPreview = 1 << 20 // enable BeginComboPreview() -}; - -// Extend ImGuiSliderFlags_ -enum ImGuiSliderFlagsPrivate_ -{ - ImGuiSliderFlags_Vertical = 1 << 20, // Should this slider be orientated vertically? - ImGuiSliderFlags_ReadOnly = 1 << 21 -}; - -// Extend ImGuiSelectableFlags_ -enum ImGuiSelectableFlagsPrivate_ -{ - // NB: need to be in sync with last value of ImGuiSelectableFlags_ - ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, - ImGuiSelectableFlags_SelectOnNav = 1 << 21, // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API. - ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release) - ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release) - ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) - ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 25, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. - ImGuiSelectableFlags_SetNavIdOnHover = 1 << 26, // Set Nav/Focus ID on mouse hover (used by MenuItem) - ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 27 // Disable padding each side with ItemSpacing * 0.5f -}; - -// Extend ImGuiTreeNodeFlags_ -enum ImGuiTreeNodeFlagsPrivate_ -{ - ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20 -}; - -enum ImGuiSeparatorFlags_ -{ - ImGuiSeparatorFlags_None = 0, - ImGuiSeparatorFlags_Horizontal = 1 << 0, // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar - ImGuiSeparatorFlags_Vertical = 1 << 1, - ImGuiSeparatorFlags_SpanAllColumns = 1 << 2 -}; - -enum ImGuiTextFlags_ -{ - ImGuiTextFlags_None = 0, - ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0 -}; - -enum ImGuiTooltipFlags_ -{ - ImGuiTooltipFlags_None = 0, - ImGuiTooltipFlags_OverridePreviousTooltip = 1 << 0 // Override will clear/ignore previously submitted tooltip (defaults to append) -}; - -// FIXME: this is in development, not exposed/functional as a generic feature yet. -// Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2 -enum ImGuiLayoutType_ -{ - ImGuiLayoutType_Horizontal = 0, - ImGuiLayoutType_Vertical = 1 -}; - -enum ImGuiLogType -{ - ImGuiLogType_None = 0, - ImGuiLogType_TTY, - ImGuiLogType_File, - ImGuiLogType_Buffer, - ImGuiLogType_Clipboard -}; - -// X/Y enums are fixed to 0/1 so they may be used to index ImVec2 -enum ImGuiAxis -{ - ImGuiAxis_None = -1, - ImGuiAxis_X = 0, - ImGuiAxis_Y = 1 -}; - -enum ImGuiPlotType -{ - ImGuiPlotType_Lines, - ImGuiPlotType_Histogram -}; - -enum ImGuiPopupPositionPolicy -{ - ImGuiPopupPositionPolicy_Default, - ImGuiPopupPositionPolicy_ComboBox, - ImGuiPopupPositionPolicy_Tooltip -}; - -struct ImGuiDataTypeTempStorage -{ - ImU8 Data[8]; // Can fit any data up to ImGuiDataType_COUNT -}; - -// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo(). -struct ImGuiDataTypeInfo -{ - size_t Size; // Size in bytes - const char* Name; // Short descriptive name for the type, for debugging - const char* PrintFmt; // Default printf format for the type - const char* ScanFmt; // Default scanf format for the type -}; - -// Extend ImGuiDataType_ -enum ImGuiDataTypePrivate_ -{ - ImGuiDataType_String = ImGuiDataType_COUNT + 1, - ImGuiDataType_Pointer, - ImGuiDataType_ID -}; - -// Stacked color modifier, backup of modified data so we can restore it -struct ImGuiColorMod -{ - ImGuiCol Col; - ImVec4 BackupValue; -}; - -// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable. -struct ImGuiStyleMod -{ - ImGuiStyleVar VarIdx; - union { int BackupInt[2]; float BackupFloat[2]; }; - ImGuiStyleMod(ImGuiStyleVar idx, int v) { VarIdx = idx; BackupInt[0] = v; } - ImGuiStyleMod(ImGuiStyleVar idx, float v) { VarIdx = idx; BackupFloat[0] = v; } - ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } -}; - -// Storage data for BeginComboPreview()/EndComboPreview() -struct IMGUI_API ImGuiComboPreviewData -{ - ImRect PreviewRect; - ImVec2 BackupCursorPos; - ImVec2 BackupCursorMaxPos; - ImVec2 BackupCursorPosPrevLine; - float BackupPrevLineTextBaseOffset; - ImGuiLayoutType BackupLayout; - - ImGuiComboPreviewData() { memset(this, 0, sizeof(*this)); } -}; - -// Stacked storage data for BeginGroup()/EndGroup() -struct IMGUI_API ImGuiGroupData -{ - ImGuiID WindowID; - ImVec2 BackupCursorPos; - ImVec2 BackupCursorMaxPos; - ImVec1 BackupIndent; - ImVec1 BackupGroupOffset; - ImVec2 BackupCurrLineSize; - float BackupCurrLineTextBaseOffset; - ImGuiID BackupActiveIdIsAlive; - bool BackupActiveIdPreviousFrameIsAlive; - bool BackupHoveredIdIsAlive; - bool EmitItem; -}; - -// Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper. -struct IMGUI_API ImGuiMenuColumns -{ - ImU32 TotalWidth; - ImU32 NextTotalWidth; - ImU16 Spacing; - ImU16 OffsetIcon; // Always zero for now - ImU16 OffsetLabel; // Offsets are locked in Update() - ImU16 OffsetShortcut; - ImU16 OffsetMark; - ImU16 Widths[4]; // Width of: Icon, Label, Shortcut, Mark (accumulators for current frame) - - ImGuiMenuColumns() { memset(this, 0, sizeof(*this)); } - void Update(float spacing, bool window_reappearing); - float DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark); - void CalcNextTotalWidth(bool update_offsets); -}; - -// Internal state of the currently focused/edited text input box -// For a given item ID, access with ImGui::GetInputTextState() -struct IMGUI_API ImGuiInputTextState -{ - ImGuiID ID; // widget id owning the text state - int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not. - ImVector TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. - ImVector TextA; // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity. - ImVector InitialTextA; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) - bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument) - int BufCapacityA; // end-user buffer capacity - float ScrollX; // horizontal scrolling/offset - ImStb::STB_TexteditState Stb; // state for stb_textedit.h - float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately - bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) - bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection - bool Edited; // edited this frame - ImGuiInputTextFlags Flags; // copy of InputText() flags - - ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } - void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } - void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); } - int GetUndoAvailCount() const { return Stb.undostate.undo_point; } - int GetRedoAvailCount() const { return STB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; } - void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation - - // Cursor & Selection - void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking - void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); } - bool HasSelection() const { return Stb.select_start != Stb.select_end; } - void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; } - int GetCursorPos() const { return Stb.cursor; } - int GetSelectionStart() const { return Stb.select_start; } - int GetSelectionEnd() const { return Stb.select_end; } - void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } -}; - -// Storage for current popup stack -struct ImGuiPopupData -{ - ImGuiID PopupId; // Set on OpenPopup() - ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() - ImGuiWindow* SourceWindow; // Set on OpenPopup() copy of NavWindow at the time of opening the popup - int ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value - int OpenFrameCount; // Set on OpenPopup() - ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) - ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) - ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup - - ImGuiPopupData() { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; } -}; - -enum ImGuiNextWindowDataFlags_ -{ - ImGuiNextWindowDataFlags_None = 0, - ImGuiNextWindowDataFlags_HasPos = 1 << 0, - ImGuiNextWindowDataFlags_HasSize = 1 << 1, - ImGuiNextWindowDataFlags_HasContentSize = 1 << 2, - ImGuiNextWindowDataFlags_HasCollapsed = 1 << 3, - ImGuiNextWindowDataFlags_HasSizeConstraint = 1 << 4, - ImGuiNextWindowDataFlags_HasFocus = 1 << 5, - ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6, - ImGuiNextWindowDataFlags_HasScroll = 1 << 7 -}; - -// Storage for SetNexWindow** functions -struct ImGuiNextWindowData -{ - ImGuiNextWindowDataFlags Flags; - ImGuiCond PosCond; - ImGuiCond SizeCond; - ImGuiCond CollapsedCond; - ImVec2 PosVal; - ImVec2 PosPivotVal; - ImVec2 SizeVal; - ImVec2 ContentSizeVal; - ImVec2 ScrollVal; - bool CollapsedVal; - ImRect SizeConstraintRect; - ImGuiSizeCallback SizeCallback; - void* SizeCallbackUserData; - float BgAlphaVal; // Override background alpha - ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) - - ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); } - inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } -}; - -enum ImGuiNextItemDataFlags_ -{ - ImGuiNextItemDataFlags_None = 0, - ImGuiNextItemDataFlags_HasWidth = 1 << 0, - ImGuiNextItemDataFlags_HasOpen = 1 << 1 -}; - -struct ImGuiNextItemData -{ - ImGuiNextItemDataFlags Flags; - float Width; // Set by SetNextItemWidth() - ImGuiID FocusScopeId; // Set by SetNextItemMultiSelectData() (!= 0 signify value has been set, so it's an alternate version of HasSelectionData, we don't use Flags for this because they are cleared too early. This is mostly used for debugging) - ImGuiCond OpenCond; - bool OpenVal; // Set by SetNextItemOpen() - - ImGuiNextItemData() { memset(this, 0, sizeof(*this)); } - inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; } // Also cleared manually by ItemAdd()! -}; - -// Status storage for the last submitted item -struct ImGuiLastItemData -{ - ImGuiID ID; - ImGuiItemFlags InFlags; // See ImGuiItemFlags_ - ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_ - ImRect Rect; // Full rectangle - ImRect NavRect; // Navigation scoring rectangle (not displayed) - ImRect DisplayRect; // Display rectangle (only if ImGuiItemStatusFlags_HasDisplayRect is set) - - ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } -}; - -struct IMGUI_API ImGuiStackSizes -{ - short SizeOfIDStack; - short SizeOfColorStack; - short SizeOfStyleVarStack; - short SizeOfFontStack; - short SizeOfFocusScopeStack; - short SizeOfGroupStack; - short SizeOfItemFlagsStack; - short SizeOfBeginPopupStack; - short SizeOfDisabledStack; - - ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } - void SetToCurrentState(); - void CompareWithCurrentState(); -}; - -// Data saved for each window pushed into the stack -struct ImGuiWindowStackData -{ - ImGuiWindow* Window; - ImGuiLastItemData ParentLastItemDataBackup; - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting -}; - -struct ImGuiShrinkWidthItem -{ - int Index; - float Width; - float InitialWidth; -}; - -struct ImGuiPtrOrIndex -{ - void* Ptr; // Either field can be set, not both. e.g. Dock node tab bars are loose while BeginTabBar() ones are in a pool. - int Index; // Usually index in a main pool. - - ImGuiPtrOrIndex(void* ptr) { Ptr = ptr; Index = -1; } - ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Inputs support -//----------------------------------------------------------------------------- - -typedef ImBitArray ImBitArrayForNamedKeys; - -enum ImGuiKeyPrivate_ -{ - ImGuiKey_LegacyNativeKey_BEGIN = 0, - ImGuiKey_LegacyNativeKey_END = 512, - ImGuiKey_Gamepad_BEGIN = ImGuiKey_GamepadStart, - ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1 -}; - -enum ImGuiInputEventType -{ - ImGuiInputEventType_None = 0, - ImGuiInputEventType_MousePos, - ImGuiInputEventType_MouseWheel, - ImGuiInputEventType_MouseButton, - ImGuiInputEventType_Key, - ImGuiInputEventType_Text, - ImGuiInputEventType_Focus, - ImGuiInputEventType_COUNT -}; - -enum ImGuiInputSource -{ - ImGuiInputSource_None = 0, - ImGuiInputSource_Mouse, - ImGuiInputSource_Keyboard, - ImGuiInputSource_Gamepad, - ImGuiInputSource_Clipboard, // Currently only used by InputText() - ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only - ImGuiInputSource_COUNT -}; - -// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension? -// Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor' -struct ImGuiInputEventMousePos { float PosX, PosY; }; -struct ImGuiInputEventMouseWheel { float WheelX, WheelY; }; -struct ImGuiInputEventMouseButton { int Button; bool Down; }; -struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; -struct ImGuiInputEventText { unsigned int Char; }; -struct ImGuiInputEventAppFocused { bool Focused; }; - -struct ImGuiInputEvent -{ - ImGuiInputEventType Type; - ImGuiInputSource Source; - union - { - ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos - ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel - ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton - ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key - ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text - ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus - }; - bool AddedByTestEngine; - - ImGuiInputEvent() { memset(this, 0, sizeof(*this)); } -}; - -// FIXME-NAV: Clarify/expose various repeat delay/rate -enum ImGuiNavReadMode -{ - ImGuiNavReadMode_Down, - ImGuiNavReadMode_Pressed, - ImGuiNavReadMode_Released, - ImGuiNavReadMode_Repeat, - ImGuiNavReadMode_RepeatSlow, - ImGuiNavReadMode_RepeatFast -}; - -//----------------------------------------------------------------------------- -// [SECTION] Clipper support -//----------------------------------------------------------------------------- - -struct ImGuiListClipperRange -{ - int Min; - int Max; - bool PosToIndexConvert; // Begin/End are absolute position (will be converted to indices later) - ImS8 PosToIndexOffsetMin; // Add to Min after converting to indices - ImS8 PosToIndexOffsetMax; // Add to Min after converting to indices - - static ImGuiListClipperRange FromIndices(int min, int max) { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; } - static ImGuiListClipperRange FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; } -}; - -// Temporary clipper data, buffers shared/reused between instances -struct ImGuiListClipperData -{ - ImGuiListClipper* ListClipper; - float LossynessOffset; - int StepNo; - int ItemsFrozen; - ImVector Ranges; - - ImGuiListClipperData() { memset(this, 0, sizeof(*this)); } - void Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Navigation support -//----------------------------------------------------------------------------- - -enum ImGuiActivateFlags_ -{ - ImGuiActivateFlags_None = 0, - ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default if keyboard is available. - ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default if keyboard is not available. - ImGuiActivateFlags_TryToPreserveState = 1 << 2 // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) -}; - -// Early work-in-progress API for ScrollToItem() -enum ImGuiScrollFlags_ -{ - ImGuiScrollFlags_None = 0, - ImGuiScrollFlags_KeepVisibleEdgeX = 1 << 0, // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis] - ImGuiScrollFlags_KeepVisibleEdgeY = 1 << 1, // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible] - ImGuiScrollFlags_KeepVisibleCenterX = 1 << 2, // If item is not visible: scroll to make the item centered on X axis [rarely used] - ImGuiScrollFlags_KeepVisibleCenterY = 1 << 3, // If item is not visible: scroll to make the item centered on Y axis - ImGuiScrollFlags_AlwaysCenterX = 1 << 4, // Always center the result item on X axis [rarely used] - ImGuiScrollFlags_AlwaysCenterY = 1 << 5, // Always center the result item on Y axis [default for Y axis for appearing window) - ImGuiScrollFlags_NoScrollParent = 1 << 6, // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to). - ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX, - ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY -}; - -enum ImGuiNavHighlightFlags_ -{ - ImGuiNavHighlightFlags_None = 0, - ImGuiNavHighlightFlags_TypeDefault = 1 << 0, - ImGuiNavHighlightFlags_TypeThin = 1 << 1, - ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. - ImGuiNavHighlightFlags_NoRounding = 1 << 3 -}; - -enum ImGuiNavDirSourceFlags_ -{ - ImGuiNavDirSourceFlags_None = 0, - ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), facilitate use of some functions before we can unify nav and keys - ImGuiNavDirSourceFlags_Keyboard = 1 << 1, - ImGuiNavDirSourceFlags_PadDPad = 1 << 2, - ImGuiNavDirSourceFlags_PadLStick = 1 << 3 -}; - -enum ImGuiNavMoveFlags_ -{ - ImGuiNavMoveFlags_None = 0, - ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side - ImGuiNavMoveFlags_LoopY = 1 << 1, - ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) - ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness - ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) - ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) - ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary - ImGuiNavMoveFlags_Forwarded = 1 << 7, - ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result - ImGuiNavMoveFlags_FocusApi = 1 << 9, - ImGuiNavMoveFlags_Tabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight - ImGuiNavMoveFlags_Activate = 1 << 11, - ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 12 // Do not alter the visible state of keyboard vs mouse nav highlight -}; - -enum ImGuiNavLayer -{ - ImGuiNavLayer_Main = 0, // Main scrolling layer - ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) - ImGuiNavLayer_COUNT -}; - -struct ImGuiNavItemData -{ - ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window) - ImGuiID ID; // Init,Move // Best candidate item ID - ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID - ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space - ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags - float DistBox; // Move // Best candidate box distance to current NavId - float DistCenter; // Move // Best candidate center distance to current NavId - float DistAxial; // Move // Best candidate axial distance to current NavId - - ImGuiNavItemData() { Clear(); } - void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Columns support -//----------------------------------------------------------------------------- - -// Flags for internal's BeginColumns(). Prefix using BeginTable() nowadays! -enum ImGuiOldColumnFlags_ -{ - ImGuiOldColumnFlags_None = 0, - ImGuiOldColumnFlags_NoBorder = 1 << 0, // Disable column dividers - ImGuiOldColumnFlags_NoResize = 1 << 1, // Disable resizing columns when clicking on the dividers - ImGuiOldColumnFlags_NoPreserveWidths = 1 << 2, // Disable column width preservation when adjusting columns - ImGuiOldColumnFlags_NoForceWithinWindow = 1 << 3, // Disable forcing columns to fit within window - ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4 // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove. - - // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiColumnsFlags_None = ImGuiOldColumnFlags_None, - ImGuiColumnsFlags_NoBorder = ImGuiOldColumnFlags_NoBorder, - ImGuiColumnsFlags_NoResize = ImGuiOldColumnFlags_NoResize, - ImGuiColumnsFlags_NoPreserveWidths = ImGuiOldColumnFlags_NoPreserveWidths, - ImGuiColumnsFlags_NoForceWithinWindow = ImGuiOldColumnFlags_NoForceWithinWindow, - ImGuiColumnsFlags_GrowParentContentsSize = ImGuiOldColumnFlags_GrowParentContentsSize -#endif -}; - -struct ImGuiOldColumnData -{ - float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right) - float OffsetNormBeforeResize; - ImGuiOldColumnFlags Flags; // Not exposed - ImRect ClipRect; - - ImGuiOldColumnData() { memset(this, 0, sizeof(*this)); } -}; - -struct ImGuiOldColumns -{ - ImGuiID ID; - ImGuiOldColumnFlags Flags; - bool IsFirstFrame; - bool IsBeingResized; - int Current; - int Count; - float OffMinX, OffMaxX; // Offsets from HostWorkRect.Min.x - float LineMinY, LineMaxY; - float HostCursorPosY; // Backup of CursorPos at the time of BeginColumns() - float HostCursorMaxPosX; // Backup of CursorMaxPos at the time of BeginColumns() - ImRect HostInitialClipRect; // Backup of ClipRect at the time of BeginColumns() - ImRect HostBackupClipRect; // Backup of ClipRect during PushColumnsBackground()/PopColumnsBackground() - ImRect HostBackupParentWorkRect;//Backup of WorkRect at the time of BeginColumns() - ImVector Columns; - ImDrawListSplitter Splitter; - - ImGuiOldColumns() { memset(this, 0, sizeof(*this)); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Multi-select support -//----------------------------------------------------------------------------- - -#ifdef IMGUI_HAS_MULTI_SELECT -// -#endif // #ifdef IMGUI_HAS_MULTI_SELECT - -//----------------------------------------------------------------------------- -// [SECTION] Docking support -//----------------------------------------------------------------------------- - -#ifdef IMGUI_HAS_DOCK -// -#endif // #ifdef IMGUI_HAS_DOCK - -//----------------------------------------------------------------------------- -// [SECTION] Viewport support -//----------------------------------------------------------------------------- - -// ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!) -// Every instance of ImGuiViewport is in fact a ImGuiViewportP. -struct ImGuiViewportP : public ImGuiViewport -{ - int DrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used - ImDrawList* DrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. - ImDrawData DrawDataP; - ImDrawDataBuilder DrawDataBuilder; - - ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) - ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). - ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f. - ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f. - - ImGuiViewportP() { DrawListsLastFrame[0] = DrawListsLastFrame[1] = -1; DrawLists[0] = DrawLists[1] = NULL; } - ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } - - // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) - ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); } - ImVec2 CalcWorkRectSize(const ImVec2& off_min, const ImVec2& off_max) const { return ImVec2(ImMax(0.0f, Size.x - off_min.x + off_max.x), ImMax(0.0f, Size.y - off_min.y + off_max.y)); } - void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields - - // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry) - ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } - ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Settings support -//----------------------------------------------------------------------------- - -// Windows data saved in imgui.ini file -// Because we never destroy or rename ImGuiWindowSettings, we can store the names in a separate buffer easily. -// (this is designed to be stored in a ImChunkStream buffer, with the variable-length Name following our structure) -struct ImGuiWindowSettings -{ - ImGuiID ID; - ImVec2ih Pos; - ImVec2ih Size; - bool Collapsed; - bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) - - ImGuiWindowSettings() { memset(this, 0, sizeof(*this)); } - char* GetName() { return (char*)(this + 1); } -}; - -struct ImGuiSettingsHandler -{ - const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' - ImGuiID TypeHash; // == ImHashStr(TypeName) - void (*ClearAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Clear all settings data - void (*ReadInitFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called before reading (in registration order) - void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]" - void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry - void (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called after reading (in registration order) - void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' - void* UserData; - - ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Metrics, Debug Tools -//----------------------------------------------------------------------------- - -enum ImGuiDebugLogFlags_ -{ - // Event types - ImGuiDebugLogFlags_None = 0, - ImGuiDebugLogFlags_EventActiveId = 1 << 0, - ImGuiDebugLogFlags_EventFocus = 1 << 1, - ImGuiDebugLogFlags_EventPopup = 1 << 2, - ImGuiDebugLogFlags_EventNav = 1 << 3, - ImGuiDebugLogFlags_EventIO = 1 << 4, - ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventIO, - ImGuiDebugLogFlags_OutputToTTY = 1 << 10 // Also send output to TTY -}; - -struct ImGuiMetricsConfig -{ - bool ShowDebugLog; - bool ShowStackTool; - bool ShowWindowsRects; - bool ShowWindowsBeginOrder; - bool ShowTablesRects; - bool ShowDrawCmdMesh; - bool ShowDrawCmdBoundingBoxes; - int ShowWindowsRectsType; - int ShowTablesRectsType; - - ImGuiMetricsConfig() - { - ShowDebugLog = ShowStackTool = ShowWindowsRects = ShowWindowsBeginOrder = ShowTablesRects = false; - ShowDrawCmdMesh = true; - ShowDrawCmdBoundingBoxes = true; - ShowWindowsRectsType = ShowTablesRectsType = -1; - } -}; - -struct ImGuiStackLevelInfo -{ - ImGuiID ID; - ImS8 QueryFrameCount; // >= 1: Query in progress - bool QuerySuccess; // Obtained result from DebugHookIdInfo() - ImGuiDataType DataType : 8; - char Desc[57]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) FIXME: Now that we added CTRL+C this should be fixed. - - ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); } -}; - -// State for Stack tool queries -struct ImGuiStackTool -{ - int LastActiveFrame; - int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level - ImGuiID QueryId; // ID to query details for - ImVector Results; - bool CopyToClipboardOnCtrlC; - float CopyToClipboardLastTime; - - ImGuiStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Generic context hooks -//----------------------------------------------------------------------------- - -typedef void (*ImGuiContextHookCallback)(ImGuiContext* ctx, ImGuiContextHook* hook); -enum ImGuiContextHookType { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown, ImGuiContextHookType_PendingRemoval_ }; - -struct ImGuiContextHook -{ - ImGuiID HookId; // A unique ID assigned by AddContextHook() - ImGuiContextHookType Type; - ImGuiID Owner; - ImGuiContextHookCallback Callback; - void* UserData; - - ImGuiContextHook() { memset(this, 0, sizeof(*this)); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] ImGuiContext (main Dear ImGui context) -//----------------------------------------------------------------------------- - -struct ImGuiContext -{ - bool Initialized; - bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it. - ImGuiIO IO; - ImVector InputEventsQueue; // Input events which will be tricked/written into IO structure. - ImVector InputEventsTrail; // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail. - ImGuiStyle Style; - ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() - float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. - float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. - ImDrawListSharedData DrawListSharedData; - double Time; - int FrameCount; - int FrameCountEnded; - int FrameCountRendered; - bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame() - bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed - bool WithinEndChild; // Set within EndChild() - bool GcCompactAll; // Request full GC - bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() - void* TestEngine; // Test engine user data - - // Windows state - ImVector Windows; // Windows, sorted in display order, back to front - ImVector WindowsFocusOrder; // Root windows, sorted in focus order, back to front. - ImVector WindowsTempSortBuffer; // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child - ImVector CurrentWindowStack; - ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow* - int WindowsActiveCount; // Number of unique windows submitted by frame - ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING) - ImGuiWindow* CurrentWindow; // Window being drawn into - ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. - ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. - ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow. - ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. - ImVec2 WheelingWindowRefMousePos; - float WheelingWindowTimer; - - // Item/widgets state and tracking information - ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] - ImGuiID HoveredId; // Hovered widget, filled during the frame - ImGuiID HoveredIdPreviousFrame; - bool HoveredIdAllowOverlap; - bool HoveredIdUsingMouseWheel; // Hovered widget will use mouse wheel. Blocks scrolling the underlying window. - bool HoveredIdPreviousFrameUsingMouseWheel; - bool HoveredIdDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0. - float HoveredIdTimer; // Measure contiguous hovering time - float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active - ImGuiID ActiveId; // Active widget - ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame) - float ActiveIdTimer; - bool ActiveIdIsJustActivated; // Set at the time of activation for one frame - bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always) - bool ActiveIdNoClearOnFocusLoss; // Disable losing active id if the active id window gets unfocused. - bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch. - bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state. - bool ActiveIdHasBeenEditedThisFrame; - ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) - ImGuiWindow* ActiveIdWindow; - ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) - int ActiveIdMouseButton; - ImGuiID ActiveIdPreviousFrame; - bool ActiveIdPreviousFrameIsAlive; - bool ActiveIdPreviousFrameHasBeenEditedBefore; - ImGuiWindow* ActiveIdPreviousFrameWindow; - ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation. - float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. - - // Input Ownership - bool ActiveIdUsingMouseWheel; // Active widget will want to read mouse wheel. Blocks scrolling the underlying window. - ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) - ImU32 ActiveIdUsingNavInputMask; // Active widget will want to read those nav inputs. - ImBitArrayForNamedKeys ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array. - - // Next window/item data - ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back() - ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions - ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd) - ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions - - // Shared stacks - ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() - ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin() - ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() - ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - not inherited by Begin(), unless child window - ImVectorItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin() - ImVectorGroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() - ImVectorOpenPopupStack; // Which popups are open (persistent) - ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) - int BeginMenuCount; - - // Viewports - ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. - - // Gamepad/keyboard Navigation - ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' - ImGuiID NavId; // Focused item for navigation - ImGuiID NavFocusScopeId; // Identify a selection scope (selection code often wants to "clear other items" when landing on an item of the selection set) - ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem() - ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavActivateInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0; ImGuiActivateFlags_PreferInput will be set and NavActivateId will be 0. - ImGuiActivateFlags NavActivateFlags; - ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). - ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). - ImGuiModFlags NavJustMovedToKeyMods; - ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. - ImGuiActivateFlags NavNextActivateFlags; - ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. - ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. - bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid - bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default) - bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) - bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. - - // Navigation: Init & Move Requests - bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd() - bool NavInitRequest; // Init request for appearing window to select first item - bool NavInitRequestFromMove; - ImGuiID NavInitResultId; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) - ImRect NavInitResultRectRel; // Init request result rectangle (relative to parent window) - bool NavMoveSubmitted; // Move request submitted, will process result on next NewFrame() - bool NavMoveScoringItems; // Move request submitted, still scoring incoming items - bool NavMoveForwardToNextFrame; - ImGuiNavMoveFlags NavMoveFlags; - ImGuiScrollFlags NavMoveScrollFlags; - ImGuiModFlags NavMoveKeyMods; - ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) - ImGuiDir NavMoveDirForDebug; - ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? - ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. - ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted - int NavScoringDebugCount; // Metrics for debugging - int NavTabbingDir; // Generally -1 or +1, 0 when tabbing without a nav id - int NavTabbingCounter; // >0 when counting items for tabbing - ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow - ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) - ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) - ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy - - // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize) - ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! - ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it. - ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents - float NavWindowingTimer; - float NavWindowingHighlightAlpha; - bool NavWindowingToggleLayer; - - // Render - float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) - ImGuiMouseCursor MouseCursor; - - // Drag and Drop - bool DragDropActive; - bool DragDropWithinSource; // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag source. - bool DragDropWithinTarget; // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag target. - ImGuiDragDropFlags DragDropSourceFlags; - int DragDropSourceFrameCount; - int DragDropMouseButton; - ImGuiPayload DragDropPayload; - ImRect DragDropTargetRect; // Store rectangle of current target candidate (we favor small targets when overlapping) - ImGuiID DragDropTargetId; - ImGuiDragDropFlags DragDropAcceptFlags; - float DragDropAcceptIdCurrRectSurface; // Target item surface (we resolve overlapping targets by prioritizing the smaller surface) - ImGuiID DragDropAcceptIdCurr; // Target item id (set at the time of accepting the payload) - ImGuiID DragDropAcceptIdPrev; // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets) - int DragDropAcceptFrameCount; // Last time a target expressed a desire to accept the source - ImGuiID DragDropHoldJustPressedId; // Set when holding a payload just made ButtonBehavior() return a press. - ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size - unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads - - // Clipper - int ClipperTempDataStacked; - ImVector ClipperTempData; - - // Tables - ImGuiTable* CurrentTable; - int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size) - ImVector TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting) - ImPool Tables; // Persistent table data - ImVector TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC) - ImVector DrawChannelsTempMergeBuffer; - - // Tab bars - ImGuiTabBar* CurrentTabBar; - ImPool TabBars; - ImVector CurrentTabBarStack; - ImVector ShrinkWidthBuffer; - - // Widget state - ImVec2 MouseLastValidPos; - ImGuiInputTextState InputTextState; - ImFont InputTextPasswordFont; - ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. - ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets - float ColorEditLastHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips - float ColorEditLastSat; // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips - ImU32 ColorEditLastColor; // RGB value with alpha set to 0. - ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker. - ImGuiComboPreviewData ComboPreviewData; - float SliderGrabClickOffset; - float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. - bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? - bool DragCurrentAccumDirty; - float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings - float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio - float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? - float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() - short DisabledStackSize; - short TooltipOverrideCount; - float TooltipSlowDelay; // Time before slow tooltips appears (FIXME: This is temporary until we merge in tooltip timer+priority work) - ImVector ClipboardHandlerData; // If no custom clipboard handler is defined - ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once - - // Platform support - ImGuiPlatformImeData PlatformImeData; // Data updated by current frame - ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data (when changing we will call io.SetPlatformImeDataFn - char PlatformLocaleDecimalPoint; // '.' or *localeconv()->decimal_point - - // Settings - bool SettingsLoaded; - float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero - ImGuiTextBuffer SettingsIniData; // In memory .ini settings - ImVector SettingsHandlers; // List of .ini settings handlers - ImChunkStream SettingsWindows; // ImGuiWindow .ini settings entries - ImChunkStream SettingsTables; // ImGuiTable .ini settings entries - ImVector Hooks; // Hooks for extensions (e.g. test engine) - ImGuiID HookIdNext; // Next available HookId - - // Capture/Logging - bool LogEnabled; // Currently capturing - ImGuiLogType LogType; // Capture target - ImFileHandle LogFile; // If != NULL log to stdout/ file - ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. - const char* LogNextPrefix; - const char* LogNextSuffix; - float LogLinePosY; - bool LogLineFirstItem; - int LogDepthRef; - int LogDepthToExpand; - int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. - - // Debug Tools - ImGuiDebugLogFlags DebugLogFlags; - ImGuiTextBuffer DebugLogBuf; - bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) - ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID - ImGuiMetricsConfig DebugMetricsConfig; - ImGuiStackTool DebugStackTool; - - // Misc - float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. - int FramerateSecPerFrameIdx; - int FramerateSecPerFrameCount; - float FramerateSecPerFrameAccum; - int WantCaptureMouseNextFrame; // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1. - int WantCaptureKeyboardNextFrame; // " - int WantTextInputNextFrame; - ImVector TempBuffer; // Temporary text buffer - - ImGuiContext(ImFontAtlas* shared_font_atlas) - { - Initialized = false; - FontAtlasOwnedByContext = shared_font_atlas ? false : true; - Font = NULL; - FontSize = FontBaseSize = 0.0f; - IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); - Time = 0.0f; - FrameCount = 0; - FrameCountEnded = FrameCountRendered = -1; - WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; - GcCompactAll = false; - TestEngineHookItems = false; - TestEngine = NULL; - - WindowsActiveCount = 0; - CurrentWindow = NULL; - HoveredWindow = NULL; - HoveredWindowUnderMovingWindow = NULL; - MovingWindow = NULL; - WheelingWindow = NULL; - WheelingWindowTimer = 0.0f; - - DebugHookIdInfo = 0; - HoveredId = HoveredIdPreviousFrame = 0; - HoveredIdAllowOverlap = false; - HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false; - HoveredIdDisabled = false; - HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; - ActiveId = 0; - ActiveIdIsAlive = 0; - ActiveIdTimer = 0.0f; - ActiveIdIsJustActivated = false; - ActiveIdAllowOverlap = false; - ActiveIdNoClearOnFocusLoss = false; - ActiveIdHasBeenPressedBefore = false; - ActiveIdHasBeenEditedBefore = false; - ActiveIdHasBeenEditedThisFrame = false; - ActiveIdClickOffset = ImVec2(-1, -1); - ActiveIdWindow = NULL; - ActiveIdSource = ImGuiInputSource_None; - ActiveIdMouseButton = -1; - ActiveIdPreviousFrame = 0; - ActiveIdPreviousFrameIsAlive = false; - ActiveIdPreviousFrameHasBeenEditedBefore = false; - ActiveIdPreviousFrameWindow = NULL; - LastActiveId = 0; - LastActiveIdTimer = 0.0f; - - ActiveIdUsingMouseWheel = false; - ActiveIdUsingNavDirMask = 0x00; - ActiveIdUsingNavInputMask = 0x00; - ActiveIdUsingKeyInputMask.ClearAllBits(); - - CurrentItemFlags = ImGuiItemFlags_None; - BeginMenuCount = 0; - - NavWindow = NULL; - NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; - NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; - NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; - NavJustMovedToKeyMods = ImGuiModFlags_None; - NavInputSource = ImGuiInputSource_None; - NavLayer = ImGuiNavLayer_Main; - NavIdIsAlive = false; - NavMousePosDirty = false; - NavDisableHighlight = true; - NavDisableMouseHover = false; - NavAnyRequest = false; - NavInitRequest = false; - NavInitRequestFromMove = false; - NavInitResultId = 0; - NavMoveSubmitted = false; - NavMoveScoringItems = false; - NavMoveForwardToNextFrame = false; - NavMoveFlags = ImGuiNavMoveFlags_None; - NavMoveScrollFlags = ImGuiScrollFlags_None; - NavMoveKeyMods = ImGuiModFlags_None; - NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; - NavScoringDebugCount = 0; - NavTabbingDir = 0; - NavTabbingCounter = 0; - - NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; - NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; - NavWindowingToggleLayer = false; - - DimBgRatio = 0.0f; - MouseCursor = ImGuiMouseCursor_Arrow; - - DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; - DragDropSourceFlags = ImGuiDragDropFlags_None; - DragDropSourceFrameCount = -1; - DragDropMouseButton = -1; - DragDropTargetId = 0; - DragDropAcceptFlags = ImGuiDragDropFlags_None; - DragDropAcceptIdCurrRectSurface = 0.0f; - DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0; - DragDropAcceptFrameCount = -1; - DragDropHoldJustPressedId = 0; - memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); - - ClipperTempDataStacked = 0; - - CurrentTable = NULL; - TablesTempDataStacked = 0; - CurrentTabBar = NULL; - - TempInputId = 0; - ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; - ColorEditLastHue = ColorEditLastSat = 0.0f; - ColorEditLastColor = 0; - SliderGrabClickOffset = 0.0f; - SliderCurrentAccum = 0.0f; - SliderCurrentAccumDirty = false; - DragCurrentAccumDirty = false; - DragCurrentAccum = 0.0f; - DragSpeedDefaultRatio = 1.0f / 100.0f; - DisabledAlphaBackup = 0.0f; - DisabledStackSize = 0; - ScrollbarClickDeltaToGrabCenter = 0.0f; - TooltipOverrideCount = 0; - TooltipSlowDelay = 0.50f; - - PlatformImeData.InputPos = ImVec2(0.0f, 0.0f); - PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission - PlatformLocaleDecimalPoint = '.'; - - SettingsLoaded = false; - SettingsDirtyTimer = 0.0f; - HookIdNext = 0; - - LogEnabled = false; - LogType = ImGuiLogType_None; - LogNextPrefix = LogNextSuffix = NULL; - LogFile = NULL; - LogLinePosY = FLT_MAX; - LogLineFirstItem = false; - LogDepthRef = 0; - LogDepthToExpand = LogDepthToExpandDefault = 2; - - DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY; - DebugItemPickerActive = false; - DebugItemPickerBreakId = 0; - - memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); - FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; - FramerateSecPerFrameAccum = 0.0f; - WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; - } -}; - -//----------------------------------------------------------------------------- -// [SECTION] ImGuiWindowTempData, ImGuiWindow -//----------------------------------------------------------------------------- - -// Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. -// (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..) -// (This doesn't need a constructor because we zero-clear it as part of ImGuiWindow and all frame-temporary data are setup on Begin) -struct IMGUI_API ImGuiWindowTempData -{ - // Layout - ImVec2 CursorPos; // Current emitting position, in absolute coordinates. - ImVec2 CursorPosPrevLine; - ImVec2 CursorStartPos; // Initial position after Begin(), generally ~ window position + WindowPadding. - ImVec2 CursorMaxPos; // Used to implicitly calculate ContentSize at the beginning of next frame, for scrolling range and auto-resize. Always growing during the frame. - ImVec2 IdealMaxPos; // Used to implicitly calculate ContentSizeIdeal at the beginning of next frame, for auto-resize only. Always growing during the frame. - ImVec2 CurrLineSize; - ImVec2 PrevLineSize; - float CurrLineTextBaseOffset; // Baseline offset (0.0f by default on a new line, generally == style.FramePadding.y when a framed item has been added). - float PrevLineTextBaseOffset; - bool IsSameLine; - ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) - ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. - ImVec1 GroupOffset; - ImVec2 CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensentate and fix the most common use case of large scroll area. - - // Keyboard/Gamepad navigation - ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) - short NavLayersActiveMask; // Which layers have been written to (result from previous frame) - short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame) - ImGuiID NavFocusScopeIdCurrent; // Current focus scope ID while appending - bool NavHideHighlightOneFrame; - bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) - - // Miscellaneous - bool MenuBarAppending; // FIXME: Remove this - ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs. - ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items measurement - int TreeDepth; // Current tree depth. - ImU32 TreeJumpToParentOnPopMask; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31.. Could be turned into a ImU64 if necessary. - ImVector ChildWindows; - ImGuiStorage* StateStorage; // Current persistent per-window storage (store e.g. tree node open/close state) - ImGuiOldColumns* CurrentColumns; // Current columns set - int CurrentTableIdx; // Current table index (into g.Tables) - ImGuiLayoutType LayoutType; - ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() - - // Local parameters stacks - // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. - float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). - float TextWrapPos; // Current text wrap pos. - ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) - ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) -}; - -// Storage for one window -struct IMGUI_API ImGuiWindow -{ - char* Name; // Window name, owned by the window. - ImGuiID ID; // == ImHashStr(Name) - ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ - ImGuiViewportP* Viewport; // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded. - ImVec2 Pos; // Position (always rounded-up to nearest pixel) - ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) - ImVec2 SizeFull; // Size when non collapsed - ImVec2 ContentSize; // Size of contents/scrollable client area (calculated from the extents reach of the cursor) from previous frame. Does not include window decoration or window padding. - ImVec2 ContentSizeIdeal; - ImVec2 ContentSizeExplicit; // Size of contents/scrollable client area explicitly request by the user via SetNextWindowContentSize(). - ImVec2 WindowPadding; // Window padding at the time of Begin(). - float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc. - float WindowBorderSize; // Window border size at the time of Begin(). - int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! - ImGuiID MoveId; // == window->GetID("#MOVE") - ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) - ImVec2 Scroll; - ImVec2 ScrollMax; - ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) - ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered - ImVec2 ScrollTargetEdgeSnapDist; // 0.0f = no snapping, >0.0f snapping threshold - ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar. - bool ScrollbarX, ScrollbarY; // Are scrollbars visible? - bool Active; // Set to true on Begin(), unless Collapsed - bool WasActive; - bool WriteAccessed; // Set to true when any widget access the current window - bool Collapsed; // Set when collapsing window to become only title-bar - bool WantCollapseToggle; - bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) - bool Appearing; // Set during the frame where the window is appearing (or re-appearing) - bool Hidden; // Do not display (== HiddenFrames*** > 0) - bool IsFallbackWindow; // Set on the "Debug##Default" window. - bool IsExplicitChild; // Set when passed _ChildWindow, left to false by BeginDocked() - bool HasCloseButton; // Set when the window has a close button (p_open != NULL) - signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) - short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) - short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0. - short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues. - short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused. - ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) - ImS8 AutoFitFramesX, AutoFitFramesY; - ImS8 AutoFitChildAxises; - bool AutoFitOnlyGrows; - ImGuiDir AutoPosLastDirection; - ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames - ImS8 HiddenFramesCannotSkipItems; // Hide the window for N frames while allowing items to be submitted so we can measure their size - ImS8 HiddenFramesForRenderOnly; // Hide the window until frame N at Render() time only - ImS8 DisableInputsFrames; // Disable window interactions for N frames - ImGuiCond SetWindowPosAllowFlags : 8; // store acceptable condition flags for SetNextWindowPos() use. - ImGuiCond SetWindowSizeAllowFlags : 8; // store acceptable condition flags for SetNextWindowSize() use. - ImGuiCond SetWindowCollapsedAllowFlags : 8; // store acceptable condition flags for SetNextWindowCollapsed() use. - ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) - ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0, 0) when positioning from top-left corner; ImVec2(0.5f, 0.5f) for centering; ImVec2(1, 1) for bottom right. - - ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure) - ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name. - - // The best way to understand what those rectangles are is to use the 'Metrics->Tools->Show Windows Rectangles' viewer. - // The main 'OuterRect', omitted as a field, is window->Rect(). - ImRect OuterRectClipped; // == Window->Rect() just after setup in Begin(). == window->Rect() for root window. - ImRect InnerRect; // Inner rectangle (omit title bar, menu bar, scroll bar) - ImRect InnerClipRect; // == InnerRect shrunk by WindowPadding*0.5f on each side, clipped within viewport or parent clip rect. - ImRect WorkRect; // Initially covers the whole scrolling region. Reduced by containers e.g columns/tables when active. Shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward). - ImRect ParentWorkRect; // Backup of WorkRect before entering a container such as columns/tables. Used by e.g. SpanAllColumns functions to easily access. Stacked containers are responsible for maintaining this. // FIXME-WORKRECT: Could be a stack? - ImRect ClipRect; // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back(). - ImRect ContentRegionRect; // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on. - ImVec2ih HitTestHoleSize; // Define an optional rectangular hole where mouse will pass-through the window. - ImVec2ih HitTestHoleOffset; - - int LastFrameActive; // Last frame number the window was Active. - float LastTimeActive; // Last timestamp the window was Active (using float as we don't need high precision there) - float ItemWidthDefault; - ImGuiStorage StateStorage; - ImVector ColumnsStorage; - float FontWindowScale; // User scale multiplier per-window, via SetWindowFontScale() - int SettingsOffset; // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back) - - ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) - ImDrawList DrawListInst; - ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. - ImGuiWindow* ParentWindowInBeginStack; - ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. - ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. - ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. - ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. - - ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) - ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) - ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space - - int MemoryDrawListIdxCapacity; // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy - int MemoryDrawListVtxCapacity; - bool MemoryCompacted; // Set when window extraneous data have been garbage collected - -public: - ImGuiWindow(ImGuiContext* context, const char* name); - ~ImGuiWindow(); - - ImGuiID GetID(const char* str, const char* str_end = NULL); - ImGuiID GetID(const void* ptr); - ImGuiID GetID(int n); - ImGuiID GetIDFromRectangle(const ImRect& r_abs); - - // We don't use g.FontSize because the window may be != g.CurrentWidow. - ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - float CalcFontSize() const { ImGuiContext& g = *GImGui; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; } - float TitleBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; } - ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } - float MenuBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + g.Style.FramePadding.y * 2.0f : 0.0f; } - ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Tab bar, Tab item support -//----------------------------------------------------------------------------- - -// Extend ImGuiTabBarFlags_ -enum ImGuiTabBarFlagsPrivate_ -{ - ImGuiTabBarFlags_DockNode = 1 << 20, // Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around] - ImGuiTabBarFlags_IsFocused = 1 << 21, - ImGuiTabBarFlags_SaveSettings = 1 << 22 // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs -}; - -// Extend ImGuiTabItemFlags_ -enum ImGuiTabItemFlagsPrivate_ -{ - ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, - ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) - ImGuiTabItemFlags_Button = 1 << 21 // Used by TabItemButton, change the tab item behavior to mimic a button -}; - -// Storage for one active tab item (sizeof() 40 bytes) -struct ImGuiTabItem -{ - ImGuiID ID; - ImGuiTabItemFlags Flags; - int LastFrameVisible; - int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance - float Offset; // Position relative to beginning of tab - float Width; // Width currently displayed - float ContentWidth; // Width of label, stored during BeginTabItem() call - float RequestedWidth; // Width optionally requested by caller, -1.0f is unused - ImS32 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames - ImS16 BeginOrder; // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable - ImS16 IndexDuringLayout; // Index only used during TabBarLayout() - bool WantClose; // Marked as closed by SetTabItemClosed() - - ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } -}; - -// Storage for a tab bar (sizeof() 152 bytes) -struct IMGUI_API ImGuiTabBar -{ - ImVector Tabs; - ImGuiTabBarFlags Flags; - ImGuiID ID; // Zero for tab-bars used by docking - ImGuiID SelectedTabId; // Selected tab/window - ImGuiID NextSelectedTabId; // Next selected tab/window. Will also trigger a scrolling animation - ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview) - int CurrFrameVisible; - int PrevFrameVisible; - ImRect BarRect; - float CurrTabsContentsHeight; - float PrevTabsContentsHeight; // Record the height of contents submitted below the tab bar - float WidthAllTabs; // Actual width of all tabs (locked during layout) - float WidthAllTabsIdeal; // Ideal width if all tabs were visible and not clipped - float ScrollingAnim; - float ScrollingTarget; - float ScrollingTargetDistToVisibility; - float ScrollingSpeed; - float ScrollingRectMinX; - float ScrollingRectMaxX; - ImGuiID ReorderRequestTabId; - ImS16 ReorderRequestOffset; - ImS8 BeginCount; - bool WantLayout; - bool VisibleTabWasSubmitted; - bool TabsAddedNew; // Set to true when a new tab item or button has been added to the tab bar during last frame - ImS16 TabsActiveCount; // Number of tabs submitted this frame. - ImS16 LastTabItemIdx; // Index of last BeginTabItem() tab for use by EndTabItem() - float ItemSpacingY; - ImVec2 FramePadding; // style.FramePadding locked at the time of BeginTabBar() - ImVec2 BackupCursorPos; - ImGuiTextBuffer TabsNames; // For non-docking tab bar we re-append names in a contiguous buffer. - - ImGuiTabBar(); - int GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_ptr(tab); } - const char* GetTabName(const ImGuiTabItem* tab) const - { - IM_ASSERT(tab->NameOffset != -1 && tab->NameOffset < TabsNames.Buf.Size); - return TabsNames.Buf.Data + tab->NameOffset; - } -}; - -//----------------------------------------------------------------------------- -// [SECTION] Table support -//----------------------------------------------------------------------------- - -#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color. -#define IMGUI_TABLE_MAX_COLUMNS 64 // sizeof(ImU64) * 8. This is solely because we frequently encode columns set in a ImU64. -#define IMGUI_TABLE_MAX_DRAW_CHANNELS (4 + 64 * 2) // See TableSetupDrawChannels() - -// Our current column maximum is 64 but we may raise that in the future. -typedef ImS8 ImGuiTableColumnIdx; -typedef ImU8 ImGuiTableDrawChannelIdx; - -// [Internal] sizeof() ~ 104 -// We use the terminology "Enabled" to refer to a column that is not Hidden by user/api. -// We use the terminology "Clipped" to refer to a column that is out of sight because of scrolling/clipping. -// This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped". -struct ImGuiTableColumn -{ - ImGuiTableColumnFlags Flags; // Flags after some patching (not directly same as provided by user). See ImGuiTableColumnFlags_ - float WidthGiven; // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space. - float MinX; // Absolute positions - float MaxX; - float WidthRequest; // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout() - float WidthAuto; // Automatic width - float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially. - float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_). - ImRect ClipRect; // Clipping rectangle for the column - ImGuiID UserID; // Optional, value passed to TableSetupColumn() - float WorkMinX; // Contents region min ~(MinX + CellPaddingX + CellSpacingX1) == cursor start position when entering column - float WorkMaxX; // Contents region max ~(MaxX - CellPaddingX - CellSpacingX2) - float ItemWidth; // Current item width for the column, preserved across rows - float ContentMaxXFrozen; // Contents maximum position for frozen rows (apart from headers), from which we can infer content width. - float ContentMaxXUnfrozen; - float ContentMaxXHeadersUsed; // Contents maximum position for headers rows (regardless of freezing). TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls - float ContentMaxXHeadersIdeal; - ImS16 NameOffset; // Offset into parent ColumnsNames[] - ImGuiTableColumnIdx DisplayOrder; // Index within Table's IndexToDisplayOrder[] (column may be reordered by users) - ImGuiTableColumnIdx IndexWithinEnabledSet; // Index within enabled/visible set (<= IndexToDisplayOrder) - ImGuiTableColumnIdx PrevEnabledColumn; // Index of prev enabled/visible column within Columns[], -1 if first enabled/visible column - ImGuiTableColumnIdx NextEnabledColumn; // Index of next enabled/visible column within Columns[], -1 if last enabled/visible column - ImGuiTableColumnIdx SortOrder; // Index of this column within sort specs, -1 if not sorting on this column, 0 for single-sort, may be >0 on multi-sort - ImGuiTableDrawChannelIdx DrawChannelCurrent; // Index within DrawSplitter.Channels[] - ImGuiTableDrawChannelIdx DrawChannelFrozen; // Draw channels for frozen rows (often headers) - ImGuiTableDrawChannelIdx DrawChannelUnfrozen; // Draw channels for unfrozen rows - bool IsEnabled; // IsUserEnabled && (Flags & ImGuiTableColumnFlags_Disabled) == 0 - bool IsUserEnabled; // Is the column not marked Hidden by the user? (unrelated to being off view, e.g. clipped by scrolling). - bool IsUserEnabledNextFrame; - bool IsVisibleX; // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled). - bool IsVisibleY; - bool IsRequestOutput; // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not. - bool IsSkipItems; // Do we want item submissions to this column to be completely ignored (no layout will happen). - bool IsPreserveWidthAuto; - ImS8 NavLayerCurrent; // ImGuiNavLayer in 1 byte - ImU8 AutoFitQueue; // Queue of 8 values for the next 8 frames to request auto-fit - ImU8 CannotSkipItemsQueue; // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem - ImU8 SortDirection : 2; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending - ImU8 SortDirectionsAvailCount : 2; // Number of available sort directions (0 to 3) - ImU8 SortDirectionsAvailMask : 4; // Mask of available sort directions (1-bit each) - ImU8 SortDirectionsAvailList; // Ordered of available sort directions (2-bits each) - - ImGuiTableColumn() - { - memset(this, 0, sizeof(*this)); - StretchWeight = WidthRequest = -1.0f; - NameOffset = -1; - DisplayOrder = IndexWithinEnabledSet = -1; - PrevEnabledColumn = NextEnabledColumn = -1; - SortOrder = -1; - SortDirection = ImGuiSortDirection_None; - DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU8)-1; - } -}; - -// Transient cell data stored per row. -// sizeof() ~ 6 -struct ImGuiTableCellData -{ - ImU32 BgColor; // Actual color - ImGuiTableColumnIdx Column; // Column number -}; - -// Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs, does that needs they could be moved to ImGuiTableTempData ?) -struct ImGuiTableInstanceData -{ - float LastOuterHeight; // Outer height from last frame // FIXME: multi-instance issue (#3955) - float LastFirstRowHeight; // Height of first row from last frame // FIXME: possible multi-instance issue? - - ImGuiTableInstanceData() { LastOuterHeight = LastFirstRowHeight = 0.0f; } -}; - -// FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData -struct IMGUI_API ImGuiTable -{ - ImGuiID ID; - ImGuiTableFlags Flags; - void* RawData; // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[] - ImGuiTableTempData* TempData; // Transient data while table is active. Point within g.CurrentTableStack[] - ImSpan Columns; // Point within RawData[] - ImSpan DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1) - ImSpan RowCellData; // Point within RawData[]. Store cells background requests for current row. - ImU64 EnabledMaskByDisplayOrder; // Column DisplayOrder -> IsEnabled map - ImU64 EnabledMaskByIndex; // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data - ImU64 VisibleMaskByIndex; // Column Index -> IsVisibleX|IsVisibleY map (== not hidden by user/api && not hidden by scrolling/cliprect) - ImU64 RequestOutputMaskByIndex; // Column Index -> IsVisible || AutoFit (== expect user to submit items) - ImGuiTableFlags SettingsLoadedFlags; // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order) - int SettingsOffset; // Offset in g.SettingsTables - int LastFrameActive; - int ColumnsCount; // Number of columns declared in BeginTable() - int CurrentRow; - int CurrentColumn; - ImS16 InstanceCurrent; // Count of BeginTable() calls with same ID in the same frame (generally 0). This is a little bit similar to BeginCount for a window, but multiple table with same ID look are multiple tables, they are just synched. - ImS16 InstanceInteracted; // Mark which instance (generally 0) of the same ID is being interacted with - float RowPosY1; - float RowPosY2; - float RowMinHeight; // Height submitted to TableNextRow() - float RowTextBaseline; - float RowIndentOffsetX; - ImGuiTableRowFlags RowFlags : 16; // Current row flags, see ImGuiTableRowFlags_ - ImGuiTableRowFlags LastRowFlags : 16; - int RowBgColorCounter; // Counter for alternating background colors (can be fast-forwarded by e.g clipper), not same as CurrentRow because header rows typically don't increase this. - ImU32 RowBgColor[2]; // Background color override for current row. - ImU32 BorderColorStrong; - ImU32 BorderColorLight; - float BorderX1; - float BorderX2; - float HostIndentX; - float MinColumnWidth; - float OuterPaddingX; - float CellPaddingX; // Padding from each borders - float CellPaddingY; - float CellSpacingX1; // Spacing between non-bordered cells - float CellSpacingX2; - float InnerWidth; // User value passed to BeginTable(), see comments at the top of BeginTable() for details. - float ColumnsGivenWidth; // Sum of current column width - float ColumnsAutoFitWidth; // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window - float ColumnsStretchSumWeights; // Sum of weight of all enabled stretching columns - float ResizedColumnNextWidth; - float ResizeLockMinContentsX2; // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table. - float RefScale; // Reference scale to be able to rescale columns on font/dpi changes. - ImRect OuterRect; // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable(). - ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is - ImRect WorkRect; - ImRect InnerClipRect; - ImRect BgClipRect; // We use this to cpu-clip cell background color fill, evolve during the frame as we cross frozen rows boundaries - ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped - ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect. - ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window. - ImRect HostBackupInnerClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground() - ImGuiWindow* OuterWindow; // Parent window for the table - ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window) - ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names - ImDrawListSplitter* DrawSplitter; // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly - ImGuiTableInstanceData InstanceDataFirst; - ImVector InstanceDataExtra; // FIXME-OPT: Using a small-vector pattern would be good. - ImGuiTableColumnSortSpecs SortSpecsSingle; - ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good. - ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs() - ImGuiTableColumnIdx SortSpecsCount; - ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) - ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns (<= ColumnsCount) - ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn() - ImGuiTableColumnIdx HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column! - ImGuiTableColumnIdx HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing). - ImGuiTableColumnIdx AutoFitSingleColumn; // Index of single column requesting auto-fit. - ImGuiTableColumnIdx ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0. - ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame. - ImGuiTableColumnIdx HeldHeaderColumn; // Index of column header being held. - ImGuiTableColumnIdx ReorderColumn; // Index of column being reordered. (not cleared) - ImGuiTableColumnIdx ReorderColumnDir; // -1 or +1 - ImGuiTableColumnIdx LeftMostEnabledColumn; // Index of left-most non-hidden column. - ImGuiTableColumnIdx RightMostEnabledColumn; // Index of right-most non-hidden column. - ImGuiTableColumnIdx LeftMostStretchedColumn; // Index of left-most stretched column. - ImGuiTableColumnIdx RightMostStretchedColumn; // Index of right-most stretched column. - ImGuiTableColumnIdx ContextPopupColumn; // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot - ImGuiTableColumnIdx FreezeRowsRequest; // Requested frozen rows count - ImGuiTableColumnIdx FreezeRowsCount; // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset) - ImGuiTableColumnIdx FreezeColumnsRequest; // Requested frozen columns count - ImGuiTableColumnIdx FreezeColumnsCount; // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset) - ImGuiTableColumnIdx RowCellDataCurrent; // Index of current RowCellData[] entry in current row - ImGuiTableDrawChannelIdx DummyDrawChannel; // Redirect non-visible columns here. - ImGuiTableDrawChannelIdx Bg2DrawChannelCurrent; // For Selectable() and other widgets drawing across columns after the freezing line. Index within DrawSplitter.Channels[] - ImGuiTableDrawChannelIdx Bg2DrawChannelUnfrozen; - bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row. - bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow(). - bool IsInitializing; - bool IsSortSpecsDirty; - bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag. - bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted). - bool IsSettingsRequestLoad; - bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data. - bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1) - bool IsResetAllRequest; - bool IsResetDisplayOrderRequest; - bool IsUnfrozenRows; // Set when we got past the frozen row. - bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable() - bool MemoryCompacted; - bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis - - ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } - ~ImGuiTable() { IM_FREE(RawData); } -}; - -// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). -// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. -// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. -struct IMGUI_API ImGuiTableTempData -{ - int TableIndex; // Index in g.Tables.Buf[] pool - float LastTimeActive; // Last timestamp this structure was used - - ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() - ImDrawListSplitter DrawSplitter; - - ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() - ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() - ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() - ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() - ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() - ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() - float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() - int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() - - ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } -}; - -// sizeof() ~ 12 -struct ImGuiTableColumnSettings -{ - float WidthOrWeight; - ImGuiID UserID; - ImGuiTableColumnIdx Index; - ImGuiTableColumnIdx DisplayOrder; - ImGuiTableColumnIdx SortOrder; - ImU8 SortDirection : 2; - ImU8 IsEnabled : 1; // "Visible" in ini file - ImU8 IsStretch : 1; - - ImGuiTableColumnSettings() - { - WidthOrWeight = 0.0f; - UserID = 0; - Index = -1; - DisplayOrder = SortOrder = -1; - SortDirection = ImGuiSortDirection_None; - IsEnabled = 1; - IsStretch = 0; - } -}; - -// This is designed to be stored in a single ImChunkStream (1 header followed by N ImGuiTableColumnSettings, etc.) -struct ImGuiTableSettings -{ - ImGuiID ID; // Set to 0 to invalidate/delete the setting - ImGuiTableFlags SaveFlags; // Indicate data we want to save using the Resizable/Reorderable/Sortable/Hideable flags (could be using its own flags..) - float RefScale; // Reference scale to be able to rescale columns on font/dpi changes. - ImGuiTableColumnIdx ColumnsCount; - ImGuiTableColumnIdx ColumnsCountMax; // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher - bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) - - ImGuiTableSettings() { memset(this, 0, sizeof(*this)); } - ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); } -}; - -//----------------------------------------------------------------------------- -// [SECTION] ImGui internal API -// No guarantee of forward compatibility here! -//----------------------------------------------------------------------------- - -namespace ImGui -{ - // Windows - // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) - // If this ever crash because g.CurrentWindow is NULL it means that either - // - ImGui::NewFrame() has never been called, which is illegal. - // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. - inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } - inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } - IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); - IMGUI_API ImGuiWindow* FindWindowByName(const char* name); - IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); - IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); - IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent); - IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); - IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); - IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); - IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); - IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); - IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); - inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } - inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } - - // Windows: Display Order and Focus Order - IMGUI_API void FocusWindow(ImGuiWindow* window); - IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window); - IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); - IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); - IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); - IMGUI_API void BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window); - IMGUI_API int FindWindowDisplayIndex(ImGuiWindow* window); - IMGUI_API ImGuiWindow* FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window); - - // Fonts, drawing - IMGUI_API void SetCurrentFont(ImFont* font); - inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } - inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. - IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. - IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. - - // Init - IMGUI_API void Initialize(); - IMGUI_API void Shutdown(); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). - - // NewFrame - IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs); - IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); - IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); - IMGUI_API void UpdateMouseMovingWindowNewFrame(); - IMGUI_API void UpdateMouseMovingWindowEndFrame(); - - // Generic context hooks - IMGUI_API ImGuiID AddContextHook(ImGuiContext* context, const ImGuiContextHook* hook); - IMGUI_API void RemoveContextHook(ImGuiContext* context, ImGuiID hook_to_remove); - IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); - - // Viewports - IMGUI_API void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport); - - // Settings - IMGUI_API void MarkIniSettingsDirty(); - IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); - IMGUI_API void ClearIniSettings(); - IMGUI_API ImGuiWindowSettings* CreateNewWindowSettings(const char* name); - IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id); - IMGUI_API ImGuiWindowSettings* FindOrCreateWindowSettings(const char* name); - IMGUI_API void AddSettingsHandler(const ImGuiSettingsHandler* handler); - IMGUI_API void RemoveSettingsHandler(const char* type_name); - IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); - - // Scrolling - IMGUI_API void SetNextWindowScroll(const ImVec2& scroll); // Use -1.0f on one axis to leave as-is - IMGUI_API void SetScrollX(ImGuiWindow* window, float scroll_x); - IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y); - IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio); - IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio); - - // Early work-in-progress API (ScrollToItem() will become public) - IMGUI_API void ScrollToItem(ImGuiScrollFlags flags = 0); - IMGUI_API void ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); - IMGUI_API ImVec2 ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); -//#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - inline void ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); } -//#endif - - // Basic Accessors - inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.LastItemData.ID; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand) - inline ImGuiItemStatusFlags GetItemStatusFlags(){ ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } - inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.InFlags; } - inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } - inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } - IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); - IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); - IMGUI_API void ClearActiveID(); - IMGUI_API ImGuiID GetHoveredID(); - IMGUI_API void SetHoveredID(ImGuiID id); - IMGUI_API void KeepAliveID(ImGuiID id); - IMGUI_API void MarkItemEdited(ImGuiID id); // Mark data associated to given item as "edited", used by IsItemDeactivatedAfterEdit() function. - IMGUI_API void PushOverrideID(ImGuiID id); // Push given value as-is at the top of the ID stack (whereas PushID combines old and new hashes) - IMGUI_API ImGuiID GetIDWithSeed(const char* str_id_begin, const char* str_id_end, ImGuiID seed); - - // Basic Helpers for widget code - IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); - inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min. - IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); - IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); - IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); - IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); - IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); - IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); - IMGUI_API void PushMultiItemsWidths(int components, float width_full); - IMGUI_API bool IsItemToggledSelection(); // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) - IMGUI_API ImVec2 GetContentRegionMaxAbs(); - IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); - - // Parameter stacks - IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); - IMGUI_API void PopItemFlag(); - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - // Currently refactoring focus/nav/tabbing system - // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): - // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' - // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' - // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || g.NavActivateInputId == id' (WIP) - // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText() - inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() - inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem -#endif - - // Logging/Capture - IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. - IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer - IMGUI_API void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL); - IMGUI_API void LogSetNextTextDecoration(const char* prefix, const char* suffix); - - // Popups, Modals, Tooltips - IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags); - IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); - IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); - IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); - IMGUI_API void ClosePopupsExceptModals(); - IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags); - IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); - IMGUI_API void BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); - IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); - IMGUI_API ImGuiWindow* GetTopMostPopupModal(); - IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal(); - IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); - IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); - - // Menus - IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); - IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true); - IMGUI_API bool MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true); - - // Combos - IMGUI_API bool BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags); - IMGUI_API bool BeginComboPreview(); - IMGUI_API void EndComboPreview(); - - // Gamepad/Keyboard Navigation - IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); - IMGUI_API void NavInitRequestApplyResult(); - IMGUI_API bool NavMoveRequestButNoResultYet(); - IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); - IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); - IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result); - IMGUI_API void NavMoveRequestCancel(); - IMGUI_API void NavMoveRequestApplyResult(); - IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); - IMGUI_API const char* GetNavInputName(ImGuiNavInput n); - IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode); - IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiNavReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); - IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); - IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. - IMGUI_API void SetNavWindow(ImGuiWindow* window); - IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); - - // Focus Scope (WIP) - // This is generally used to identify a selection set (multiple of which may be in the same window), as selection - // patterns generally need to react (e.g. clear selection) when landing on an item of the set. - IMGUI_API void PushFocusScope(ImGuiID id); - IMGUI_API void PopFocusScope(); - inline ImGuiID GetFocusedFocusScope() { ImGuiContext& g = *GImGui; return g.NavFocusScopeId; } // Focus scope which is actually active - inline ImGuiID GetFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.NavFocusScopeIdCurrent; } // Focus scope we are outputting into, set by PushFocusScope() - - // Inputs - // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. - inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; } - inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } - inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } - IMGUI_API ImGuiKeyData* GetKeyData(ImGuiKey key); - IMGUI_API void SetItemUsingMouseWheel(); - IMGUI_API void SetActiveIdUsingNavAndKeys(); - inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } - inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; } - inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask[key]; } - inline void SetActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key); } - IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); - inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; } - inline bool IsNavInputTest(ImGuiNavInput n, ImGuiNavReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); } - IMGUI_API ImGuiModFlags GetMergedModFlags(); -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // [removed in 1.87] -#endif - - // Drag and Drop - IMGUI_API bool IsDragDropActive(); - IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); - IMGUI_API void ClearDragDrop(); - IMGUI_API bool IsDragDropPayloadBeingAccepted(); - - // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) - IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); - IMGUI_API void BeginColumns(const char* str_id, int count, ImGuiOldColumnFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns(). - IMGUI_API void EndColumns(); // close columns - IMGUI_API void PushColumnClipRect(int column_index); - IMGUI_API void PushColumnsBackground(); - IMGUI_API void PopColumnsBackground(); - IMGUI_API ImGuiID GetColumnsID(const char* str_id, int count); - IMGUI_API ImGuiOldColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id); - IMGUI_API float GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm); - IMGUI_API float GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset); - - // Tables: Candidates for public API - IMGUI_API void TableOpenContextMenu(int column_n = -1); - IMGUI_API void TableSetColumnWidth(int column_n, float width); - IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); - IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. - IMGUI_API float TableGetHeaderRowHeight(); - IMGUI_API void TablePushBackgroundChannel(); - IMGUI_API void TablePopBackgroundChannel(); - - // Tables: Internals - inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; } - IMGUI_API ImGuiTable* TableFindByID(ImGuiID id); - IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f); - IMGUI_API void TableBeginInitMemory(ImGuiTable* table, int columns_count); - IMGUI_API void TableBeginApplyRequests(ImGuiTable* table); - IMGUI_API void TableSetupDrawChannels(ImGuiTable* table); - IMGUI_API void TableUpdateLayout(ImGuiTable* table); - IMGUI_API void TableUpdateBorders(ImGuiTable* table); - IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table); - IMGUI_API void TableDrawBorders(ImGuiTable* table); - IMGUI_API void TableDrawContextMenu(ImGuiTable* table); - IMGUI_API void TableMergeDrawChannels(ImGuiTable* table); - inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; } - IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table); - IMGUI_API void TableSortSpecsBuild(ImGuiTable* table); - IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column); - IMGUI_API void TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column); - IMGUI_API float TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column); - IMGUI_API void TableBeginRow(ImGuiTable* table); - IMGUI_API void TableEndRow(ImGuiTable* table); - IMGUI_API void TableBeginCell(ImGuiTable* table, int column_n); - IMGUI_API void TableEndCell(ImGuiTable* table); - IMGUI_API ImRect TableGetCellBgRect(const ImGuiTable* table, int column_n); - IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n); - IMGUI_API ImGuiID TableGetColumnResizeID(const ImGuiTable* table, int column_n, int instance_no = 0); - IMGUI_API float TableGetMaxColumnWidth(const ImGuiTable* table, int column_n); - IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); - IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); - IMGUI_API void TableRemove(ImGuiTable* table); - IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); - IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); - IMGUI_API void TableGcCompactSettings(); - - // Tables: Settings - IMGUI_API void TableLoadSettings(ImGuiTable* table); - IMGUI_API void TableSaveSettings(ImGuiTable* table); - IMGUI_API void TableResetSettings(ImGuiTable* table); - IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table); - IMGUI_API void TableSettingsAddSettingsHandler(); - IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count); - IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id); - - // Tab Bars - IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); - IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); - IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); - IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); - IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset); - IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos); - IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); - IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags); - IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); - IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); - IMGUI_API void TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped); - - // Render helpers - // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. - // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally) - IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); - IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); - IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); - IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); - IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); - IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); - IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); - IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); - IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_TypeDefault); // Navigation highlight - IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. - IMGUI_API void RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow); - - // Render helpers (those functions don't access any ImGui state!) - IMGUI_API void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f); - IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); - IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); - IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); - IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); - IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding); - - // Widgets - IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); - IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); - IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos); - IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); - IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); - IMGUI_API void Scrollbar(ImGuiAxis axis); - IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags); - IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col); - IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); - IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis); - IMGUI_API ImGuiID GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners - IMGUI_API ImGuiID GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir); - IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags); - IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); - IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value); - - // Widgets low-level behaviors - IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); - IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags); - IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); - IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f); - IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); - IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextItemOpen() data, if any. May return true when logging - IMGUI_API void TreePushOverrideID(ImGuiID id); - - // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. - // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). - // e.g. " extern template IMGUI_API float RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, float v); " - template IMGUI_API float ScaleRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size); - template IMGUI_API T ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size); - template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags); - template IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); - template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); - template IMGUI_API bool CheckboxFlagsT(const char* label, T* flags, T flags_value); - - // Data type helpers - IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type); - IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format); - IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2); - IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format); - IMGUI_API int DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2); - IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max); - - // InputText - IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags); - IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); - inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); } - inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active - - // Color - IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); - IMGUI_API void ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags); - IMGUI_API void ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags); - - // Plot - IMGUI_API int PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size); - - // Shade functions (write over already created vertices) - IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1); - IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp); - - // Garbage collection - IMGUI_API void GcCompactTransientMiscBuffers(); - IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window); - IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); - - // Debug Log - IMGUI_API void DebugLog(const char* fmt, ...) IM_FMTARGS(1); - IMGUI_API void DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1); - - // Debug Tools - IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); - IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); - inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); } - inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } - IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); - IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); - IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); - IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label); - IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); - IMGUI_API void DebugNodeFont(ImFont* font); - IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph); - IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label); - IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label); - IMGUI_API void DebugNodeTable(ImGuiTable* table); - IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings); - IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state); - IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); - IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); - IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); - IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); - IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); - IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); - -} // namespace ImGui - - -//----------------------------------------------------------------------------- -// [SECTION] ImFontAtlas internal API -//----------------------------------------------------------------------------- - -// This structure is likely to evolve as we add support for incremental atlas updates -struct ImFontBuilderIO -{ - bool (*FontBuilder_Build)(ImFontAtlas* atlas); -}; - -// Helper for font builder -#ifdef IMGUI_ENABLE_STB_TRUETYPE -IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype(); -#endif -IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); -IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque); -IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value); -IMGUI_API void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value); -IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); -IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); - -//----------------------------------------------------------------------------- -// [SECTION] Test Engine specific hooks (imgui_test_engine) -//----------------------------------------------------------------------------- - -#ifdef IMGUI_ENABLE_TEST_ENGINE -extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); -extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); -extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...); -extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id); - -#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box -#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) -#define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log -#else -#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0) -#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) -#endif - -//----------------------------------------------------------------------------- - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - -#ifdef _MSC_VER -#pragma warning (pop) -#endif - -#endif // #ifndef IMGUI_DISABLE diff --git a/libs/zgui/libs/imgui/imgui_tables.cpp b/libs/zgui/libs/imgui/imgui_tables.cpp deleted file mode 100644 index c3e24af35..000000000 --- a/libs/zgui/libs/imgui/imgui_tables.cpp +++ /dev/null @@ -1,4068 +0,0 @@ -// dear imgui, v1.88 -// (tables and columns code) - -/* - -Index of this file: - -// [SECTION] Commentary -// [SECTION] Header mess -// [SECTION] Tables: Main code -// [SECTION] Tables: Simple accessors -// [SECTION] Tables: Row changes -// [SECTION] Tables: Columns changes -// [SECTION] Tables: Columns width management -// [SECTION] Tables: Drawing -// [SECTION] Tables: Sorting -// [SECTION] Tables: Headers -// [SECTION] Tables: Context Menu -// [SECTION] Tables: Settings (.ini data) -// [SECTION] Tables: Garbage Collection -// [SECTION] Tables: Debugging -// [SECTION] Columns, BeginColumns, EndColumns, etc. - -*/ - -// Navigating this file: -// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. - -//----------------------------------------------------------------------------- -// [SECTION] Commentary -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Typical tables call flow: (root level is generally public API): -//----------------------------------------------------------------------------- -// - BeginTable() user begin into a table -// | BeginChild() - (if ScrollX/ScrollY is set) -// | TableBeginInitMemory() - first time table is used -// | TableResetSettings() - on settings reset -// | TableLoadSettings() - on settings load -// | TableBeginApplyRequests() - apply queued resizing/reordering/hiding requests -// | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame) -// | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width -// - TableSetupColumn() user submit columns details (optional) -// - TableSetupScrollFreeze() user submit scroll freeze information (optional) -//----------------------------------------------------------------------------- -// - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow(). -// | TableSetupDrawChannels() - setup ImDrawList channels -// | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission -// | TableDrawContextMenu() - draw right-click context menu -//----------------------------------------------------------------------------- -// - TableHeadersRow() or TableHeader() user submit a headers row (optional) -// | TableSortSpecsClickColumn() - when left-clicked: alter sort order and sort direction -// | TableOpenContextMenu() - when right-clicked: trigger opening of the default context menu -// - TableGetSortSpecs() user queries updated sort specs (optional, generally after submitting headers) -// - TableNextRow() user begin into a new row (also automatically called by TableHeadersRow()) -// | TableEndRow() - finish existing row -// | TableBeginRow() - add a new row -// - TableSetColumnIndex() / TableNextColumn() user begin into a cell -// | TableEndCell() - close existing column/cell -// | TableBeginCell() - enter into current column/cell -// - [...] user emit contents -//----------------------------------------------------------------------------- -// - EndTable() user ends the table -// | TableDrawBorders() - draw outer borders, inner vertical borders -// | TableMergeDrawChannels() - merge draw channels if clipping isn't required -// | EndChild() - (if ScrollX/ScrollY is set) -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// TABLE SIZING -//----------------------------------------------------------------------------- -// (Read carefully because this is subtle but it does make sense!) -//----------------------------------------------------------------------------- -// About 'outer_size': -// Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags. -// Default value is ImVec2(0.0f, 0.0f). -// X -// - outer_size.x <= 0.0f -> Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge. -// - outer_size.x > 0.0f -> Set Fixed width. -// Y with ScrollX/ScrollY disabled: we output table directly in current window -// - outer_size.y < 0.0f -> Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful is parent window can vertically scroll. -// - outer_size.y = 0.0f -> No minimum height (but will auto extend, unless _NoHostExtendY is set) -// - outer_size.y > 0.0f -> Set Minimum height (but will auto extend, unless _NoHostExtenY is set) -// Y with ScrollX/ScrollY enabled: using a child window for scrolling -// - outer_size.y < 0.0f -> Bottom-align. Not meaningful is parent window can vertically scroll. -// - outer_size.y = 0.0f -> Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window. -// - outer_size.y > 0.0f -> Set Exact height. Recommended when using Scrolling on any axis. -//----------------------------------------------------------------------------- -// Outer size is also affected by the NoHostExtendX/NoHostExtendY flags. -// Important to that note how the two flags have slightly different behaviors! -// - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. -// - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible. -// In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height. -// This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not easily noticeable) -//----------------------------------------------------------------------------- -// About 'inner_width': -// With ScrollX disabled: -// - inner_width -> *ignored* -// With ScrollX enabled: -// - inner_width < 0.0f -> *illegal* fit in known width (right align from outer_size.x) <-- weird -// - inner_width = 0.0f -> fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns. -// - inner_width > 0.0f -> override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space! -//----------------------------------------------------------------------------- -// Details: -// - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept -// of "available space" doesn't make sense. -// - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding -// of what the value does. -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// COLUMNS SIZING POLICIES -//----------------------------------------------------------------------------- -// About overriding column sizing policy and width/weight with TableSetupColumn(): -// We use a default parameter of 'init_width_or_weight == -1'. -// - with ImGuiTableColumnFlags_WidthFixed, init_width <= 0 (default) --> width is automatic -// - with ImGuiTableColumnFlags_WidthFixed, init_width > 0 (explicit) --> width is custom -// - with ImGuiTableColumnFlags_WidthStretch, init_weight <= 0 (default) --> weight is 1.0f -// - with ImGuiTableColumnFlags_WidthStretch, init_weight > 0 (explicit) --> weight is custom -// Widths are specified _without_ CellPadding. If you specify a width of 100.0f, the column will be cover (100.0f + Padding * 2.0f) -// and you can fit a 100.0f wide item in it without clipping and with full padding. -//----------------------------------------------------------------------------- -// About default sizing policy (if you don't specify a ImGuiTableColumnFlags_WidthXXXX flag) -// - with Table policy ImGuiTableFlags_SizingFixedFit --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is equal to contents width -// - with Table policy ImGuiTableFlags_SizingFixedSame --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is max of all contents width -// - with Table policy ImGuiTableFlags_SizingStretchSame --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is 1.0f -// - with Table policy ImGuiTableFlags_SizingStretchWeight --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is proportional to contents -// Default Width and default Weight can be overridden when calling TableSetupColumn(). -//----------------------------------------------------------------------------- -// About mixing Fixed/Auto and Stretch columns together: -// - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. -// - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place! -// that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in. -// - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents. -// - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weight/widths. -//----------------------------------------------------------------------------- -// About using column width: -// If a column is manual resizable or has a width specified with TableSetupColumn(): -// - you may use GetContentRegionAvail().x to query the width available in a given column. -// - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width. -// If the column is not resizable and has no width specified with TableSetupColumn(): -// - its width will be automatic and be set to the max of items submitted. -// - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN). -// - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN). -//----------------------------------------------------------------------------- - - -//----------------------------------------------------------------------------- -// TABLES CLIPPING/CULLING -//----------------------------------------------------------------------------- -// About clipping/culling of Rows in Tables: -// - For large numbers of rows, it is recommended you use ImGuiListClipper to only submit visible rows. -// ImGuiListClipper is reliant on the fact that rows are of equal height. -// See 'Demo->Tables->Vertical Scrolling' or 'Demo->Tables->Advanced' for a demo of using the clipper. -// - Note that auto-resizing columns don't play well with using the clipper. -// By default a table with _ScrollX but without _Resizable will have column auto-resize. -// So, if you want to use the clipper, make sure to either enable _Resizable, either setup columns width explicitly with _WidthFixed. -//----------------------------------------------------------------------------- -// About clipping/culling of Columns in Tables: -// - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing -// width measurements. Otherwise, you may skip submitting the contents of a cell/column, BUT ONLY if you know -// it is not going to contribute to row height. -// In many situations, you may skip submitting contents for every column but one (e.g. the first one). -// - Case A: column is not hidden by user, and at least partially in sight (most common case). -// - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output. -// - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.). -// -// [A] [B] [C] -// TableNextColumn(): true false false -> [userland] when TableNextColumn() / TableSetColumnIndex() return false, user can skip submitting items but only if the column doesn't contribute to row height. -// SkipItems: false false true -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output. -// ClipRect: normal zero-width zero-width -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way. -// ImDrawList output: normal dummy dummy -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway). -// -// - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row. -// However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer. -//----------------------------------------------------------------------------- -// About clipping/culling of whole Tables: -// - Scrolling tables with a known outer size can be clipped earlier as BeginTable() will return false. -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// [SECTION] Header mess -//----------------------------------------------------------------------------- - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "imgui.h" -#ifndef IMGUI_DISABLE - -#ifndef IMGUI_DEFINE_MATH_OPERATORS -#define IMGUI_DEFINE_MATH_OPERATORS -#endif -#include "imgui_internal.h" - -// System includes -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else -#include // intptr_t -#endif - -// Visual Studio warnings -#ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later -#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types -#endif -#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). -#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). -#endif - -// Clang/GCC warnings with -Weverything -#if defined(__clang__) -#if __has_warning("-Wunknown-warning-option") -#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! -#endif -#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' -#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. -#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. -#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. -#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 -#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. -#pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') -#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated -#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision -#elif defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind -#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked -#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead -#endif - -//----------------------------------------------------------------------------- -// [SECTION] Tables: Main code -//----------------------------------------------------------------------------- -// - TableFixFlags() [Internal] -// - TableFindByID() [Internal] -// - BeginTable() -// - BeginTableEx() [Internal] -// - TableBeginInitMemory() [Internal] -// - TableBeginApplyRequests() [Internal] -// - TableSetupColumnFlags() [Internal] -// - TableUpdateLayout() [Internal] -// - TableUpdateBorders() [Internal] -// - EndTable() -// - TableSetupColumn() -// - TableSetupScrollFreeze() -//----------------------------------------------------------------------------- - -// Configuration -static const int TABLE_DRAW_CHANNEL_BG0 = 0; -static const int TABLE_DRAW_CHANNEL_BG2_FROZEN = 1; -static const int TABLE_DRAW_CHANNEL_NOCLIP = 2; // When using ImGuiTableFlags_NoClip (this becomes the last visible channel) -static const float TABLE_BORDER_SIZE = 1.0f; // FIXME-TABLE: Currently hard-coded because of clipping assumptions with outer borders rendering. -static const float TABLE_RESIZE_SEPARATOR_HALF_THICKNESS = 4.0f; // Extend outside inner borders. -static const float TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06f; // Delay/timer before making the hover feedback (color+cursor) visible because tables/columns tends to be more cramped. - -// Helper -inline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_window) -{ - // Adjust flags: set default sizing policy - if ((flags & ImGuiTableFlags_SizingMask_) == 0) - flags |= ((flags & ImGuiTableFlags_ScrollX) || (outer_window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) ? ImGuiTableFlags_SizingFixedFit : ImGuiTableFlags_SizingStretchSame; - - // Adjust flags: enable NoKeepColumnsVisible when using ImGuiTableFlags_SizingFixedSame - if ((flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) - flags |= ImGuiTableFlags_NoKeepColumnsVisible; - - // Adjust flags: enforce borders when resizable - if (flags & ImGuiTableFlags_Resizable) - flags |= ImGuiTableFlags_BordersInnerV; - - // Adjust flags: disable NoHostExtendX/NoHostExtendY if we have any scrolling going on - if (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) - flags &= ~(ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_NoHostExtendY); - - // Adjust flags: NoBordersInBodyUntilResize takes priority over NoBordersInBody - if (flags & ImGuiTableFlags_NoBordersInBodyUntilResize) - flags &= ~ImGuiTableFlags_NoBordersInBody; - - // Adjust flags: disable saved settings if there's nothing to save - if ((flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Sortable)) == 0) - flags |= ImGuiTableFlags_NoSavedSettings; - - // Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set) - if (outer_window->RootWindow->Flags & ImGuiWindowFlags_NoSavedSettings) - flags |= ImGuiTableFlags_NoSavedSettings; - - return flags; -} - -ImGuiTable* ImGui::TableFindByID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - return g.Tables.GetByKey(id); -} - -// Read about "TABLE SIZING" at the top of this file. -bool ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width) -{ - ImGuiID id = GetID(str_id); - return BeginTableEx(str_id, id, columns_count, flags, outer_size, inner_width); -} - -bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* outer_window = GetCurrentWindow(); - if (outer_window->SkipItems) // Consistent with other tables + beneficial side effect that assert on miscalling EndTable() will be more visible. - return false; - - // Sanity checks - IM_ASSERT(columns_count > 0 && columns_count <= IMGUI_TABLE_MAX_COLUMNS && "Only 1..64 columns allowed!"); - if (flags & ImGuiTableFlags_ScrollX) - IM_ASSERT(inner_width >= 0.0f); - - // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping rules may evolve. - const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0; - const ImVec2 avail_size = GetContentRegionAvail(); - ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f); - ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); - if (use_child_window && IsClippedEx(outer_rect, 0)) - { - ItemSize(outer_rect); - return false; - } - - // Acquire storage for the table - ImGuiTable* table = g.Tables.GetOrAddByKey(id); - const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceCurrent + 1; - const ImGuiID instance_id = id + instance_no; - const ImGuiTableFlags table_last_flags = table->Flags; - if (instance_no > 0) - IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID"); - - // Acquire temporary buffers - const int table_idx = g.Tables.GetIndex(table); - if (++g.TablesTempDataStacked > g.TablesTempData.Size) - g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData()); - ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1]; - temp_data->TableIndex = table_idx; - table->DrawSplitter = &table->TempData->DrawSplitter; - table->DrawSplitter->Clear(); - - // Fix flags - table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0; - flags = TableFixFlags(flags, outer_window); - - // Initialize - table->ID = id; - table->Flags = flags; - table->InstanceCurrent = (ImS16)instance_no; - table->LastFrameActive = g.FrameCount; - table->OuterWindow = table->InnerWindow = outer_window; - table->ColumnsCount = columns_count; - table->IsLayoutLocked = false; - table->InnerWidth = inner_width; - temp_data->UserOuterSize = outer_size; - if (instance_no > 0 && table->InstanceDataExtra.Size < instance_no) - table->InstanceDataExtra.push_back(ImGuiTableInstanceData()); - - // When not using a child window, WorkRect.Max will grow as we append contents. - if (use_child_window) - { - // Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent - // (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing) - ImVec2 override_content_size(FLT_MAX, FLT_MAX); - if ((flags & ImGuiTableFlags_ScrollX) && !(flags & ImGuiTableFlags_ScrollY)) - override_content_size.y = FLT_MIN; - - // Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and - // never lead to any scrolling). We don't handle inner_width < 0.0f, we could potentially use it to right-align - // based on the right side of the child window work rect, which would require knowing ahead if we are going to - // have decoration taking horizontal spaces (typically a vertical scrollbar). - if ((flags & ImGuiTableFlags_ScrollX) && inner_width > 0.0f) - override_content_size.x = inner_width; - - if (override_content_size.x != FLT_MAX || override_content_size.y != FLT_MAX) - SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f)); - - // Reset scroll if we are reactivating it - if ((table_last_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0) - SetNextWindowScroll(ImVec2(0.0f, 0.0f)); - - // Create scrolling region (without border and zero window padding) - ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; - BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags); - table->InnerWindow = g.CurrentWindow; - table->WorkRect = table->InnerWindow->WorkRect; - table->OuterRect = table->InnerWindow->Rect(); - table->InnerRect = table->InnerWindow->InnerRect; - IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f); - } - else - { - // For non-scrolling tables, WorkRect == OuterRect == InnerRect. - // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable(). - table->WorkRect = table->OuterRect = table->InnerRect = outer_rect; - } - - // Push a standardized ID for both child-using and not-child-using tables - PushOverrideID(instance_id); - - // Backup a copy of host window members we will modify - ImGuiWindow* inner_window = table->InnerWindow; - table->HostIndentX = inner_window->DC.Indent.x; - table->HostClipRect = inner_window->ClipRect; - table->HostSkipItems = inner_window->SkipItems; - temp_data->HostBackupWorkRect = inner_window->WorkRect; - temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect; - temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; - temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; - temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; - temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; - temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth; - temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; - inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); - - // Padding and Spacing - // - None ........Content..... Pad .....Content........ - // - PadOuter | Pad ..Content..... Pad .....Content.. Pad | - // - PadInner ........Content.. Pad | Pad ..Content........ - // - PadOuter+PadInner | Pad ..Content.. Pad | Pad ..Content.. Pad | - const bool pad_outer_x = (flags & ImGuiTableFlags_NoPadOuterX) ? false : (flags & ImGuiTableFlags_PadOuterX) ? true : (flags & ImGuiTableFlags_BordersOuterV) != 0; - const bool pad_inner_x = (flags & ImGuiTableFlags_NoPadInnerX) ? false : true; - const float inner_spacing_for_border = (flags & ImGuiTableFlags_BordersInnerV) ? TABLE_BORDER_SIZE : 0.0f; - const float inner_spacing_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) == 0) ? g.Style.CellPadding.x : 0.0f; - const float inner_padding_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) != 0) ? g.Style.CellPadding.x : 0.0f; - table->CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border; - table->CellSpacingX2 = inner_spacing_explicit; - table->CellPaddingX = inner_padding_explicit; - table->CellPaddingY = g.Style.CellPadding.y; - - const float outer_padding_for_border = (flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; - const float outer_padding_explicit = pad_outer_x ? g.Style.CellPadding.x : 0.0f; - table->OuterPaddingX = (outer_padding_for_border + outer_padding_explicit) - table->CellPaddingX; - - table->CurrentColumn = -1; - table->CurrentRow = -1; - table->RowBgColorCounter = 0; - table->LastRowFlags = ImGuiTableRowFlags_None; - table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect; - table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width - table->InnerClipRect.ClipWithFull(table->HostClipRect); - table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y; - - table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow - table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() - table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any - table->FreezeColumnsRequest = table->FreezeColumnsCount = 0; - table->IsUnfrozenRows = true; - table->DeclColumnsCount = 0; - - // Using opaque colors facilitate overlapping elements of the grid - table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); - table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); - - // Make table current - g.CurrentTable = table; - outer_window->DC.CurrentTableIdx = table_idx; - if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. - inner_window->DC.CurrentTableIdx = table_idx; - - if ((table_last_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0) - table->IsResetDisplayOrderRequest = true; - - // Mark as used - if (table_idx >= g.TablesLastTimeActive.Size) - g.TablesLastTimeActive.resize(table_idx + 1, -1.0f); - g.TablesLastTimeActive[table_idx] = (float)g.Time; - temp_data->LastTimeActive = (float)g.Time; - table->MemoryCompacted = false; - - // Setup memory buffer (clear data if columns count changed) - ImGuiTableColumn* old_columns_to_preserve = NULL; - void* old_columns_raw_data = NULL; - const int old_columns_count = table->Columns.size(); - if (old_columns_count != 0 && old_columns_count != columns_count) - { - // Attempt to preserve width on column count change (#4046) - old_columns_to_preserve = table->Columns.Data; - old_columns_raw_data = table->RawData; - table->RawData = NULL; - } - if (table->RawData == NULL) - { - TableBeginInitMemory(table, columns_count); - table->IsInitializing = table->IsSettingsRequestLoad = true; - } - if (table->IsResetAllRequest) - TableResetSettings(table); - if (table->IsInitializing) - { - // Initialize - table->SettingsOffset = -1; - table->IsSortSpecsDirty = true; - table->InstanceInteracted = -1; - table->ContextPopupColumn = -1; - table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1; - table->AutoFitSingleColumn = -1; - table->HoveredColumnBody = table->HoveredColumnBorder = -1; - for (int n = 0; n < columns_count; n++) - { - ImGuiTableColumn* column = &table->Columns[n]; - if (old_columns_to_preserve && n < old_columns_count) - { - // FIXME: We don't attempt to preserve column order in this path. - *column = old_columns_to_preserve[n]; - } - else - { - float width_auto = column->WidthAuto; - *column = ImGuiTableColumn(); - column->WidthAuto = width_auto; - column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker - column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true; - } - column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n; - } - } - if (old_columns_raw_data) - IM_FREE(old_columns_raw_data); - - // Load settings - if (table->IsSettingsRequestLoad) - TableLoadSettings(table); - - // Handle DPI/font resize - // This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well. - // It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition. - // FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory. - // This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path. - const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ? - if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit) - { - const float scale_factor = new_ref_scale_unit / table->RefScale; - //IMGUI_DEBUG_PRINT("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor); - for (int n = 0; n < columns_count; n++) - table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor; - } - table->RefScale = new_ref_scale_unit; - - // Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call.. - // This is not strictly necessary but will reduce cases were "out of table" output will be misleading to the user. - // Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option. - inner_window->SkipItems = true; - - // Clear names - // At this point the ->NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn() - if (table->ColumnsNames.Buf.Size > 0) - table->ColumnsNames.Buf.resize(0); - - // Apply queued resizing/reordering/hiding requests - TableBeginApplyRequests(table); - - return true; -} - -// For reference, the average total _allocation count_ for a table is: -// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables) -// + 1 (for table->RawData allocated below) -// + 1 (for table->ColumnsNames, if names are used) -// Shared allocations per number of nested tables -// + 1 (for table->Splitter._Channels) -// + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels) -// Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details. -// Unused channels don't perform their +2 allocations. -void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count) -{ - // Allocate single buffer for our arrays - ImSpanAllocator<3> span_allocator; - span_allocator.Reserve(0, columns_count * sizeof(ImGuiTableColumn)); - span_allocator.Reserve(1, columns_count * sizeof(ImGuiTableColumnIdx)); - span_allocator.Reserve(2, columns_count * sizeof(ImGuiTableCellData), 4); - table->RawData = IM_ALLOC(span_allocator.GetArenaSizeInBytes()); - memset(table->RawData, 0, span_allocator.GetArenaSizeInBytes()); - span_allocator.SetArenaBasePtr(table->RawData); - span_allocator.GetSpan(0, &table->Columns); - span_allocator.GetSpan(1, &table->DisplayOrderToIndex); - span_allocator.GetSpan(2, &table->RowCellData); -} - -// Apply queued resizing/reordering/hiding requests -void ImGui::TableBeginApplyRequests(ImGuiTable* table) -{ - // Handle resizing request - // (We process this at the first TableBegin of the frame) - // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling? - if (table->InstanceCurrent == 0) - { - if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX) - TableSetColumnWidth(table->ResizedColumn, table->ResizedColumnNextWidth); - table->LastResizedColumn = table->ResizedColumn; - table->ResizedColumnNextWidth = FLT_MAX; - table->ResizedColumn = -1; - - // Process auto-fit for single column, which is a special case for stretch columns and fixed columns with FixedSame policy. - // FIXME-TABLE: Would be nice to redistribute available stretch space accordingly to other weights, instead of giving it all to siblings. - if (table->AutoFitSingleColumn != -1) - { - TableSetColumnWidth(table->AutoFitSingleColumn, table->Columns[table->AutoFitSingleColumn].WidthAuto); - table->AutoFitSingleColumn = -1; - } - } - - // Handle reordering request - // Note: we don't clear ReorderColumn after handling the request. - if (table->InstanceCurrent == 0) - { - if (table->HeldHeaderColumn == -1 && table->ReorderColumn != -1) - table->ReorderColumn = -1; - table->HeldHeaderColumn = -1; - if (table->ReorderColumn != -1 && table->ReorderColumnDir != 0) - { - // We need to handle reordering across hidden columns. - // In the configuration below, moving C to the right of E will lead to: - // ... C [D] E ---> ... [D] E C (Column name/index) - // ... 2 3 4 ... 2 3 4 (Display order) - const int reorder_dir = table->ReorderColumnDir; - IM_ASSERT(reorder_dir == -1 || reorder_dir == +1); - IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable); - ImGuiTableColumn* src_column = &table->Columns[table->ReorderColumn]; - ImGuiTableColumn* dst_column = &table->Columns[(reorder_dir == -1) ? src_column->PrevEnabledColumn : src_column->NextEnabledColumn]; - IM_UNUSED(dst_column); - const int src_order = src_column->DisplayOrder; - const int dst_order = dst_column->DisplayOrder; - src_column->DisplayOrder = (ImGuiTableColumnIdx)dst_order; - for (int order_n = src_order + reorder_dir; order_n != dst_order + reorder_dir; order_n += reorder_dir) - table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder -= (ImGuiTableColumnIdx)reorder_dir; - IM_ASSERT(dst_column->DisplayOrder == dst_order - reorder_dir); - - // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[], - // rebuild the later from the former. - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; - table->ReorderColumnDir = 0; - table->IsSettingsDirty = true; - } - } - - // Handle display order reset request - if (table->IsResetDisplayOrderRequest) - { - for (int n = 0; n < table->ColumnsCount; n++) - table->DisplayOrderToIndex[n] = table->Columns[n].DisplayOrder = (ImGuiTableColumnIdx)n; - table->IsResetDisplayOrderRequest = false; - table->IsSettingsDirty = true; - } -} - -// Adjust flags: default width mode + stretch columns are not allowed when auto extending -static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags flags_in) -{ - ImGuiTableColumnFlags flags = flags_in; - - // Sizing Policy - if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0) - { - const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_); - if (table_sizing_policy == ImGuiTableFlags_SizingFixedFit || table_sizing_policy == ImGuiTableFlags_SizingFixedSame) - flags |= ImGuiTableColumnFlags_WidthFixed; - else - flags |= ImGuiTableColumnFlags_WidthStretch; - } - else - { - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_WidthMask_)); // Check that only 1 of each set is used. - } - - // Resize - if ((table->Flags & ImGuiTableFlags_Resizable) == 0) - flags |= ImGuiTableColumnFlags_NoResize; - - // Sorting - if ((flags & ImGuiTableColumnFlags_NoSortAscending) && (flags & ImGuiTableColumnFlags_NoSortDescending)) - flags |= ImGuiTableColumnFlags_NoSort; - - // Indentation - if ((flags & ImGuiTableColumnFlags_IndentMask_) == 0) - flags |= (table->Columns.index_from_ptr(column) == 0) ? ImGuiTableColumnFlags_IndentEnable : ImGuiTableColumnFlags_IndentDisable; - - // Alignment - //if ((flags & ImGuiTableColumnFlags_AlignMask_) == 0) - // flags |= ImGuiTableColumnFlags_AlignCenter; - //IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_AlignMask_)); // Check that only 1 of each set is used. - - // Preserve status flags - column->Flags = flags | (column->Flags & ImGuiTableColumnFlags_StatusMask_); - - // Build an ordered list of available sort directions - column->SortDirectionsAvailCount = column->SortDirectionsAvailMask = column->SortDirectionsAvailList = 0; - if (table->Flags & ImGuiTableFlags_Sortable) - { - int count = 0, mask = 0, list = 0; - if ((flags & ImGuiTableColumnFlags_PreferSortAscending) != 0 && (flags & ImGuiTableColumnFlags_NoSortAscending) == 0) { mask |= 1 << ImGuiSortDirection_Ascending; list |= ImGuiSortDirection_Ascending << (count << 1); count++; } - if ((flags & ImGuiTableColumnFlags_PreferSortDescending) != 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; } - if ((flags & ImGuiTableColumnFlags_PreferSortAscending) == 0 && (flags & ImGuiTableColumnFlags_NoSortAscending) == 0) { mask |= 1 << ImGuiSortDirection_Ascending; list |= ImGuiSortDirection_Ascending << (count << 1); count++; } - if ((flags & ImGuiTableColumnFlags_PreferSortDescending) == 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; } - if ((table->Flags & ImGuiTableFlags_SortTristate) || count == 0) { mask |= 1 << ImGuiSortDirection_None; count++; } - column->SortDirectionsAvailList = (ImU8)list; - column->SortDirectionsAvailMask = (ImU8)mask; - column->SortDirectionsAvailCount = (ImU8)count; - ImGui::TableFixColumnSortDirection(table, column); - } -} - -// Layout columns for the frame. This is in essence the followup to BeginTable(). -// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() to be called first. -// FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for _WidthAuto columns. -// Increase feedback side-effect with widgets relying on WorkRect.Max.x... Maybe provide a default distribution for _WidthAuto columns? -void ImGui::TableUpdateLayout(ImGuiTable* table) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(table->IsLayoutLocked == false); - - const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_); - table->IsDefaultDisplayOrder = true; - table->ColumnsEnabledCount = 0; - table->EnabledMaskByIndex = 0x00; - table->EnabledMaskByDisplayOrder = 0x00; - table->LeftMostEnabledColumn = -1; - table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE - - // [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns. - // Process columns in their visible orders as we are building the Prev/Next indices. - int count_fixed = 0; // Number of columns that have fixed sizing policies - int count_stretch = 0; // Number of columns that have stretch sizing policies - int prev_visible_column_idx = -1; - bool has_auto_fit_request = false; - bool has_resizable = false; - float stretch_sum_width_auto = 0.0f; - float fixed_max_width_auto = 0.0f; - for (int order_n = 0; order_n < table->ColumnsCount; order_n++) - { - const int column_n = table->DisplayOrderToIndex[order_n]; - if (column_n != order_n) - table->IsDefaultDisplayOrder = false; - ImGuiTableColumn* column = &table->Columns[column_n]; - - // Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame. - // It would easily work without but we're not ready to guarantee it since e.g. names need resubmission anyway. - // We take a slight shortcut but in theory we could be calling TableSetupColumn() here with dummy values, it should yield the same effect. - if (table->DeclColumnsCount <= column_n) - { - TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None); - column->NameOffset = -1; - column->UserID = 0; - column->InitStretchWeightOrWidth = -1.0f; - } - - // Update Enabled state, mark settings and sort specs dirty - if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide)) - column->IsUserEnabledNextFrame = true; - if (column->IsUserEnabled != column->IsUserEnabledNextFrame) - { - column->IsUserEnabled = column->IsUserEnabledNextFrame; - table->IsSettingsDirty = true; - } - column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0; - - if (column->SortOrder != -1 && !column->IsEnabled) - table->IsSortSpecsDirty = true; - if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_SortMulti)) - table->IsSortSpecsDirty = true; - - // Auto-fit unsized columns - const bool start_auto_fit = (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? (column->WidthRequest < 0.0f) : (column->StretchWeight < 0.0f); - if (start_auto_fit) - column->AutoFitQueue = column->CannotSkipItemsQueue = (1 << 3) - 1; // Fit for three frames - - if (!column->IsEnabled) - { - column->IndexWithinEnabledSet = -1; - continue; - } - - // Mark as enabled and link to previous/next enabled column - column->PrevEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx; - column->NextEnabledColumn = -1; - if (prev_visible_column_idx != -1) - table->Columns[prev_visible_column_idx].NextEnabledColumn = (ImGuiTableColumnIdx)column_n; - else - table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n; - column->IndexWithinEnabledSet = table->ColumnsEnabledCount++; - table->EnabledMaskByIndex |= (ImU64)1 << column_n; - table->EnabledMaskByDisplayOrder |= (ImU64)1 << column->DisplayOrder; - prev_visible_column_idx = column_n; - IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder); - - // Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping) - // Combine width from regular rows + width from headers unless requested not to. - if (!column->IsPreserveWidthAuto) - column->WidthAuto = TableGetColumnWidthAuto(table, column); - - // Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto) - const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0; - if (column_is_resizable) - has_resizable = true; - if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f && !column_is_resizable) - column->WidthAuto = column->InitStretchWeightOrWidth; - - if (column->AutoFitQueue != 0x00) - has_auto_fit_request = true; - if (column->Flags & ImGuiTableColumnFlags_WidthStretch) - { - stretch_sum_width_auto += column->WidthAuto; - count_stretch++; - } - else - { - fixed_max_width_auto = ImMax(fixed_max_width_auto, column->WidthAuto); - count_fixed++; - } - } - if ((table->Flags & ImGuiTableFlags_Sortable) && table->SortSpecsCount == 0 && !(table->Flags & ImGuiTableFlags_SortTristate)) - table->IsSortSpecsDirty = true; - table->RightMostEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx; - IM_ASSERT(table->LeftMostEnabledColumn >= 0 && table->RightMostEnabledColumn >= 0); - - // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible - // to avoid the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). - // FIXME-TABLE: for always auto-resizing columns may not want to do that all the time. - if (has_auto_fit_request && table->OuterWindow != table->InnerWindow) - table->InnerWindow->SkipItems = false; - if (has_auto_fit_request) - table->IsSettingsDirty = true; - - // [Part 3] Fix column flags and record a few extra information. - float sum_width_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding. - float stretch_sum_weights = 0.0f; // Sum of all weights for stretch columns. - table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - if (!(table->EnabledMaskByIndex & ((ImU64)1 << column_n))) - continue; - ImGuiTableColumn* column = &table->Columns[column_n]; - - const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0; - if (column->Flags & ImGuiTableColumnFlags_WidthFixed) - { - // Apply same widths policy - float width_auto = column->WidthAuto; - if (table_sizing_policy == ImGuiTableFlags_SizingFixedSame && (column->AutoFitQueue != 0x00 || !column_is_resizable)) - width_auto = fixed_max_width_auto; - - // Apply automatic width - // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!) - if (column->AutoFitQueue != 0x00) - column->WidthRequest = width_auto; - else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n))) - column->WidthRequest = width_auto; - - // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets - // (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very - // large height (= first frame scrollbar display very off + clipper would skip lots of items). - // This is merely making the side-effect less extreme, but doesn't properly fixes it. - // FIXME: Move this to ->WidthGiven to avoid temporary lossyless? - // FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller. - if (column->AutoFitQueue > 0x01 && table->IsInitializing && !column->IsPreserveWidthAuto) - column->WidthRequest = ImMax(column->WidthRequest, table->MinColumnWidth * 4.0f); // FIXME-TABLE: Another constant/scale? - sum_width_requests += column->WidthRequest; - } - else - { - // Initialize stretch weight - if (column->AutoFitQueue != 0x00 || column->StretchWeight < 0.0f || !column_is_resizable) - { - if (column->InitStretchWeightOrWidth > 0.0f) - column->StretchWeight = column->InitStretchWeightOrWidth; - else if (table_sizing_policy == ImGuiTableFlags_SizingStretchProp) - column->StretchWeight = (column->WidthAuto / stretch_sum_width_auto) * count_stretch; - else - column->StretchWeight = 1.0f; - } - - stretch_sum_weights += column->StretchWeight; - if (table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder > column->DisplayOrder) - table->LeftMostStretchedColumn = (ImGuiTableColumnIdx)column_n; - if (table->RightMostStretchedColumn == -1 || table->Columns[table->RightMostStretchedColumn].DisplayOrder < column->DisplayOrder) - table->RightMostStretchedColumn = (ImGuiTableColumnIdx)column_n; - } - column->IsPreserveWidthAuto = false; - sum_width_requests += table->CellPaddingX * 2.0f; - } - table->ColumnsEnabledFixedCount = (ImGuiTableColumnIdx)count_fixed; - table->ColumnsStretchSumWeights = stretch_sum_weights; - - // [Part 4] Apply final widths based on requested widths - const ImRect work_rect = table->WorkRect; - const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); - const float width_avail = ((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth(); - const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests; - float width_remaining_for_stretched_columns = width_avail_for_stretched_columns; - table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - if (!(table->EnabledMaskByIndex & ((ImU64)1 << column_n))) - continue; - ImGuiTableColumn* column = &table->Columns[column_n]; - - // Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest) - if (column->Flags & ImGuiTableColumnFlags_WidthStretch) - { - float weight_ratio = column->StretchWeight / stretch_sum_weights; - column->WidthRequest = IM_FLOOR(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f); - width_remaining_for_stretched_columns -= column->WidthRequest; - } - - // [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column - // See additional comments in TableSetColumnWidth(). - if (column->NextEnabledColumn == -1 && table->LeftMostStretchedColumn != -1) - column->Flags |= ImGuiTableColumnFlags_NoDirectResize_; - - // Assign final width, record width in case we will need to shrink - column->WidthGiven = ImFloor(ImMax(column->WidthRequest, table->MinColumnWidth)); - table->ColumnsGivenWidth += column->WidthGiven; - } - - // [Part 5] Redistribute stretch remainder width due to rounding (remainder width is < 1.0f * number of Stretch column). - // Using right-to-left distribution (more likely to match resizing cursor). - if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths)) - for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--) - { - if (!(table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n))) - continue; - ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]]; - if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch)) - continue; - column->WidthRequest += 1.0f; - column->WidthGiven += 1.0f; - width_remaining_for_stretched_columns -= 1.0f; - } - - ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); - table->HoveredColumnBody = -1; - table->HoveredColumnBorder = -1; - const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight)); - const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0); - - // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column - // Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping. - int visible_n = 0; - bool offset_x_frozen = (table->FreezeColumnsCount > 0); - float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1; - ImRect host_clip_rect = table->InnerClipRect; - //host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2; - table->VisibleMaskByIndex = 0x00; - table->RequestOutputMaskByIndex = 0x00; - for (int order_n = 0; order_n < table->ColumnsCount; order_n++) - { - const int column_n = table->DisplayOrderToIndex[order_n]; - ImGuiTableColumn* column = &table->Columns[column_n]; - - column->NavLayerCurrent = (ImS8)((table->FreezeRowsCount > 0 || column_n < table->FreezeColumnsCount) ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); - - if (offset_x_frozen && table->FreezeColumnsCount == visible_n) - { - offset_x += work_rect.Min.x - table->OuterRect.Min.x; - offset_x_frozen = false; - } - - // Clear status flags - column->Flags &= ~ImGuiTableColumnFlags_StatusMask_; - - if ((table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n)) == 0) - { - // Hidden column: clear a few fields and we are done with it for the remainder of the function. - // We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper. - column->MinX = column->MaxX = column->WorkMinX = column->ClipRect.Min.x = column->ClipRect.Max.x = offset_x; - column->WidthGiven = 0.0f; - column->ClipRect.Min.y = work_rect.Min.y; - column->ClipRect.Max.y = FLT_MAX; - column->ClipRect.ClipWithFull(host_clip_rect); - column->IsVisibleX = column->IsVisibleY = column->IsRequestOutput = false; - column->IsSkipItems = true; - column->ItemWidth = 1.0f; - continue; - } - - // Detect hovered column - if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x) - table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n; - - // Lock start position - column->MinX = offset_x; - - // Lock width based on start position and minimum/maximum width for this position - float max_width = TableGetMaxColumnWidth(table, column_n); - column->WidthGiven = ImMin(column->WidthGiven, max_width); - column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth)); - column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; - - // Lock other positions - // - ClipRect.Min.x: Because merging draw commands doesn't compare min boundaries, we make ClipRect.Min.x match left bounds to be consistent regardless of merging. - // - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column. - // - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow. - // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter. - column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1; - column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max - column->ItemWidth = ImFloor(column->WidthGiven * 0.65f); - column->ClipRect.Min.x = column->MinX; - column->ClipRect.Min.y = work_rect.Min.y; - column->ClipRect.Max.x = column->MaxX; //column->WorkMaxX; - column->ClipRect.Max.y = FLT_MAX; - column->ClipRect.ClipWithFull(host_clip_rect); - - // Mark column as Clipped (not in sight) - // Note that scrolling tables (where inner_window != outer_window) handle Y clipped earlier in BeginTable() so IsVisibleY really only applies to non-scrolling tables. - // FIXME-TABLE: Because InnerClipRect.Max.y is conservatively ==outer_window->ClipRect.Max.y, we never can mark columns _Above_ the scroll line as not IsVisibleY. - // Taking advantage of LastOuterHeight would yield good results there... - // FIXME-TABLE: Y clipping is disabled because it effectively means not submitting will reduce contents width which is fed to outer_window->DC.CursorMaxPos.x, - // and this may be used (e.g. typically by outer_window using AlwaysAutoResize or outer_window's horizontal scrollbar, but could be something else). - // Possible solution to preserve last known content width for clipped column. Test 'table_reported_size' fails when enabling Y clipping and window is resized small. - column->IsVisibleX = (column->ClipRect.Max.x > column->ClipRect.Min.x); - column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y); - const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY; - if (is_visible) - table->VisibleMaskByIndex |= ((ImU64)1 << column_n); - - // Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output. - column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0; - if (column->IsRequestOutput) - table->RequestOutputMaskByIndex |= ((ImU64)1 << column_n); - - // Mark column as SkipItems (ignoring all items/layout) - column->IsSkipItems = !column->IsEnabled || table->HostSkipItems; - if (column->IsSkipItems) - IM_ASSERT(!is_visible); - - // Update status flags - column->Flags |= ImGuiTableColumnFlags_IsEnabled; - if (is_visible) - column->Flags |= ImGuiTableColumnFlags_IsVisible; - if (column->SortOrder != -1) - column->Flags |= ImGuiTableColumnFlags_IsSorted; - if (table->HoveredColumnBody == column_n) - column->Flags |= ImGuiTableColumnFlags_IsHovered; - - // Alignment - // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in - // many cases (to be able to honor this we might be able to store a log of cells width, per row, for - // visible rows, but nav/programmatic scroll would have visible artifacts.) - //if (column->Flags & ImGuiTableColumnFlags_AlignRight) - // column->WorkMinX = ImMax(column->WorkMinX, column->MaxX - column->ContentWidthRowsUnfrozen); - //else if (column->Flags & ImGuiTableColumnFlags_AlignCenter) - // column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f); - - // Reset content width variables - column->ContentMaxXFrozen = column->ContentMaxXUnfrozen = column->WorkMinX; - column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX; - - // Don't decrement auto-fit counters until container window got a chance to submit its items - if (table->HostSkipItems == false) - { - column->AutoFitQueue >>= 1; - column->CannotSkipItemsQueue >>= 1; - } - - if (visible_n < table->FreezeColumnsCount) - host_clip_rect.Min.x = ImClamp(column->MaxX + TABLE_BORDER_SIZE, host_clip_rect.Min.x, host_clip_rect.Max.x); - - offset_x += column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; - visible_n++; - } - - // [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it) - // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either - // because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu. - const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); - if (is_hovering_table && table->HoveredColumnBody == -1) - { - if (g.IO.MousePos.x >= unused_x1) - table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; - } - if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable)) - table->Flags &= ~ImGuiTableFlags_Resizable; - - // [Part 8] Lock actual OuterRect/WorkRect right-most position. - // This is done late to handle the case of fixed-columns tables not claiming more widths that they need. - // Because of this we are careful with uses of WorkRect and InnerClipRect before this point. - if (table->RightMostStretchedColumn != -1) - table->Flags &= ~ImGuiTableFlags_NoHostExtendX; - if (table->Flags & ImGuiTableFlags_NoHostExtendX) - { - table->OuterRect.Max.x = table->WorkRect.Max.x = unused_x1; - table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1); - } - table->InnerWindow->ParentWorkRect = table->WorkRect; - table->BorderX1 = table->InnerClipRect.Min.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : -1.0f); - table->BorderX2 = table->InnerClipRect.Max.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : +1.0f); - - // [Part 9] Allocate draw channels and setup background cliprect - TableSetupDrawChannels(table); - - // [Part 10] Hit testing on borders - if (table->Flags & ImGuiTableFlags_Resizable) - TableUpdateBorders(table); - table_instance->LastFirstRowHeight = 0.0f; - table->IsLayoutLocked = true; - table->IsUsingHeaders = false; - - // [Part 11] Context menu - if (table->IsContextPopupOpen && table->InstanceCurrent == table->InstanceInteracted) - { - const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID); - if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings)) - { - TableDrawContextMenu(table); - EndPopup(); - } - else - { - table->IsContextPopupOpen = false; - } - } - - // [Part 13] Sanitize and build sort specs before we have a change to use them for display. - // This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change) - if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable)) - TableSortSpecsBuild(table); - - // Initial state - ImGuiWindow* inner_window = table->InnerWindow; - if (table->Flags & ImGuiTableFlags_NoClip) - table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); - else - inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false); -} - -// Process hit-testing on resizing borders. Actual size change will be applied in EndTable() -// - Set table->HoveredColumnBorder with a short delay/timer to reduce feedback noise -// - Submit ahead of table contents and header, use ImGuiButtonFlags_AllowItemOverlap to prioritize widgets -// overlapping the same area. -void ImGui::TableUpdateBorders(ImGuiTable* table) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(table->Flags & ImGuiTableFlags_Resizable); - - // At this point OuterRect height may be zero or under actual final height, so we rely on temporal coherency and - // use the final height from last frame. Because this is only affecting _interaction_ with columns, it is not - // really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height). - // Actual columns highlight/render will be performed in EndTable() and not be affected. - ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); - const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS; - const float hit_y1 = table->OuterRect.Min.y; - const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight); - const float hit_y2_head = hit_y1 + table_instance->LastFirstRowHeight; - - for (int order_n = 0; order_n < table->ColumnsCount; order_n++) - { - if (!(table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n))) - continue; - - const int column_n = table->DisplayOrderToIndex[order_n]; - ImGuiTableColumn* column = &table->Columns[column_n]; - if (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) - continue; - - // ImGuiTableFlags_NoBordersInBodyUntilResize will be honored in TableDrawBorders() - const float border_y2_hit = (table->Flags & ImGuiTableFlags_NoBordersInBody) ? hit_y2_head : hit_y2_body; - if ((table->Flags & ImGuiTableFlags_NoBordersInBody) && table->IsUsingHeaders == false) - continue; - - if (!column->IsVisibleX && table->LastResizedColumn != column_n) - continue; - - ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent); - ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit); - //GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100)); - KeepAliveID(column_id); - - bool hovered = false, held = false; - bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); - if (pressed && IsMouseDoubleClicked(0)) - { - TableSetColumnWidthAutoSingle(table, column_n); - ClearActiveID(); - held = hovered = false; - } - if (held) - { - if (table->LastResizedColumn == -1) - table->ResizeLockMinContentsX2 = table->RightMostEnabledColumn != -1 ? table->Columns[table->RightMostEnabledColumn].MaxX : -FLT_MAX; - table->ResizedColumn = (ImGuiTableColumnIdx)column_n; - table->InstanceInteracted = table->InstanceCurrent; - } - if ((hovered && g.HoveredIdTimer > TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER) || held) - { - table->HoveredColumnBorder = (ImGuiTableColumnIdx)column_n; - SetMouseCursor(ImGuiMouseCursor_ResizeEW); - } - } -} - -void ImGui::EndTable() -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - IM_ASSERT(table != NULL && "Only call EndTable() if BeginTable() returns true!"); - - // This assert would be very useful to catch a common error... unfortunately it would probably trigger in some - // cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border) - //IM_ASSERT(table->IsLayoutLocked && "Table unused: never called TableNextRow(), is that the intent?"); - - // If the user never got to call TableNextRow() or TableNextColumn(), we call layout ourselves to ensure all our - // code paths are consistent (instead of just hoping that TableBegin/TableEnd will work), get borders drawn, etc. - if (!table->IsLayoutLocked) - TableUpdateLayout(table); - - const ImGuiTableFlags flags = table->Flags; - ImGuiWindow* inner_window = table->InnerWindow; - ImGuiWindow* outer_window = table->OuterWindow; - ImGuiTableTempData* temp_data = table->TempData; - IM_ASSERT(inner_window == g.CurrentWindow); - IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow); - - if (table->IsInsideRow) - TableEndRow(table); - - // Context menu in columns body - if (flags & ImGuiTableFlags_ContextMenuInBody) - if (table->HoveredColumnBody != -1 && !IsAnyItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) - TableOpenContextMenu((int)table->HoveredColumnBody); - - // Finalize table height - ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); - inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize; - inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize; - inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos; - const float inner_content_max_y = table->RowPosY2; - IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y); - if (inner_window != outer_window) - inner_window->DC.CursorMaxPos.y = inner_content_max_y; - else if (!(flags & ImGuiTableFlags_NoHostExtendY)) - table->OuterRect.Max.y = table->InnerRect.Max.y = ImMax(table->OuterRect.Max.y, inner_content_max_y); // Patch OuterRect/InnerRect height - table->WorkRect.Max.y = ImMax(table->WorkRect.Max.y, table->OuterRect.Max.y); - table_instance->LastOuterHeight = table->OuterRect.GetHeight(); - - // Setup inner scrolling range - // FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y, - // but since the later is likely to be impossible to do we'd rather update both axises together. - if (table->Flags & ImGuiTableFlags_ScrollX) - { - const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; - float max_pos_x = table->InnerWindow->DC.CursorMaxPos.x; - if (table->RightMostEnabledColumn != -1) - max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border); - if (table->ResizedColumn != -1) - max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2); - table->InnerWindow->DC.CursorMaxPos.x = max_pos_x; - } - - // Pop clipping rect - if (!(flags & ImGuiTableFlags_NoClip)) - inner_window->DrawList->PopClipRect(); - inner_window->ClipRect = inner_window->DrawList->_ClipRectStack.back(); - - // Draw borders - if ((flags & ImGuiTableFlags_Borders) != 0) - TableDrawBorders(table); - -#if 0 - // Strip out dummy channel draw calls - // We have no way to prevent user submitting direct ImDrawList calls into a hidden column (but ImGui:: calls will be clipped out) - // Pros: remove draw calls which will have no effect. since they'll have zero-size cliprect they may be early out anyway. - // Cons: making it harder for users watching metrics/debugger to spot the wasted vertices. - if (table->DummyDrawChannel != (ImGuiTableColumnIdx)-1) - { - ImDrawChannel* dummy_channel = &table->DrawSplitter._Channels[table->DummyDrawChannel]; - dummy_channel->_CmdBuffer.resize(0); - dummy_channel->_IdxBuffer.resize(0); - } -#endif - - // Flatten channels and merge draw calls - ImDrawListSplitter* splitter = table->DrawSplitter; - splitter->SetCurrentChannel(inner_window->DrawList, 0); - if ((table->Flags & ImGuiTableFlags_NoClip) == 0) - TableMergeDrawChannels(table); - splitter->Merge(inner_window->DrawList); - - // Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable() - float auto_fit_width_for_fixed = 0.0f; - float auto_fit_width_for_stretched = 0.0f; - float auto_fit_width_for_stretched_min = 0.0f; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - if (table->EnabledMaskByIndex & ((ImU64)1 << column_n)) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - float column_width_request = ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize)) ? column->WidthRequest : TableGetColumnWidthAuto(table, column); - if (column->Flags & ImGuiTableColumnFlags_WidthFixed) - auto_fit_width_for_fixed += column_width_request; - else - auto_fit_width_for_stretched += column_width_request; - if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) && (column->Flags & ImGuiTableColumnFlags_NoResize) != 0) - auto_fit_width_for_stretched_min = ImMax(auto_fit_width_for_stretched_min, column_width_request / (column->StretchWeight / table->ColumnsStretchSumWeights)); - } - const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); - table->ColumnsAutoFitWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount + auto_fit_width_for_fixed + ImMax(auto_fit_width_for_stretched, auto_fit_width_for_stretched_min); - - // Update scroll - if ((table->Flags & ImGuiTableFlags_ScrollX) == 0 && inner_window != outer_window) - { - inner_window->Scroll.x = 0.0f; - } - else if (table->LastResizedColumn != -1 && table->ResizedColumn == -1 && inner_window->ScrollbarX && table->InstanceInteracted == table->InstanceCurrent) - { - // When releasing a column being resized, scroll to keep the resulting column in sight - const float neighbor_width_to_keep_visible = table->MinColumnWidth + table->CellPaddingX * 2.0f; - ImGuiTableColumn* column = &table->Columns[table->LastResizedColumn]; - if (column->MaxX < table->InnerClipRect.Min.x) - SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x - neighbor_width_to_keep_visible, 1.0f); - else if (column->MaxX > table->InnerClipRect.Max.x) - SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x + neighbor_width_to_keep_visible, 1.0f); - } - - // Apply resizing/dragging at the end of the frame - if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted) - { - ImGuiTableColumn* column = &table->Columns[table->ResizedColumn]; - const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + TABLE_RESIZE_SEPARATOR_HALF_THICKNESS); - const float new_width = ImFloor(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f); - table->ResizedColumnNextWidth = new_width; - } - - // Pop from id stack - IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table->ID + table->InstanceCurrent, "Mismatching PushID/PopID!"); - IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); - PopID(); - - // Restore window data that we modified - const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos; - inner_window->WorkRect = temp_data->HostBackupWorkRect; - inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect; - inner_window->SkipItems = table->HostSkipItems; - outer_window->DC.CursorPos = table->OuterRect.Min; - outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth; - outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize; - outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset; - - // Layout in outer window - // (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding - // CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414) - if (inner_window != outer_window) - { - EndChild(); - } - else - { - ItemSize(table->OuterRect.GetSize()); - ItemAdd(table->OuterRect, 0); - } - - // Override declared contents width/height to enable auto-resize while not needlessly adding a scrollbar - if (table->Flags & ImGuiTableFlags_NoHostExtendX) - { - // FIXME-TABLE: Could we remove this section? - // ColumnsAutoFitWidth may be one frame ahead here since for Fixed+NoResize is calculated from latest contents - IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0); - outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth); - } - else if (temp_data->UserOuterSize.x <= 0.0f) - { - const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f; - outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x); - outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth)); - } - else - { - outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x); - } - if (temp_data->UserOuterSize.y <= 0.0f) - { - const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f; - outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y); - outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y)); - } - else - { - // OuterRect.Max.y may already have been pushed downward from the initial value (unless ImGuiTableFlags_NoHostExtendY is set) - outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, table->OuterRect.Max.y); - } - - // Save settings - if (table->IsSettingsDirty) - TableSaveSettings(table); - table->IsInitializing = false; - - // Clear or restore current table, if any - IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table); - IM_ASSERT(g.TablesTempDataStacked > 0); - temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL; - g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; - if (g.CurrentTable) - { - g.CurrentTable->TempData = temp_data; - g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter; - } - outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1; -} - -// See "COLUMN SIZING POLICIES" comments at the top of this file -// If (init_width_or_weight <= 0.0f) it is ignored -void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!"); - IM_ASSERT(table->IsLayoutLocked == false && "Need to call call TableSetupColumn() before first row!"); - IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()"); - if (table->DeclColumnsCount >= table->ColumnsCount) - { - IM_ASSERT_USER_ERROR(table->DeclColumnsCount < table->ColumnsCount, "Called TableSetupColumn() too many times!"); - return; - } - - ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount]; - table->DeclColumnsCount++; - - // Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa. - // Give a grace to users of ImGuiTableFlags_ScrollX. - if (table->IsDefaultSizingPolicy && (flags & ImGuiTableColumnFlags_WidthMask_) == 0 && (flags & ImGuiTableFlags_ScrollX) == 0) - IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column."); - - // When passing a width automatically enforce WidthFixed policy - // (whereas TableSetupColumnFlags would default to WidthAuto if table is not Resizable) - if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f) - if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) - flags |= ImGuiTableColumnFlags_WidthFixed; - - TableSetupColumnFlags(table, column, flags); - column->UserID = user_id; - flags = column->Flags; - - // Initialize defaults - column->InitStretchWeightOrWidth = init_width_or_weight; - if (table->IsInitializing) - { - // Init width or weight - if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f) - { - if ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) - column->WidthRequest = init_width_or_weight; - if (flags & ImGuiTableColumnFlags_WidthStretch) - column->StretchWeight = (init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f; - - // Disable auto-fit if an explicit width/weight has been specified - if (init_width_or_weight > 0.0f) - column->AutoFitQueue = 0x00; - } - - // Init default visibility/sort state - if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0) - column->IsUserEnabled = column->IsUserEnabledNextFrame = false; - if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0) - { - column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs. - column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending); - } - } - - // Store name (append with zero-terminator in contiguous buffer) - column->NameOffset = -1; - if (label != NULL && label[0] != 0) - { - column->NameOffset = (ImS16)table->ColumnsNames.size(); - table->ColumnsNames.append(label, label + strlen(label) + 1); - } -} - -// [Public] -void ImGui::TableSetupScrollFreeze(int columns, int rows) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!"); - IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!"); - IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS); - IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit - - table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0; - table->FreezeColumnsCount = (table->InnerWindow->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0; - table->FreezeRowsRequest = (table->Flags & ImGuiTableFlags_ScrollY) ? (ImGuiTableColumnIdx)rows : 0; - table->FreezeRowsCount = (table->InnerWindow->Scroll.y != 0.0f) ? table->FreezeRowsRequest : 0; - table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b - - // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered. - // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section) - for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++) - { - int order_n = table->DisplayOrderToIndex[column_n]; - if (order_n != column_n && order_n >= table->FreezeColumnsRequest) - { - ImSwap(table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder, table->Columns[table->DisplayOrderToIndex[column_n]].DisplayOrder); - ImSwap(table->DisplayOrderToIndex[order_n], table->DisplayOrderToIndex[column_n]); - } - } -} - -//----------------------------------------------------------------------------- -// [SECTION] Tables: Simple accessors -//----------------------------------------------------------------------------- -// - TableGetColumnCount() -// - TableGetColumnName() -// - TableGetColumnName() [Internal] -// - TableSetColumnEnabled() -// - TableGetColumnFlags() -// - TableGetCellBgRect() [Internal] -// - TableGetColumnResizeID() [Internal] -// - TableGetHoveredColumn() [Internal] -// - TableSetBgColor() -//----------------------------------------------------------------------------- - -int ImGui::TableGetColumnCount() -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - return table ? table->ColumnsCount : 0; -} - -const char* ImGui::TableGetColumnName(int column_n) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - if (!table) - return NULL; - if (column_n < 0) - column_n = table->CurrentColumn; - return TableGetColumnName(table, column_n); -} - -const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n) -{ - if (table->IsLayoutLocked == false && column_n >= table->DeclColumnsCount) - return ""; // NameOffset is invalid at this point - const ImGuiTableColumn* column = &table->Columns[column_n]; - if (column->NameOffset == -1) - return ""; - return &table->ColumnsNames.Buf[column->NameOffset]; -} - -// Change user accessible enabled/disabled state of a column (often perceived as "showing/hiding" from users point of view) -// Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) -// - Require table to have the ImGuiTableFlags_Hideable flag because we are manipulating user accessible state. -// - Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable(). -// - For the getter you can test (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) != 0. -// - Alternative: the ImGuiTableColumnFlags_Disabled is an overriding/master disable flag which will also hide the column from context menu. -void ImGui::TableSetColumnEnabled(int column_n, bool enabled) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - IM_ASSERT(table != NULL); - if (!table) - return; - IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above - if (column_n < 0) - column_n = table->CurrentColumn; - IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); - ImGuiTableColumn* column = &table->Columns[column_n]; - column->IsUserEnabledNextFrame = enabled; -} - -// We allow querying for an extra column in order to poll the IsHovered state of the right-most section -ImGuiTableColumnFlags ImGui::TableGetColumnFlags(int column_n) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - if (!table) - return ImGuiTableColumnFlags_None; - if (column_n < 0) - column_n = table->CurrentColumn; - if (column_n == table->ColumnsCount) - return (table->HoveredColumnBody == column_n) ? ImGuiTableColumnFlags_IsHovered : ImGuiTableColumnFlags_None; - return table->Columns[column_n].Flags; -} - -// Return the cell rectangle based on currently known height. -// - Important: we generally don't know our row height until the end of the row, so Max.y will be incorrect in many situations. -// The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it, or in TableEndRow() when we locked that height. -// - Important: if ImGuiTableFlags_PadOuterX is set but ImGuiTableFlags_PadInnerX is not set, the outer-most left and right -// columns report a small offset so their CellBgRect can extend up to the outer border. -// FIXME: But the rendering code in TableEndRow() nullifies that with clamping required for scrolling. -ImRect ImGui::TableGetCellBgRect(const ImGuiTable* table, int column_n) -{ - const ImGuiTableColumn* column = &table->Columns[column_n]; - float x1 = column->MinX; - float x2 = column->MaxX; - //if (column->PrevEnabledColumn == -1) - // x1 -= table->OuterPaddingX; - //if (column->NextEnabledColumn == -1) - // x2 += table->OuterPaddingX; - x1 = ImMax(x1, table->WorkRect.Min.x); - x2 = ImMin(x2, table->WorkRect.Max.x); - return ImRect(x1, table->RowPosY1, x2, table->RowPosY2); -} - -// Return the resizing ID for the right-side of the given column. -ImGuiID ImGui::TableGetColumnResizeID(const ImGuiTable* table, int column_n, int instance_no) -{ - IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); - ImGuiID id = table->ID + 1 + (instance_no * table->ColumnsCount) + column_n; - return id; -} - -// Return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. -int ImGui::TableGetHoveredColumn() -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - if (!table) - return -1; - return (int)table->HoveredColumnBody; -} - -void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - IM_ASSERT(target != ImGuiTableBgTarget_None); - - if (color == IM_COL32_DISABLE) - color = 0; - - // We cannot draw neither the cell or row background immediately as we don't know the row height at this point in time. - switch (target) - { - case ImGuiTableBgTarget_CellBg: - { - if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard - return; - if (column_n == -1) - column_n = table->CurrentColumn; - if ((table->VisibleMaskByIndex & ((ImU64)1 << column_n)) == 0) - return; - if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n) - table->RowCellDataCurrent++; - ImGuiTableCellData* cell_data = &table->RowCellData[table->RowCellDataCurrent]; - cell_data->BgColor = color; - cell_data->Column = (ImGuiTableColumnIdx)column_n; - break; - } - case ImGuiTableBgTarget_RowBg0: - case ImGuiTableBgTarget_RowBg1: - { - if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard - return; - IM_ASSERT(column_n == -1); - int bg_idx = (target == ImGuiTableBgTarget_RowBg1) ? 1 : 0; - table->RowBgColor[bg_idx] = color; - break; - } - default: - IM_ASSERT(0); - } -} - -//------------------------------------------------------------------------- -// [SECTION] Tables: Row changes -//------------------------------------------------------------------------- -// - TableGetRowIndex() -// - TableNextRow() -// - TableBeginRow() [Internal] -// - TableEndRow() [Internal] -//------------------------------------------------------------------------- - -// [Public] Note: for row coloring we use ->RowBgColorCounter which is the same value without counting header rows -int ImGui::TableGetRowIndex() -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - if (!table) - return 0; - return table->CurrentRow; -} - -// [Public] Starts into the first cell of a new row -void ImGui::TableNextRow(ImGuiTableRowFlags row_flags, float row_min_height) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - - if (!table->IsLayoutLocked) - TableUpdateLayout(table); - if (table->IsInsideRow) - TableEndRow(table); - - table->LastRowFlags = table->RowFlags; - table->RowFlags = row_flags; - table->RowMinHeight = row_min_height; - TableBeginRow(table); - - // We honor min_row_height requested by user, but cannot guarantee per-row maximum height, - // because that would essentially require a unique clipping rectangle per-cell. - table->RowPosY2 += table->CellPaddingY * 2.0f; - table->RowPosY2 = ImMax(table->RowPosY2, table->RowPosY1 + row_min_height); - - // Disable output until user calls TableNextColumn() - table->InnerWindow->SkipItems = true; -} - -// [Internal] Called by TableNextRow() -void ImGui::TableBeginRow(ImGuiTable* table) -{ - ImGuiWindow* window = table->InnerWindow; - IM_ASSERT(!table->IsInsideRow); - - // New row - table->CurrentRow++; - table->CurrentColumn = -1; - table->RowBgColor[0] = table->RowBgColor[1] = IM_COL32_DISABLE; - table->RowCellDataCurrent = -1; - table->IsInsideRow = true; - - // Begin frozen rows - float next_y1 = table->RowPosY2; - if (table->CurrentRow == 0 && table->FreezeRowsCount > 0) - next_y1 = window->DC.CursorPos.y = table->OuterRect.Min.y; - - table->RowPosY1 = table->RowPosY2 = next_y1; - table->RowTextBaseline = 0.0f; - table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent - window->DC.PrevLineTextBaseOffset = 0.0f; - window->DC.CursorMaxPos.y = next_y1; - - // Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging. - if (table->RowFlags & ImGuiTableRowFlags_Headers) - { - TableSetBgColor(ImGuiTableBgTarget_RowBg0, GetColorU32(ImGuiCol_TableHeaderBg)); - if (table->CurrentRow == 0) - table->IsUsingHeaders = true; - } -} - -// [Internal] Called by TableNextRow() -void ImGui::TableEndRow(ImGuiTable* table) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window == table->InnerWindow); - IM_ASSERT(table->IsInsideRow); - - if (table->CurrentColumn != -1) - TableEndCell(table); - - // Logging - if (g.LogEnabled) - LogRenderedText(NULL, "|"); - - // Position cursor at the bottom of our row so it can be used for e.g. clipping calculation. However it is - // likely that the next call to TableBeginCell() will reposition the cursor to take account of vertical padding. - window->DC.CursorPos.y = table->RowPosY2; - - // Row background fill - const float bg_y1 = table->RowPosY1; - const float bg_y2 = table->RowPosY2; - const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount); - const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest); - if (table->CurrentRow == 0) - TableGetInstanceData(table, table->InstanceCurrent)->LastFirstRowHeight = bg_y2 - bg_y1; - - const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y); - if (is_visible) - { - // Decide of background color for the row - ImU32 bg_col0 = 0; - ImU32 bg_col1 = 0; - if (table->RowBgColor[0] != IM_COL32_DISABLE) - bg_col0 = table->RowBgColor[0]; - else if (table->Flags & ImGuiTableFlags_RowBg) - bg_col0 = GetColorU32((table->RowBgColorCounter & 1) ? ImGuiCol_TableRowBgAlt : ImGuiCol_TableRowBg); - if (table->RowBgColor[1] != IM_COL32_DISABLE) - bg_col1 = table->RowBgColor[1]; - - // Decide of top border color - ImU32 border_col = 0; - const float border_size = TABLE_BORDER_SIZE; - if (table->CurrentRow > 0 || table->InnerWindow == table->OuterWindow) - if (table->Flags & ImGuiTableFlags_BordersInnerH) - border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight; - - const bool draw_cell_bg_color = table->RowCellDataCurrent >= 0; - const bool draw_strong_bottom_border = unfreeze_rows_actual; - if ((bg_col0 | bg_col1 | border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color) - { - // In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is - // always followed by a change of clipping rectangle we perform the smallest overwrite possible here. - if ((table->Flags & ImGuiTableFlags_NoClip) == 0) - window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4(); - table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); - } - - // Draw row background - // We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle - if (bg_col0 || bg_col1) - { - ImRect row_rect(table->WorkRect.Min.x, bg_y1, table->WorkRect.Max.x, bg_y2); - row_rect.ClipWith(table->BgClipRect); - if (bg_col0 != 0 && row_rect.Min.y < row_rect.Max.y) - window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col0); - if (bg_col1 != 0 && row_rect.Min.y < row_rect.Max.y) - window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col1); - } - - // Draw cell background color - if (draw_cell_bg_color) - { - ImGuiTableCellData* cell_data_end = &table->RowCellData[table->RowCellDataCurrent]; - for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++) - { - // As we render the BG here we need to clip things (for layout we would not) - // FIXME: This cancels the OuterPadding addition done by TableGetCellBgRect(), need to keep it while rendering correctly while scrolling. - const ImGuiTableColumn* column = &table->Columns[cell_data->Column]; - ImRect cell_bg_rect = TableGetCellBgRect(table, cell_data->Column); - cell_bg_rect.ClipWith(table->BgClipRect); - cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling - cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX); - window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor); - } - } - - // Draw top border - if (border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y) - window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), border_col, border_size); - - // Draw bottom border at the row unfreezing mark (always strong) - if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y) - window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size); - } - - // End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle) - // We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and - // get the new cursor position. - if (unfreeze_rows_request) - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - column->NavLayerCurrent = (ImS8)((column_n < table->FreezeColumnsCount) ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); - } - if (unfreeze_rows_actual) - { - IM_ASSERT(table->IsUnfrozenRows == false); - table->IsUnfrozenRows = true; - - // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect - float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); - table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); - table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; - table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; - IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y); - - float row_height = table->RowPosY2 - table->RowPosY1; - table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y; - table->RowPosY1 = table->RowPosY2 - row_height; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - column->DrawChannelCurrent = column->DrawChannelUnfrozen; - column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y; - } - - // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y - SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect); - table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); - } - - if (!(table->RowFlags & ImGuiTableRowFlags_Headers)) - table->RowBgColorCounter++; - table->IsInsideRow = false; -} - -//------------------------------------------------------------------------- -// [SECTION] Tables: Columns changes -//------------------------------------------------------------------------- -// - TableGetColumnIndex() -// - TableSetColumnIndex() -// - TableNextColumn() -// - TableBeginCell() [Internal] -// - TableEndCell() [Internal] -//------------------------------------------------------------------------- - -int ImGui::TableGetColumnIndex() -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - if (!table) - return 0; - return table->CurrentColumn; -} - -// [Public] Append into a specific column -bool ImGui::TableSetColumnIndex(int column_n) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - if (!table) - return false; - - if (table->CurrentColumn != column_n) - { - if (table->CurrentColumn != -1) - TableEndCell(table); - IM_ASSERT(column_n >= 0 && table->ColumnsCount); - TableBeginCell(table, column_n); - } - - // Return whether the column is visible. User may choose to skip submitting items based on this return value, - // however they shouldn't skip submitting for columns that may have the tallest contribution to row height. - return (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n)) != 0; -} - -// [Public] Append into the next column, wrap and create a new row when already on last column -bool ImGui::TableNextColumn() -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - if (!table) - return false; - - if (table->IsInsideRow && table->CurrentColumn + 1 < table->ColumnsCount) - { - if (table->CurrentColumn != -1) - TableEndCell(table); - TableBeginCell(table, table->CurrentColumn + 1); - } - else - { - TableNextRow(); - TableBeginCell(table, 0); - } - - // Return whether the column is visible. User may choose to skip submitting items based on this return value, - // however they shouldn't skip submitting for columns that may have the tallest contribution to row height. - int column_n = table->CurrentColumn; - return (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n)) != 0; -} - - -// [Internal] Called by TableSetColumnIndex()/TableNextColumn() -// This is called very frequently, so we need to be mindful of unnecessary overhead. -// FIXME-TABLE FIXME-OPT: Could probably shortcut some things for non-active or clipped columns. -void ImGui::TableBeginCell(ImGuiTable* table, int column_n) -{ - ImGuiTableColumn* column = &table->Columns[column_n]; - ImGuiWindow* window = table->InnerWindow; - table->CurrentColumn = column_n; - - // Start position is roughly ~~ CellRect.Min + CellPadding + Indent - float start_x = column->WorkMinX; - if (column->Flags & ImGuiTableColumnFlags_IndentEnable) - start_x += table->RowIndentOffsetX; // ~~ += window.DC.Indent.x - table->HostIndentX, except we locked it for the row. - - window->DC.CursorPos.x = start_x; - window->DC.CursorPos.y = table->RowPosY1 + table->CellPaddingY; - window->DC.CursorMaxPos.x = window->DC.CursorPos.x; - window->DC.ColumnsOffset.x = start_x - window->Pos.x - window->DC.Indent.x; // FIXME-WORKRECT - window->DC.CurrLineTextBaseOffset = table->RowTextBaseline; - window->DC.NavLayerCurrent = (ImGuiNavLayer)column->NavLayerCurrent; - - window->WorkRect.Min.y = window->DC.CursorPos.y; - window->WorkRect.Min.x = column->WorkMinX; - window->WorkRect.Max.x = column->WorkMaxX; - window->DC.ItemWidth = column->ItemWidth; - - // To allow ImGuiListClipper to function we propagate our row height - if (!column->IsEnabled) - window->DC.CursorPos.y = ImMax(window->DC.CursorPos.y, table->RowPosY2); - - window->SkipItems = column->IsSkipItems; - if (column->IsSkipItems) - { - ImGuiContext& g = *GImGui; - g.LastItemData.ID = 0; - g.LastItemData.StatusFlags = 0; - } - - if (table->Flags & ImGuiTableFlags_NoClip) - { - // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed. - table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); - //IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP); - } - else - { - // FIXME-TABLE: Could avoid this if draw channel is dummy channel? - SetWindowClipRectBeforeSetChannel(window, column->ClipRect); - table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); - } - - // Logging - ImGuiContext& g = *GImGui; - if (g.LogEnabled && !column->IsSkipItems) - { - LogRenderedText(&window->DC.CursorPos, "|"); - g.LogLinePosY = FLT_MAX; - } -} - -// [Internal] Called by TableNextRow()/TableSetColumnIndex()/TableNextColumn() -void ImGui::TableEndCell(ImGuiTable* table) -{ - ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; - ImGuiWindow* window = table->InnerWindow; - - // Report maximum position so we can infer content size per column. - float* p_max_pos_x; - if (table->RowFlags & ImGuiTableRowFlags_Headers) - p_max_pos_x = &column->ContentMaxXHeadersUsed; // Useful in case user submit contents in header row that is not a TableHeader() call - else - p_max_pos_x = table->IsUnfrozenRows ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen; - *p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x); - table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->CellPaddingY); - column->ItemWidth = window->DC.ItemWidth; - - // Propagate text baseline for the entire row - // FIXME-TABLE: Here we propagate text baseline from the last line of the cell.. instead of the first one. - table->RowTextBaseline = ImMax(table->RowTextBaseline, window->DC.PrevLineTextBaseOffset); -} - -//------------------------------------------------------------------------- -// [SECTION] Tables: Columns width management -//------------------------------------------------------------------------- -// - TableGetMaxColumnWidth() [Internal] -// - TableGetColumnWidthAuto() [Internal] -// - TableSetColumnWidth() -// - TableSetColumnWidthAutoSingle() [Internal] -// - TableSetColumnWidthAutoAll() [Internal] -// - TableUpdateColumnsWeightFromWidth() [Internal] -//------------------------------------------------------------------------- - -// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis. -float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n) -{ - const ImGuiTableColumn* column = &table->Columns[column_n]; - float max_width = FLT_MAX; - const float min_column_distance = table->MinColumnWidth + table->CellPaddingX * 2.0f + table->CellSpacingX1 + table->CellSpacingX2; - if (table->Flags & ImGuiTableFlags_ScrollX) - { - // Frozen columns can't reach beyond visible width else scrolling will naturally break. - // (we use DisplayOrder as within a set of multiple frozen column reordering is possible) - if (column->DisplayOrder < table->FreezeColumnsRequest) - { - max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX; - max_width = max_width - table->OuterPaddingX - table->CellPaddingX - table->CellSpacingX2; - } - } - else if ((table->Flags & ImGuiTableFlags_NoKeepColumnsVisible) == 0) - { - // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make - // sure they are all visible. Because of this we also know that all of the columns will always fit in - // table->WorkRect and therefore in table->InnerRect (because ScrollX is off) - // FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match. - // See "table_width_distrib" and "table_width_keep_visible" tests - max_width = table->WorkRect.Max.x - (table->ColumnsEnabledCount - column->IndexWithinEnabledSet - 1) * min_column_distance - column->MinX; - //max_width -= table->CellSpacingX1; - max_width -= table->CellSpacingX2; - max_width -= table->CellPaddingX * 2.0f; - max_width -= table->OuterPaddingX; - } - return max_width; -} - -// Note this is meant to be stored in column->WidthAuto, please generally use the WidthAuto field -float ImGui::TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column) -{ - const float content_width_body = ImMax(column->ContentMaxXFrozen, column->ContentMaxXUnfrozen) - column->WorkMinX; - const float content_width_headers = column->ContentMaxXHeadersIdeal - column->WorkMinX; - float width_auto = content_width_body; - if (!(column->Flags & ImGuiTableColumnFlags_NoHeaderWidth)) - width_auto = ImMax(width_auto, content_width_headers); - - // Non-resizable fixed columns preserve their requested width - if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f) - if (!(table->Flags & ImGuiTableFlags_Resizable) || (column->Flags & ImGuiTableColumnFlags_NoResize)) - width_auto = column->InitStretchWeightOrWidth; - - return ImMax(width_auto, table->MinColumnWidth); -} - -// 'width' = inner column width, without padding -void ImGui::TableSetColumnWidth(int column_n, float width) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - IM_ASSERT(table != NULL && table->IsLayoutLocked == false); - IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); - ImGuiTableColumn* column_0 = &table->Columns[column_n]; - float column_0_width = width; - - // Apply constraints early - // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded) - IM_ASSERT(table->MinColumnWidth > 0.0f); - const float min_width = table->MinColumnWidth; - const float max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n)); - column_0_width = ImClamp(column_0_width, min_width, max_width); - if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width) - return; - - //IMGUI_DEBUG_PRINT("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthGiven, column_0_width); - ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL; - - // In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns. - // - All fixed: easy. - // - All stretch: easy. - // - One or more fixed + one stretch: easy. - // - One or more fixed + more than one stretch: tricky. - // Qt when manual resize is enabled only support a single _trailing_ stretch column. - - // When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1. - // FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user. - // Scenarios: - // - F1 F2 F3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. Subsequent columns will be offset. - // - F1 F2 F3 resize from F3| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered. - // - F1 F2 W3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size. - // - F1 F2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1) - // - W1 W2 W3 resize from W1| or W2| --> ok - // - W1 W2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1) - // - W1 F2 F3 resize from F3| --> ok: no-op (disabled by Resize Rule 1) - // - W1 F2 resize from F2| --> ok: no-op (disabled by Resize Rule 1) - // - W1 W2 F3 resize from W1| or W2| --> ok - // - W1 F2 W3 resize from W1| or F2| --> ok - // - F1 W2 F3 resize from W2| --> ok - // - F1 W3 F2 resize from W3| --> ok - // - W1 F2 F3 resize from W1| --> ok: equivalent to resizing |F2. F3 will not move. - // - W1 F2 F3 resize from F2| --> ok - // All resizes from a Wx columns are locking other columns. - - // Possible improvements: - // - W1 W2 W3 resize W1| --> to not be stuck, both W2 and W3 would stretch down. Seems possible to fix. Would be most beneficial to simplify resize of all-weighted columns. - // - W3 F1 F2 resize W3| --> to not be stuck past F1|, both F1 and F2 would need to stretch down, which would be lossy or ambiguous. Seems hard to fix. - - // [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout(). - - // If we have all Fixed columns OR resizing a Fixed column that doesn't come after a Stretch one, we can do an offsetting resize. - // This is the preferred resize path - if (column_0->Flags & ImGuiTableColumnFlags_WidthFixed) - if (!column_1 || table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder >= column_0->DisplayOrder) - { - column_0->WidthRequest = column_0_width; - table->IsSettingsDirty = true; - return; - } - - // We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column) - if (column_1 == NULL) - column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL; - if (column_1 == NULL) - return; - - // Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column. - // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b) - float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width); - column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width; - IM_ASSERT(column_0_width > 0.0f && column_1_width > 0.0f); - column_0->WidthRequest = column_0_width; - column_1->WidthRequest = column_1_width; - if ((column_0->Flags | column_1->Flags) & ImGuiTableColumnFlags_WidthStretch) - TableUpdateColumnsWeightFromWidth(table); - table->IsSettingsDirty = true; -} - -// Disable clipping then auto-fit, will take 2 frames -// (we don't take a shortcut for unclipped columns to reduce inconsistencies when e.g. resizing multiple columns) -void ImGui::TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n) -{ - // Single auto width uses auto-fit - ImGuiTableColumn* column = &table->Columns[column_n]; - if (!column->IsEnabled) - return; - column->CannotSkipItemsQueue = (1 << 0); - table->AutoFitSingleColumn = (ImGuiTableColumnIdx)column_n; -} - -void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table) -{ - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - if (!column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) // Cannot reset weight of hidden stretch column - continue; - column->CannotSkipItemsQueue = (1 << 0); - column->AutoFitQueue = (1 << 1); - } -} - -void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) -{ - IM_ASSERT(table->LeftMostStretchedColumn != -1 && table->RightMostStretchedColumn != -1); - - // Measure existing quantity - float visible_weight = 0.0f; - float visible_width = 0.0f; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) - continue; - IM_ASSERT(column->StretchWeight > 0.0f); - visible_weight += column->StretchWeight; - visible_width += column->WidthRequest; - } - IM_ASSERT(visible_weight > 0.0f && visible_width > 0.0f); - - // Apply new weights - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) - continue; - column->StretchWeight = (column->WidthRequest / visible_width) * visible_weight; - IM_ASSERT(column->StretchWeight > 0.0f); - } -} - -//------------------------------------------------------------------------- -// [SECTION] Tables: Drawing -//------------------------------------------------------------------------- -// - TablePushBackgroundChannel() [Internal] -// - TablePopBackgroundChannel() [Internal] -// - TableSetupDrawChannels() [Internal] -// - TableMergeDrawChannels() [Internal] -// - TableDrawBorders() [Internal] -//------------------------------------------------------------------------- - -// Bg2 is used by Selectable (and possibly other widgets) to render to the background. -// Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect. -void ImGui::TablePushBackgroundChannel() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiTable* table = g.CurrentTable; - - // Optimization: avoid SetCurrentChannel() + PushClipRect() - table->HostBackupInnerClipRect = window->ClipRect; - SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd); - table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); -} - -void ImGui::TablePopBackgroundChannel() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiTable* table = g.CurrentTable; - ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; - - // Optimization: avoid PopClipRect() + SetCurrentChannel() - SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect); - table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); -} - -// Allocate draw channels. Called by TableUpdateLayout() -// - We allocate them following storage order instead of display order so reordering columns won't needlessly -// increase overall dormant memory cost. -// - We isolate headers draw commands in their own channels instead of just altering clip rects. -// This is in order to facilitate merging of draw commands. -// - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels. -// - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other -// channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged. -// - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for -// horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4). -// Draw channel allocation (before merging): -// - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call) -// - Clip --> 2+D+N channels -// - FreezeRows --> 2+D+N*2 (unless scrolling value is zero) -// - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero) -// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0. -void ImGui::TableSetupDrawChannels(ImGuiTable* table) -{ - const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1; - const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount; - const int channels_for_bg = 1 + 1 * freeze_row_multiplier; - const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->VisibleMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0; - const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy; - table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total); - table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1); - table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN; - table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN); - - int draw_channel_current = 2; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - if (column->IsVisibleX && column->IsVisibleY) - { - column->DrawChannelFrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current); - column->DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current + (table->FreezeRowsCount > 0 ? channels_for_row + 1 : 0)); - if (!(table->Flags & ImGuiTableFlags_NoClip)) - draw_channel_current++; - } - else - { - column->DrawChannelFrozen = column->DrawChannelUnfrozen = table->DummyDrawChannel; - } - column->DrawChannelCurrent = column->DrawChannelFrozen; - } - - // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default. - // All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect. - // (This technically isn't part of setting up draw channels, but is reasonably related to be done here) - table->BgClipRect = table->InnerClipRect; - table->Bg0ClipRectForDrawCmd = table->OuterWindow->ClipRect; - table->Bg2ClipRectForDrawCmd = table->HostClipRect; - IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); -} - -// This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable(). -// For simplicity we call it TableMergeDrawChannels() but in fact it only reorder channels + overwrite ClipRect, -// actual merging is done by table->DrawSplitter.Merge() which is called right after TableMergeDrawChannels(). -// -// Columns where the contents didn't stray off their local clip rectangle can be merged. To achieve -// this we merge their clip rect and make them contiguous in the channel list, so they can be merged -// by the call to DrawSplitter.Merge() following to the call to this function. -// We reorder draw commands by arranging them into a maximum of 4 distinct groups: -// -// 1 group: 2 groups: 2 groups: 4 groups: -// [ 0. ] no freeze [ 0. ] row freeze [ 01 ] col freeze [ 01 ] row+col freeze -// [ .. ] or no scroll [ 2. ] and v-scroll [ .. ] and h-scroll [ 23 ] and v+h-scroll -// -// Each column itself can use 1 channel (row freeze disabled) or 2 channels (row freeze enabled). -// When the contents of a column didn't stray off its limit, we move its channels into the corresponding group -// based on its position (within frozen rows/columns groups or not). -// At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect. -// This function assume that each column are pointing to a distinct draw channel, -// otherwise merge_group->ChannelsCount will not match set bit count of merge_group->ChannelsMask. -// -// Column channels will not be merged into one of the 1-4 groups in the following cases: -// - The contents stray off its clipping rectangle (we only compare the MaxX value, not the MinX value). -// Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui:: bounds -// matches, by e.g. calling SetCursorScreenPos(). -// - The channel uses more than one draw command itself. We drop all our attempt at merging stuff here.. -// we could do better but it's going to be rare and probably not worth the hassle. -// Columns for which the draw channel(s) haven't been merged with other will use their own ImDrawCmd. -// -// This function is particularly tricky to understand.. take a breath. -void ImGui::TableMergeDrawChannels(ImGuiTable* table) -{ - ImGuiContext& g = *GImGui; - ImDrawListSplitter* splitter = table->DrawSplitter; - const bool has_freeze_v = (table->FreezeRowsCount > 0); - const bool has_freeze_h = (table->FreezeColumnsCount > 0); - IM_ASSERT(splitter->_Current == 0); - - // Track which groups we are going to attempt to merge, and which channels goes into each group. - struct MergeGroup - { - ImRect ClipRect; - int ChannelsCount; - ImBitArray ChannelsMask; - - MergeGroup() { ChannelsCount = 0; } - }; - int merge_group_mask = 0x00; - MergeGroup merge_groups[4]; - - // 1. Scan channels and take note of those which can be merged - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - if ((table->VisibleMaskByIndex & ((ImU64)1 << column_n)) == 0) - continue; - ImGuiTableColumn* column = &table->Columns[column_n]; - - const int merge_group_sub_count = has_freeze_v ? 2 : 1; - for (int merge_group_sub_n = 0; merge_group_sub_n < merge_group_sub_count; merge_group_sub_n++) - { - const int channel_no = (merge_group_sub_n == 0) ? column->DrawChannelFrozen : column->DrawChannelUnfrozen; - - // Don't attempt to merge if there are multiple draw calls within the column - ImDrawChannel* src_channel = &splitter->_Channels[channel_no]; - if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd() - src_channel->_CmdBuffer.pop_back(); - if (src_channel->_CmdBuffer.Size != 1) - continue; - - // Find out the width of this merge group and check if it will fit in our column - // (note that we assume that rendering didn't stray on the left direction. we should need a CursorMinPos to detect it) - if (!(column->Flags & ImGuiTableColumnFlags_NoClip)) - { - float content_max_x; - if (!has_freeze_v) - content_max_x = ImMax(column->ContentMaxXUnfrozen, column->ContentMaxXHeadersUsed); // No row freeze - else if (merge_group_sub_n == 0) - content_max_x = ImMax(column->ContentMaxXFrozen, column->ContentMaxXHeadersUsed); // Row freeze: use width before freeze - else - content_max_x = column->ContentMaxXUnfrozen; // Row freeze: use width after freeze - if (content_max_x > column->ClipRect.Max.x) - continue; - } - - const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2); - IM_ASSERT(channel_no < IMGUI_TABLE_MAX_DRAW_CHANNELS); - MergeGroup* merge_group = &merge_groups[merge_group_n]; - if (merge_group->ChannelsCount == 0) - merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); - merge_group->ChannelsMask.SetBit(channel_no); - merge_group->ChannelsCount++; - merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect); - merge_group_mask |= (1 << merge_group_n); - } - - // Invalidate current draw channel - // (we don't clear DrawChannelFrozen/DrawChannelUnfrozen solely to facilitate debugging/later inspection of data) - column->DrawChannelCurrent = (ImGuiTableDrawChannelIdx)-1; - } - - // [DEBUG] Display merge groups -#if 0 - if (g.IO.KeyShift) - for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++) - { - MergeGroup* merge_group = &merge_groups[merge_group_n]; - if (merge_group->ChannelsCount == 0) - continue; - char buf[32]; - ImFormatString(buf, 32, "MG%d:%d", merge_group_n, merge_group->ChannelsCount); - ImVec2 text_pos = merge_group->ClipRect.Min + ImVec2(4, 4); - ImVec2 text_size = CalcTextSize(buf, NULL); - GetForegroundDrawList()->AddRectFilled(text_pos, text_pos + text_size, IM_COL32(0, 0, 0, 255)); - GetForegroundDrawList()->AddText(text_pos, IM_COL32(255, 255, 0, 255), buf, NULL); - GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 255, 0, 255)); - } -#endif - - // 2. Rewrite channel list in our preferred order - if (merge_group_mask != 0) - { - // We skip channel 0 (Bg0/Bg1) and 1 (Bg2 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels(). - const int LEADING_DRAW_CHANNELS = 2; - g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized - ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data; - ImBitArray remaining_mask; // We need 132-bit of storage - remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count); - remaining_mask.ClearBit(table->Bg2DrawChannelUnfrozen); - IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN); - int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS); - //ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect; - ImRect host_rect = table->HostClipRect; - for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++) - { - if (int merge_channels_count = merge_groups[merge_group_n].ChannelsCount) - { - MergeGroup* merge_group = &merge_groups[merge_group_n]; - ImRect merge_clip_rect = merge_group->ClipRect; - - // Extend outer-most clip limits to match those of host, so draw calls can be merged even if - // outer-most columns have some outer padding offsetting them from their parent ClipRect. - // The principal cases this is dealing with are: - // - On a same-window table (not scrolling = single group), all fitting columns ClipRect -> will extend and match host ClipRect -> will merge - // - Columns can use padding and have left-most ClipRect.Min.x and right-most ClipRect.Max.x != from host ClipRect -> will extend and match host ClipRect -> will merge - // FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column doesn't fit - // within host clip rect, solely because of the half-padding difference between window->WorkRect and window->InnerClipRect. - if ((merge_group_n & 1) == 0 || !has_freeze_h) - merge_clip_rect.Min.x = ImMin(merge_clip_rect.Min.x, host_rect.Min.x); - if ((merge_group_n & 2) == 0 || !has_freeze_v) - merge_clip_rect.Min.y = ImMin(merge_clip_rect.Min.y, host_rect.Min.y); - if ((merge_group_n & 1) != 0) - merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, host_rect.Max.x); - if ((merge_group_n & 2) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0) - merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y); -#if 0 - GetOverlayDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, 0, 1.0f); - GetOverlayDrawList()->AddLine(merge_group->ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200)); - GetOverlayDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200)); -#endif - remaining_count -= merge_group->ChannelsCount; - for (int n = 0; n < IM_ARRAYSIZE(remaining_mask.Storage); n++) - remaining_mask.Storage[n] &= ~merge_group->ChannelsMask.Storage[n]; - for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++) - { - // Copy + overwrite new clip rect - if (!merge_group->ChannelsMask.TestBit(n)) - continue; - merge_group->ChannelsMask.ClearBit(n); - merge_channels_count--; - - ImDrawChannel* channel = &splitter->_Channels[n]; - IM_ASSERT(channel->_CmdBuffer.Size == 1 && merge_clip_rect.Contains(ImRect(channel->_CmdBuffer[0].ClipRect))); - channel->_CmdBuffer[0].ClipRect = merge_clip_rect.ToVec4(); - memcpy(dst_tmp++, channel, sizeof(ImDrawChannel)); - } - } - - // Make sure Bg2DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0/Bg1 and Bg2 frozen are fixed to 0 and 1) - if (merge_group_n == 1 && has_freeze_v) - memcpy(dst_tmp++, &splitter->_Channels[table->Bg2DrawChannelUnfrozen], sizeof(ImDrawChannel)); - } - - // Append unmergeable channels that we didn't reorder at the end of the list - for (int n = 0; n < splitter->_Count && remaining_count != 0; n++) - { - if (!remaining_mask.TestBit(n)) - continue; - ImDrawChannel* channel = &splitter->_Channels[n]; - memcpy(dst_tmp++, channel, sizeof(ImDrawChannel)); - remaining_count--; - } - IM_ASSERT(dst_tmp == g.DrawChannelsTempMergeBuffer.Data + g.DrawChannelsTempMergeBuffer.Size); - memcpy(splitter->_Channels.Data + LEADING_DRAW_CHANNELS, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - LEADING_DRAW_CHANNELS) * sizeof(ImDrawChannel)); - } -} - -// FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow) -void ImGui::TableDrawBorders(ImGuiTable* table) -{ - ImGuiWindow* inner_window = table->InnerWindow; - if (!table->OuterWindow->ClipRect.Overlaps(table->OuterRect)) - return; - - ImDrawList* inner_drawlist = inner_window->DrawList; - table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); - inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false); - - // Draw inner border and resizing feedback - ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); - const float border_size = TABLE_BORDER_SIZE; - const float draw_y1 = table->InnerRect.Min.y; - const float draw_y2_body = table->InnerRect.Max.y; - const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastFirstRowHeight) : draw_y1; - if (table->Flags & ImGuiTableFlags_BordersInnerV) - { - for (int order_n = 0; order_n < table->ColumnsCount; order_n++) - { - if (!(table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n))) - continue; - - const int column_n = table->DisplayOrderToIndex[order_n]; - ImGuiTableColumn* column = &table->Columns[column_n]; - const bool is_hovered = (table->HoveredColumnBorder == column_n); - const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); - const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0; - const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); - if (column->MaxX > table->InnerClipRect.Max.x && !is_resized) - continue; - - // Decide whether right-most column is visible - if (column->NextEnabledColumn == -1 && !is_resizable) - if ((table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame || (table->Flags & ImGuiTableFlags_NoHostExtendX)) - continue; - if (column->MaxX <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size.. - continue; - - // Draw in outer window so right-most column won't be clipped - // Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling. - ImU32 col; - float draw_y2; - if (is_hovered || is_resized || is_frozen_separator) - { - draw_y2 = draw_y2_body; - col = is_resized ? GetColorU32(ImGuiCol_SeparatorActive) : is_hovered ? GetColorU32(ImGuiCol_SeparatorHovered) : table->BorderColorStrong; - } - else - { - draw_y2 = (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) ? draw_y2_head : draw_y2_body; - col = (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) ? table->BorderColorStrong : table->BorderColorLight; - } - - if (draw_y2 > draw_y1) - inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), col, border_size); - } - } - - // Draw outer border - // FIXME: could use AddRect or explicit VLine/HLine helper? - if (table->Flags & ImGuiTableFlags_BordersOuter) - { - // Display outer border offset by 1 which is a simple way to display it without adding an extra draw call - // (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their - // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part - // of it in inner window, and the part that's over scrollbars in the outer window..) - // Either solution currently won't allow us to use a larger border size: the border would clipped. - const ImRect outer_border = table->OuterRect; - const ImU32 outer_col = table->BorderColorStrong; - if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) - { - inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size); - } - else if (table->Flags & ImGuiTableFlags_BordersOuterV) - { - inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size); - inner_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size); - } - else if (table->Flags & ImGuiTableFlags_BordersOuterH) - { - inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size); - inner_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size); - } - } - if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) - { - // Draw bottom-most row border - const float border_y = table->RowPosY2; - if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y) - inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size); - } - - inner_drawlist->PopClipRect(); -} - -//------------------------------------------------------------------------- -// [SECTION] Tables: Sorting -//------------------------------------------------------------------------- -// - TableGetSortSpecs() -// - TableFixColumnSortDirection() [Internal] -// - TableGetColumnNextSortDirection() [Internal] -// - TableSetColumnSortDirection() [Internal] -// - TableSortSpecsSanitize() [Internal] -// - TableSortSpecsBuild() [Internal] -//------------------------------------------------------------------------- - -// Return NULL if no sort specs (most often when ImGuiTableFlags_Sortable is not set) -// You can sort your data again when 'SpecsChanged == true'. It will be true with sorting specs have changed since -// last call, or the first time. -// Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable()! -ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - IM_ASSERT(table != NULL); - - if (!(table->Flags & ImGuiTableFlags_Sortable)) - return NULL; - - // Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths. - if (!table->IsLayoutLocked) - TableUpdateLayout(table); - - TableSortSpecsBuild(table); - - return &table->SortSpecs; -} - -static inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n) -{ - IM_ASSERT(n < column->SortDirectionsAvailCount); - return (column->SortDirectionsAvailList >> (n << 1)) & 0x03; -} - -// Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending) -void ImGui::TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column) -{ - if (column->SortOrder == -1 || (column->SortDirectionsAvailMask & (1 << column->SortDirection)) != 0) - return; - column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0); - table->IsSortSpecsDirty = true; -} - -// Calculate next sort direction that would be set after clicking the column -// - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click. -// - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op. -IM_STATIC_ASSERT(ImGuiSortDirection_None == 0 && ImGuiSortDirection_Ascending == 1 && ImGuiSortDirection_Descending == 2); -ImGuiSortDirection ImGui::TableGetColumnNextSortDirection(ImGuiTableColumn* column) -{ - IM_ASSERT(column->SortDirectionsAvailCount > 0); - if (column->SortOrder == -1) - return TableGetColumnAvailSortDirection(column, 0); - for (int n = 0; n < 3; n++) - if (column->SortDirection == TableGetColumnAvailSortDirection(column, n)) - return TableGetColumnAvailSortDirection(column, (n + 1) % column->SortDirectionsAvailCount); - IM_ASSERT(0); - return ImGuiSortDirection_None; -} - -// Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert -// the value of SortDirection. We could technically also do it here but it would be unnecessary and duplicate code. -void ImGui::TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - - if (!(table->Flags & ImGuiTableFlags_SortMulti)) - append_to_sort_specs = false; - if (!(table->Flags & ImGuiTableFlags_SortTristate)) - IM_ASSERT(sort_direction != ImGuiSortDirection_None); - - ImGuiTableColumnIdx sort_order_max = 0; - if (append_to_sort_specs) - for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) - sort_order_max = ImMax(sort_order_max, table->Columns[other_column_n].SortOrder); - - ImGuiTableColumn* column = &table->Columns[column_n]; - column->SortDirection = (ImU8)sort_direction; - if (column->SortDirection == ImGuiSortDirection_None) - column->SortOrder = -1; - else if (column->SortOrder == -1 || !append_to_sort_specs) - column->SortOrder = append_to_sort_specs ? sort_order_max + 1 : 0; - - for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) - { - ImGuiTableColumn* other_column = &table->Columns[other_column_n]; - if (other_column != column && !append_to_sort_specs) - other_column->SortOrder = -1; - TableFixColumnSortDirection(table, other_column); - } - table->IsSettingsDirty = true; - table->IsSortSpecsDirty = true; -} - -void ImGui::TableSortSpecsSanitize(ImGuiTable* table) -{ - IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable); - - // Clear SortOrder from hidden column and verify that there's no gap or duplicate. - int sort_order_count = 0; - ImU64 sort_order_mask = 0x00; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - if (column->SortOrder != -1 && !column->IsEnabled) - column->SortOrder = -1; - if (column->SortOrder == -1) - continue; - sort_order_count++; - sort_order_mask |= ((ImU64)1 << column->SortOrder); - IM_ASSERT(sort_order_count < (int)sizeof(sort_order_mask) * 8); - } - - const bool need_fix_linearize = ((ImU64)1 << sort_order_count) != (sort_order_mask + 1); - const bool need_fix_single_sort_order = (sort_order_count > 1) && !(table->Flags & ImGuiTableFlags_SortMulti); - if (need_fix_linearize || need_fix_single_sort_order) - { - ImU64 fixed_mask = 0x00; - for (int sort_n = 0; sort_n < sort_order_count; sort_n++) - { - // Fix: Rewrite sort order fields if needed so they have no gap or duplicate. - // (e.g. SortOrder 0 disappeared, SortOrder 1..2 exists --> rewrite then as SortOrder 0..1) - int column_with_smallest_sort_order = -1; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - if ((fixed_mask & ((ImU64)1 << (ImU64)column_n)) == 0 && table->Columns[column_n].SortOrder != -1) - if (column_with_smallest_sort_order == -1 || table->Columns[column_n].SortOrder < table->Columns[column_with_smallest_sort_order].SortOrder) - column_with_smallest_sort_order = column_n; - IM_ASSERT(column_with_smallest_sort_order != -1); - fixed_mask |= ((ImU64)1 << column_with_smallest_sort_order); - table->Columns[column_with_smallest_sort_order].SortOrder = (ImGuiTableColumnIdx)sort_n; - - // Fix: Make sure only one column has a SortOrder if ImGuiTableFlags_MultiSortable is not set. - if (need_fix_single_sort_order) - { - sort_order_count = 1; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - if (column_n != column_with_smallest_sort_order) - table->Columns[column_n].SortOrder = -1; - break; - } - } - } - - // Fallback default sort order (if no column had the ImGuiTableColumnFlags_DefaultSort flag) - if (sort_order_count == 0 && !(table->Flags & ImGuiTableFlags_SortTristate)) - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - if (column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_NoSort)) - { - sort_order_count = 1; - column->SortOrder = 0; - column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0); - break; - } - } - - table->SortSpecsCount = (ImGuiTableColumnIdx)sort_order_count; -} - -void ImGui::TableSortSpecsBuild(ImGuiTable* table) -{ - bool dirty = table->IsSortSpecsDirty; - if (dirty) - { - TableSortSpecsSanitize(table); - table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); - table->SortSpecs.SpecsDirty = true; // Mark as dirty for user - table->IsSortSpecsDirty = false; // Mark as not dirty for us - } - - // Write output - ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; - if (dirty && sort_specs != NULL) - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - if (column->SortOrder == -1) - continue; - IM_ASSERT(column->SortOrder < table->SortSpecsCount); - ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder]; - sort_spec->ColumnUserID = column->UserID; - sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; - sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; - sort_spec->SortDirection = column->SortDirection; - } - - table->SortSpecs.Specs = sort_specs; - table->SortSpecs.SpecsCount = table->SortSpecsCount; -} - -//------------------------------------------------------------------------- -// [SECTION] Tables: Headers -//------------------------------------------------------------------------- -// - TableGetHeaderRowHeight() [Internal] -// - TableHeadersRow() -// - TableHeader() -//------------------------------------------------------------------------- - -float ImGui::TableGetHeaderRowHeight() -{ - // Caring for a minor edge case: - // Calculate row height, for the unlikely case that some labels may be taller than others. - // If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height. - // In your custom header row you may omit this all together and just call TableNextRow() without a height... - float row_height = GetTextLineHeight(); - int columns_count = TableGetColumnCount(); - for (int column_n = 0; column_n < columns_count; column_n++) - { - ImGuiTableColumnFlags flags = TableGetColumnFlags(column_n); - if ((flags & ImGuiTableColumnFlags_IsEnabled) && !(flags & ImGuiTableColumnFlags_NoHeaderLabel)) - row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y); - } - row_height += GetStyle().CellPadding.y * 2.0f; - return row_height; -} - -// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn(). -// The intent is that advanced users willing to create customized headers would not need to use this helper -// and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets. -// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this. -// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy. -// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public. -void ImGui::TableHeadersRow() -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); - - // Layout if not already done (this is automatically done by TableNextRow, we do it here solely to facilitate stepping in debugger as it is frequent to step in TableUpdateLayout) - if (!table->IsLayoutLocked) - TableUpdateLayout(table); - - // Open row - const float row_y1 = GetCursorScreenPos().y; - const float row_height = TableGetHeaderRowHeight(); - TableNextRow(ImGuiTableRowFlags_Headers, row_height); - if (table->HostSkipItems) // Merely an optimization, you may skip in your own code. - return; - - const int columns_count = TableGetColumnCount(); - for (int column_n = 0; column_n < columns_count; column_n++) - { - if (!TableSetColumnIndex(column_n)) - continue; - - // Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them) - // - in your own code you may omit the PushID/PopID all-together, provided you know they won't collide - // - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier. - const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n); - PushID(table->InstanceCurrent * table->ColumnsCount + column_n); - TableHeader(name); - PopID(); - } - - // Allow opening popup from the right-most section after the last column. - ImVec2 mouse_pos = ImGui::GetMousePos(); - if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count) - if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height) - TableOpenContextMenu(-1); // Will open a non-column-specific popup. -} - -// Emit a column header (text + optional sort order) -// We cpu-clip text here so that all columns headers can be merged into a same draw call. -// Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader() -void ImGui::TableHeader(const char* label) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return; - - ImGuiTable* table = g.CurrentTable; - IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!"); - IM_ASSERT(table->CurrentColumn != -1); - const int column_n = table->CurrentColumn; - ImGuiTableColumn* column = &table->Columns[column_n]; - - // Label - if (label == NULL) - label = ""; - const char* label_end = FindRenderedTextEnd(label); - ImVec2 label_size = CalcTextSize(label, label_end, true); - ImVec2 label_pos = window->DC.CursorPos; - - // If we already got a row height, there's use that. - // FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect? - ImRect cell_r = TableGetCellBgRect(table, column_n); - float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f); - - // Calculate ideal size for sort order arrow - float w_arrow = 0.0f; - float w_sort_text = 0.0f; - char sort_order_suf[4] = ""; - const float ARROW_SCALE = 0.65f; - if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) - { - w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x); - if (column->SortOrder > 0) - { - ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1); - w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x; - } - } - - // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging. - float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow; - column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, column->WorkMaxX); - column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x); - - // Keep header highlighted when context menu is open. - const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceCurrent); - ImGuiID id = window->GetID(label); - ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f)); - ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal - if (!ItemAdd(bb, id)) - return; - - //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] - //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] - - // Using AllowItemOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowItemOverlap); - if (g.ActiveId != id) - SetItemAllowOverlap(); - if (held || hovered || selected) - { - const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - //RenderFrame(bb.Min, bb.Max, col, false, 0.0f); - TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn); - } - else - { - // Submit single cell bg color in the case we didn't submit a full header row - if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) - TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); - } - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); - if (held) - table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; - window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; - - // Drag and drop to re-order columns. - // FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone. - if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive) - { - // While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x - table->ReorderColumn = (ImGuiTableColumnIdx)column_n; - table->InstanceInteracted = table->InstanceCurrent; - - // We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder. - if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x) - if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL) - if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder)) - if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest)) - table->ReorderColumnDir = -1; - if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x) - if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL) - if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder)) - if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest)) - table->ReorderColumnDir = +1; - } - - // Sort order arrow - const float ellipsis_max = cell_r.Max.x - w_arrow - w_sort_text; - if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) - { - if (column->SortOrder != -1) - { - float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text); - float y = label_pos.y; - if (column->SortOrder > 0) - { - PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f)); - RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf); - PopStyleColor(); - x += w_sort_text; - } - RenderArrow(window->DrawList, ImVec2(x, y), GetColorU32(ImGuiCol_Text), column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE); - } - - // Handle clicking on column header to adjust Sort Order - if (pressed && table->ReorderColumn != column_n) - { - ImGuiSortDirection sort_direction = TableGetColumnNextSortDirection(column); - TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift); - } - } - - // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will - // be merged into a single draw call. - //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE); - RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size); - - const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x); - if (text_clipped && hovered && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay) - SetTooltip("%.*s", (int)(label_end - label), label); - - // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden - if (IsMouseReleased(1) && IsItemHovered()) - TableOpenContextMenu(column_n); -} - -//------------------------------------------------------------------------- -// [SECTION] Tables: Context Menu -//------------------------------------------------------------------------- -// - TableOpenContextMenu() [Internal] -// - TableDrawContextMenu() [Internal] -//------------------------------------------------------------------------- - -// Use -1 to open menu not specific to a given column. -void ImGui::TableOpenContextMenu(int column_n) -{ - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - if (column_n == -1 && table->CurrentColumn != -1) // When called within a column automatically use this one (for consistency) - column_n = table->CurrentColumn; - if (column_n == table->ColumnsCount) // To facilitate using with TableGetHoveredColumn() - column_n = -1; - IM_ASSERT(column_n >= -1 && column_n < table->ColumnsCount); - if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) - { - table->IsContextPopupOpen = true; - table->ContextPopupColumn = (ImGuiTableColumnIdx)column_n; - table->InstanceInteracted = table->InstanceCurrent; - const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID); - OpenPopupEx(context_menu_id, ImGuiPopupFlags_None); - } -} - -// Output context menu into current window (generally a popup) -// FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data? -void ImGui::TableDrawContextMenu(ImGuiTable* table) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return; - - bool want_separator = false; - const int column_n = (table->ContextPopupColumn >= 0 && table->ContextPopupColumn < table->ColumnsCount) ? table->ContextPopupColumn : -1; - ImGuiTableColumn* column = (column_n != -1) ? &table->Columns[column_n] : NULL; - - // Sizing - if (table->Flags & ImGuiTableFlags_Resizable) - { - if (column != NULL) - { - const bool can_resize = !(column->Flags & ImGuiTableColumnFlags_NoResize) && column->IsEnabled; - if (MenuItem("Size column to fit###SizeOne", NULL, false, can_resize)) - TableSetColumnWidthAutoSingle(table, column_n); - } - - const char* size_all_desc; - if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount && (table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame) - size_all_desc = "Size all columns to fit###SizeAll"; // All fixed - else - size_all_desc = "Size all columns to default###SizeAll"; // All stretch or mixed - if (MenuItem(size_all_desc, NULL)) - TableSetColumnWidthAutoAll(table); - want_separator = true; - } - - // Ordering - if (table->Flags & ImGuiTableFlags_Reorderable) - { - if (MenuItem("Reset order", NULL, false, !table->IsDefaultDisplayOrder)) - table->IsResetDisplayOrderRequest = true; - want_separator = true; - } - - // Reset all (should work but seems unnecessary/noisy to expose?) - //if (MenuItem("Reset all")) - // table->IsResetAllRequest = true; - - // Sorting - // (modify TableOpenContextMenu() to add _Sortable flag if enabling this) -#if 0 - if ((table->Flags & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0) - { - if (want_separator) - Separator(); - want_separator = true; - - bool append_to_sort_specs = g.IO.KeyShift; - if (MenuItem("Sort in Ascending Order", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Ascending, (column->Flags & ImGuiTableColumnFlags_NoSortAscending) == 0)) - TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Ascending, append_to_sort_specs); - if (MenuItem("Sort in Descending Order", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Descending, (column->Flags & ImGuiTableColumnFlags_NoSortDescending) == 0)) - TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Descending, append_to_sort_specs); - } -#endif - - // Hiding / Visibility - if (table->Flags & ImGuiTableFlags_Hideable) - { - if (want_separator) - Separator(); - want_separator = true; - - PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true); - for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) - { - ImGuiTableColumn* other_column = &table->Columns[other_column_n]; - if (other_column->Flags & ImGuiTableColumnFlags_Disabled) - continue; - - const char* name = TableGetColumnName(table, other_column_n); - if (name == NULL || name[0] == 0) - name = ""; - - // Make sure we can't hide the last active column - bool menu_item_active = (other_column->Flags & ImGuiTableColumnFlags_NoHide) ? false : true; - if (other_column->IsUserEnabled && table->ColumnsEnabledCount <= 1) - menu_item_active = false; - if (MenuItem(name, NULL, other_column->IsUserEnabled, menu_item_active)) - other_column->IsUserEnabledNextFrame = !other_column->IsUserEnabled; - } - PopItemFlag(); - } -} - -//------------------------------------------------------------------------- -// [SECTION] Tables: Settings (.ini data) -//------------------------------------------------------------------------- -// FIXME: The binding/finding/creating flow are too confusing. -//------------------------------------------------------------------------- -// - TableSettingsInit() [Internal] -// - TableSettingsCalcChunkSize() [Internal] -// - TableSettingsCreate() [Internal] -// - TableSettingsFindByID() [Internal] -// - TableGetBoundSettings() [Internal] -// - TableResetSettings() -// - TableSaveSettings() [Internal] -// - TableLoadSettings() [Internal] -// - TableSettingsHandler_ClearAll() [Internal] -// - TableSettingsHandler_ApplyAll() [Internal] -// - TableSettingsHandler_ReadOpen() [Internal] -// - TableSettingsHandler_ReadLine() [Internal] -// - TableSettingsHandler_WriteAll() [Internal] -// - TableSettingsInstallHandler() [Internal] -//------------------------------------------------------------------------- -// [Init] 1: TableSettingsHandler_ReadXXXX() Load and parse .ini file into TableSettings. -// [Main] 2: TableLoadSettings() When table is created, bind Table to TableSettings, serialize TableSettings data into Table. -// [Main] 3: TableSaveSettings() When table properties are modified, serialize Table data into bound or new TableSettings, mark .ini as dirty. -// [Main] 4: TableSettingsHandler_WriteAll() When .ini file is dirty (which can come from other source), save TableSettings into .ini file. -//------------------------------------------------------------------------- - -// Clear and initialize empty settings instance -static void TableSettingsInit(ImGuiTableSettings* settings, ImGuiID id, int columns_count, int columns_count_max) -{ - IM_PLACEMENT_NEW(settings) ImGuiTableSettings(); - ImGuiTableColumnSettings* settings_column = settings->GetColumnSettings(); - for (int n = 0; n < columns_count_max; n++, settings_column++) - IM_PLACEMENT_NEW(settings_column) ImGuiTableColumnSettings(); - settings->ID = id; - settings->ColumnsCount = (ImGuiTableColumnIdx)columns_count; - settings->ColumnsCountMax = (ImGuiTableColumnIdx)columns_count_max; - settings->WantApply = true; -} - -static size_t TableSettingsCalcChunkSize(int columns_count) -{ - return sizeof(ImGuiTableSettings) + (size_t)columns_count * sizeof(ImGuiTableColumnSettings); -} - -ImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count) -{ - ImGuiContext& g = *GImGui; - ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(TableSettingsCalcChunkSize(columns_count)); - TableSettingsInit(settings, id, columns_count, columns_count); - return settings; -} - -// Find existing settings -ImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id) -{ - // FIXME-OPT: Might want to store a lookup map for this? - ImGuiContext& g = *GImGui; - for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) - if (settings->ID == id) - return settings; - return NULL; -} - -// Get settings for a given table, NULL if none -ImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table) -{ - if (table->SettingsOffset != -1) - { - ImGuiContext& g = *GImGui; - ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset); - IM_ASSERT(settings->ID == table->ID); - if (settings->ColumnsCountMax >= table->ColumnsCount) - return settings; // OK - settings->ID = 0; // Invalidate storage, we won't fit because of a count change - } - return NULL; -} - -// Restore initial state of table (with or without saved settings) -void ImGui::TableResetSettings(ImGuiTable* table) -{ - table->IsInitializing = table->IsSettingsDirty = true; - table->IsResetAllRequest = false; - table->IsSettingsRequestLoad = false; // Don't reload from ini - table->SettingsLoadedFlags = ImGuiTableFlags_None; // Mark as nothing loaded so our initialized data becomes authoritative -} - -void ImGui::TableSaveSettings(ImGuiTable* table) -{ - table->IsSettingsDirty = false; - if (table->Flags & ImGuiTableFlags_NoSavedSettings) - return; - - // Bind or create settings data - ImGuiContext& g = *GImGui; - ImGuiTableSettings* settings = TableGetBoundSettings(table); - if (settings == NULL) - { - settings = TableSettingsCreate(table->ID, table->ColumnsCount); - table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings); - } - settings->ColumnsCount = (ImGuiTableColumnIdx)table->ColumnsCount; - - // Serialize ImGuiTable/ImGuiTableColumn into ImGuiTableSettings/ImGuiTableColumnSettings - IM_ASSERT(settings->ID == table->ID); - IM_ASSERT(settings->ColumnsCount == table->ColumnsCount && settings->ColumnsCountMax >= settings->ColumnsCount); - ImGuiTableColumn* column = table->Columns.Data; - ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); - - bool save_ref_scale = false; - settings->SaveFlags = ImGuiTableFlags_None; - for (int n = 0; n < table->ColumnsCount; n++, column++, column_settings++) - { - const float width_or_weight = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? column->StretchWeight : column->WidthRequest; - column_settings->WidthOrWeight = width_or_weight; - column_settings->Index = (ImGuiTableColumnIdx)n; - column_settings->DisplayOrder = column->DisplayOrder; - column_settings->SortOrder = column->SortOrder; - column_settings->SortDirection = column->SortDirection; - column_settings->IsEnabled = column->IsUserEnabled; - column_settings->IsStretch = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? 1 : 0; - if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) == 0) - save_ref_scale = true; - - // We skip saving some data in the .ini file when they are unnecessary to restore our state. - // Note that fixed width where initial width was derived from auto-fit will always be saved as InitStretchWeightOrWidth will be 0.0f. - // FIXME-TABLE: We don't have logic to easily compare SortOrder to DefaultSortOrder yet so it's always saved when present. - if (width_or_weight != column->InitStretchWeightOrWidth) - settings->SaveFlags |= ImGuiTableFlags_Resizable; - if (column->DisplayOrder != n) - settings->SaveFlags |= ImGuiTableFlags_Reorderable; - if (column->SortOrder != -1) - settings->SaveFlags |= ImGuiTableFlags_Sortable; - if (column->IsUserEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0)) - settings->SaveFlags |= ImGuiTableFlags_Hideable; - } - settings->SaveFlags &= table->Flags; - settings->RefScale = save_ref_scale ? table->RefScale : 0.0f; - - MarkIniSettingsDirty(); -} - -void ImGui::TableLoadSettings(ImGuiTable* table) -{ - ImGuiContext& g = *GImGui; - table->IsSettingsRequestLoad = false; - if (table->Flags & ImGuiTableFlags_NoSavedSettings) - return; - - // Bind settings - ImGuiTableSettings* settings; - if (table->SettingsOffset == -1) - { - settings = TableSettingsFindByID(table->ID); - if (settings == NULL) - return; - if (settings->ColumnsCount != table->ColumnsCount) // Allow settings if columns count changed. We could otherwise decide to return... - table->IsSettingsDirty = true; - table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings); - } - else - { - settings = TableGetBoundSettings(table); - } - - table->SettingsLoadedFlags = settings->SaveFlags; - table->RefScale = settings->RefScale; - - // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn - ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); - ImU64 display_order_mask = 0; - for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++) - { - int column_n = column_settings->Index; - if (column_n < 0 || column_n >= table->ColumnsCount) - continue; - - ImGuiTableColumn* column = &table->Columns[column_n]; - if (settings->SaveFlags & ImGuiTableFlags_Resizable) - { - if (column_settings->IsStretch) - column->StretchWeight = column_settings->WidthOrWeight; - else - column->WidthRequest = column_settings->WidthOrWeight; - column->AutoFitQueue = 0x00; - } - if (settings->SaveFlags & ImGuiTableFlags_Reorderable) - column->DisplayOrder = column_settings->DisplayOrder; - else - column->DisplayOrder = (ImGuiTableColumnIdx)column_n; - display_order_mask |= (ImU64)1 << column->DisplayOrder; - column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled; - column->SortOrder = column_settings->SortOrder; - column->SortDirection = column_settings->SortDirection; - } - - // Validate and fix invalid display order data - const ImU64 expected_display_order_mask = (settings->ColumnsCount == 64) ? ~0 : ((ImU64)1 << settings->ColumnsCount) - 1; - if (display_order_mask != expected_display_order_mask) - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - table->Columns[column_n].DisplayOrder = (ImGuiTableColumnIdx)column_n; - - // Rebuild index - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; -} - -static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) -{ - ImGuiContext& g = *ctx; - for (int i = 0; i != g.Tables.GetMapSize(); i++) - if (ImGuiTable* table = g.Tables.TryGetMapData(i)) - table->SettingsOffset = -1; - g.SettingsTables.clear(); -} - -// Apply to existing windows (if any) -static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) -{ - ImGuiContext& g = *ctx; - for (int i = 0; i != g.Tables.GetMapSize(); i++) - if (ImGuiTable* table = g.Tables.TryGetMapData(i)) - { - table->IsSettingsRequestLoad = true; - table->SettingsOffset = -1; - } -} - -static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) -{ - ImGuiID id = 0; - int columns_count = 0; - if (sscanf(name, "0x%08X,%d", &id, &columns_count) < 2) - return NULL; - - if (ImGuiTableSettings* settings = ImGui::TableSettingsFindByID(id)) - { - if (settings->ColumnsCountMax >= columns_count) - { - TableSettingsInit(settings, id, columns_count, settings->ColumnsCountMax); // Recycle - return settings; - } - settings->ID = 0; // Invalidate storage, we won't fit because of a count change - } - return ImGui::TableSettingsCreate(id, columns_count); -} - -static void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) -{ - // "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v" - ImGuiTableSettings* settings = (ImGuiTableSettings*)entry; - float f = 0.0f; - int column_n = 0, r = 0, n = 0; - - if (sscanf(line, "RefScale=%f", &f) == 1) { settings->RefScale = f; return; } - - if (sscanf(line, "Column %d%n", &column_n, &r) == 1) - { - if (column_n < 0 || column_n >= settings->ColumnsCount) - return; - line = ImStrSkipBlank(line + r); - char c = 0; - ImGuiTableColumnSettings* column = settings->GetColumnSettings() + column_n; - column->Index = (ImGuiTableColumnIdx)column_n; - if (sscanf(line, "UserID=0x%08X%n", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); column->UserID = (ImGuiID)n; } - if (sscanf(line, "Width=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = (float)n; column->IsStretch = 0; settings->SaveFlags |= ImGuiTableFlags_Resizable; } - if (sscanf(line, "Weight=%f%n", &f, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = f; column->IsStretch = 1; settings->SaveFlags |= ImGuiTableFlags_Resizable; } - if (sscanf(line, "Visible=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->IsEnabled = (ImU8)n; settings->SaveFlags |= ImGuiTableFlags_Hideable; } - if (sscanf(line, "Order=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->DisplayOrder = (ImGuiTableColumnIdx)n; settings->SaveFlags |= ImGuiTableFlags_Reorderable; } - if (sscanf(line, "Sort=%d%c%n", &n, &c, &r) == 2) { line = ImStrSkipBlank(line + r); column->SortOrder = (ImGuiTableColumnIdx)n; column->SortDirection = (c == '^') ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; settings->SaveFlags |= ImGuiTableFlags_Sortable; } - } -} - -static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -{ - ImGuiContext& g = *ctx; - for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) - { - if (settings->ID == 0) // Skip ditched settings - continue; - - // TableSaveSettings() may clear some of those flags when we establish that the data can be stripped - // (e.g. Order was unchanged) - const bool save_size = (settings->SaveFlags & ImGuiTableFlags_Resizable) != 0; - const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0; - const bool save_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0; - const bool save_sort = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0; - if (!save_size && !save_visible && !save_order && !save_sort) - continue; - - buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve - buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount); - if (settings->RefScale != 0.0f) - buf->appendf("RefScale=%g\n", settings->RefScale); - ImGuiTableColumnSettings* column = settings->GetColumnSettings(); - for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++) - { - // "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v" - bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1); - if (!save_column) - continue; - buf->appendf("Column %-2d", column_n); - if (column->UserID != 0) buf->appendf(" UserID=%08X", column->UserID); - if (save_size && column->IsStretch) buf->appendf(" Weight=%.4f", column->WidthOrWeight); - if (save_size && !column->IsStretch) buf->appendf(" Width=%d", (int)column->WidthOrWeight); - if (save_visible) buf->appendf(" Visible=%d", column->IsEnabled); - if (save_order) buf->appendf(" Order=%d", column->DisplayOrder); - if (save_sort && column->SortOrder != -1) buf->appendf(" Sort=%d%c", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^'); - buf->append("\n"); - } - buf->append("\n"); - } -} - -void ImGui::TableSettingsAddSettingsHandler() -{ - ImGuiSettingsHandler ini_handler; - ini_handler.TypeName = "Table"; - ini_handler.TypeHash = ImHashStr("Table"); - ini_handler.ClearAllFn = TableSettingsHandler_ClearAll; - ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen; - ini_handler.ReadLineFn = TableSettingsHandler_ReadLine; - ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll; - ini_handler.WriteAllFn = TableSettingsHandler_WriteAll; - AddSettingsHandler(&ini_handler); -} - -//------------------------------------------------------------------------- -// [SECTION] Tables: Garbage Collection -//------------------------------------------------------------------------- -// - TableRemove() [Internal] -// - TableGcCompactTransientBuffers() [Internal] -// - TableGcCompactSettings() [Internal] -//------------------------------------------------------------------------- - -// Remove Table (currently only used by TestEngine) -void ImGui::TableRemove(ImGuiTable* table) -{ - //IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID); - ImGuiContext& g = *GImGui; - int table_idx = g.Tables.GetIndex(table); - //memset(table->RawData.Data, 0, table->RawData.size_in_bytes()); - //memset(table, 0, sizeof(ImGuiTable)); - g.Tables.Remove(table->ID, table); - g.TablesLastTimeActive[table_idx] = -1.0f; -} - -// Free up/compact internal Table buffers for when it gets unused -void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) -{ - //IMGUI_DEBUG_PRINT("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID); - ImGuiContext& g = *GImGui; - IM_ASSERT(table->MemoryCompacted == false); - table->SortSpecs.Specs = NULL; - table->SortSpecsMulti.clear(); - table->IsSortSpecsDirty = true; // FIXME: shouldn't have to leak into user performing a sort - table->ColumnsNames.clear(); - table->MemoryCompacted = true; - for (int n = 0; n < table->ColumnsCount; n++) - table->Columns[n].NameOffset = -1; - g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f; -} - -void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data) -{ - temp_data->DrawSplitter.ClearFreeMemory(); - temp_data->LastTimeActive = -1.0f; -} - -// Compact and remove unused settings data (currently only used by TestEngine) -void ImGui::TableGcCompactSettings() -{ - ImGuiContext& g = *GImGui; - int required_memory = 0; - for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) - if (settings->ID != 0) - required_memory += (int)TableSettingsCalcChunkSize(settings->ColumnsCount); - if (required_memory == g.SettingsTables.Buf.Size) - return; - ImChunkStream new_chunk_stream; - new_chunk_stream.Buf.reserve(required_memory); - for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) - if (settings->ID != 0) - memcpy(new_chunk_stream.alloc_chunk(TableSettingsCalcChunkSize(settings->ColumnsCount)), settings, TableSettingsCalcChunkSize(settings->ColumnsCount)); - g.SettingsTables.swap(new_chunk_stream); -} - - -//------------------------------------------------------------------------- -// [SECTION] Tables: Debugging -//------------------------------------------------------------------------- -// - DebugNodeTable() [Internal] -//------------------------------------------------------------------------- - -#ifndef IMGUI_DISABLE_DEBUG_TOOLS - -static const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_policy) -{ - sizing_policy &= ImGuiTableFlags_SizingMask_; - if (sizing_policy == ImGuiTableFlags_SizingFixedFit) { return "FixedFit"; } - if (sizing_policy == ImGuiTableFlags_SizingFixedSame) { return "FixedSame"; } - if (sizing_policy == ImGuiTableFlags_SizingStretchProp) { return "StretchProp"; } - if (sizing_policy == ImGuiTableFlags_SizingStretchSame) { return "StretchSame"; } - return "N/A"; -} - -void ImGui::DebugNodeTable(ImGuiTable* table) -{ - char buf[512]; - char* p = buf; - const char* buf_end = buf + IM_ARRAYSIZE(buf); - const bool is_active = (table->LastFrameActive >= ImGui::GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here. - ImFormatString(p, buf_end - p, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*"); - if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } - bool open = TreeNode(table, "%s", buf); - if (!is_active) { PopStyleColor(); } - if (IsItemHovered()) - GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255)); - if (IsItemVisible() && table->HoveredColumnBody != -1) - GetForegroundDrawList()->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255)); - if (!open) - return; - if (table->InstanceCurrent > 0) - ImGui::Text("** %d instances of same table! Some data below will refer to last instance.", table->InstanceCurrent + 1); - bool clear_settings = SmallButton("Clear settings"); - BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight(), DebugNodeTableGetSizingPolicyDesc(table->Flags)); - BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", table->ColumnsGivenWidth, table->ColumnsAutoFitWidth, table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : ""); - BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", table->CellPaddingX, table->CellSpacingX1, table->CellSpacingX2, table->OuterPaddingX); - BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder); - BulletText("ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn); - //BulletText("BgDrawChannels: %d/%d", 0, table->BgDrawChannelUnfrozen); - float sum_weights = 0.0f; - for (int n = 0; n < table->ColumnsCount; n++) - if (table->Columns[n].Flags & ImGuiTableColumnFlags_WidthStretch) - sum_weights += table->Columns[n].StretchWeight; - for (int n = 0; n < table->ColumnsCount; n++) - { - ImGuiTableColumn* column = &table->Columns[n]; - const char* name = TableGetColumnName(table, n); - ImFormatString(buf, IM_ARRAYSIZE(buf), - "Column %d order %d '%s': offset %+.2f to %+.2f%s\n" - "Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n" - "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\n" - "MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n" - "ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\n" - "Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s..", - n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x, (n < table->FreezeColumnsRequest) ? " (Frozen)" : "", - column->IsEnabled, column->IsVisibleX, column->IsVisibleY, column->IsRequestOutput, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen, - column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, column->StretchWeight > 0.0f ? (column->StretchWeight / sum_weights) * 100.0f : 0.0f, - column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x, - column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX, - column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? " (Asc)" : (column->SortDirection == ImGuiSortDirection_Descending) ? " (Des)" : "", column->UserID, column->Flags, - (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? "WidthStretch " : "", - (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? "WidthFixed " : "", - (column->Flags & ImGuiTableColumnFlags_NoResize) ? "NoResize " : ""); - Bullet(); - Selectable(buf); - if (IsItemHovered()) - { - ImRect r(column->MinX, table->OuterRect.Min.y, column->MaxX, table->OuterRect.Max.y); - GetForegroundDrawList()->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255)); - } - } - if (ImGuiTableSettings* settings = TableGetBoundSettings(table)) - DebugNodeTableSettings(settings); - if (clear_settings) - table->IsResetAllRequest = true; - TreePop(); -} - -void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings) -{ - if (!TreeNode((void*)(intptr_t)settings->ID, "Settings 0x%08X (%d columns)", settings->ID, settings->ColumnsCount)) - return; - BulletText("SaveFlags: 0x%08X", settings->SaveFlags); - BulletText("ColumnsCount: %d (max %d)", settings->ColumnsCount, settings->ColumnsCountMax); - for (int n = 0; n < settings->ColumnsCount; n++) - { - ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n]; - ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None; - BulletText("Column %d Order %d SortOrder %d %s Vis %d %s %7.3f UserID 0x%08X", - n, column_settings->DisplayOrder, column_settings->SortOrder, - (sort_dir == ImGuiSortDirection_Ascending) ? "Asc" : (sort_dir == ImGuiSortDirection_Descending) ? "Des" : "---", - column_settings->IsEnabled, column_settings->IsStretch ? "Weight" : "Width ", column_settings->WidthOrWeight, column_settings->UserID); - } - TreePop(); -} - -#else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS - -void ImGui::DebugNodeTable(ImGuiTable*) {} -void ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {} - -#endif - - -//------------------------------------------------------------------------- -// [SECTION] Columns, BeginColumns, EndColumns, etc. -// (This is a legacy API, prefer using BeginTable/EndTable!) -//------------------------------------------------------------------------- -// FIXME: sizing is lossy when columns width is very small (default width may turn negative etc.) -//------------------------------------------------------------------------- -// - SetWindowClipRectBeforeSetChannel() [Internal] -// - GetColumnIndex() -// - GetColumnsCount() -// - GetColumnOffset() -// - GetColumnWidth() -// - SetColumnOffset() -// - SetColumnWidth() -// - PushColumnClipRect() [Internal] -// - PushColumnsBackground() [Internal] -// - PopColumnsBackground() [Internal] -// - FindOrCreateColumns() [Internal] -// - GetColumnsID() [Internal] -// - BeginColumns() -// - NextColumn() -// - EndColumns() -// - Columns() -//------------------------------------------------------------------------- - -// [Internal] Small optimization to avoid calls to PopClipRect/SetCurrentChannel/PushClipRect in sequences, -// they would meddle many times with the underlying ImDrawCmd. -// Instead, we do a preemptive overwrite of clipping rectangle _without_ altering the command-buffer and let -// the subsequent single call to SetCurrentChannel() does it things once. -void ImGui::SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect) -{ - ImVec4 clip_rect_vec4 = clip_rect.ToVec4(); - window->ClipRect = clip_rect; - window->DrawList->_CmdHeader.ClipRect = clip_rect_vec4; - window->DrawList->_ClipRectStack.Data[window->DrawList->_ClipRectStack.Size - 1] = clip_rect_vec4; -} - -int ImGui::GetColumnIndex() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CurrentColumns ? window->DC.CurrentColumns->Current : 0; -} - -int ImGui::GetColumnsCount() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CurrentColumns ? window->DC.CurrentColumns->Count : 1; -} - -float ImGui::GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm) -{ - return offset_norm * (columns->OffMaxX - columns->OffMinX); -} - -float ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset) -{ - return offset / (columns->OffMaxX - columns->OffMinX); -} - -static const float COLUMNS_HIT_RECT_HALF_WIDTH = 4.0f; - -static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index) -{ - // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing - // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning. - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(column_index > 0); // We are not supposed to drag column 0. - IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index)); - - float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + COLUMNS_HIT_RECT_HALF_WIDTH - window->Pos.x; - x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing); - if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths)) - x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing); - - return x; -} - -float ImGui::GetColumnOffset(int column_index) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiOldColumns* columns = window->DC.CurrentColumns; - if (columns == NULL) - return 0.0f; - - if (column_index < 0) - column_index = columns->Current; - IM_ASSERT(column_index < columns->Columns.Size); - - const float t = columns->Columns[column_index].OffsetNorm; - const float x_offset = ImLerp(columns->OffMinX, columns->OffMaxX, t); - return x_offset; -} - -static float GetColumnWidthEx(ImGuiOldColumns* columns, int column_index, bool before_resize = false) -{ - if (column_index < 0) - column_index = columns->Current; - - float offset_norm; - if (before_resize) - offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize; - else - offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm; - return ImGui::GetColumnOffsetFromNorm(columns, offset_norm); -} - -float ImGui::GetColumnWidth(int column_index) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiOldColumns* columns = window->DC.CurrentColumns; - if (columns == NULL) - return GetContentRegionAvail().x; - - if (column_index < 0) - column_index = columns->Current; - return GetColumnOffsetFromNorm(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm); -} - -void ImGui::SetColumnOffset(int column_index, float offset) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiOldColumns* columns = window->DC.CurrentColumns; - IM_ASSERT(columns != NULL); - - if (column_index < 0) - column_index = columns->Current; - IM_ASSERT(column_index < columns->Columns.Size); - - const bool preserve_width = !(columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths) && (column_index < columns->Count - 1); - const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f; - - if (!(columns->Flags & ImGuiOldColumnFlags_NoForceWithinWindow)) - offset = ImMin(offset, columns->OffMaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index)); - columns->Columns[column_index].OffsetNorm = GetColumnNormFromOffset(columns, offset - columns->OffMinX); - - if (preserve_width) - SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width)); -} - -void ImGui::SetColumnWidth(int column_index, float width) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiOldColumns* columns = window->DC.CurrentColumns; - IM_ASSERT(columns != NULL); - - if (column_index < 0) - column_index = columns->Current; - SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width); -} - -void ImGui::PushColumnClipRect(int column_index) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiOldColumns* columns = window->DC.CurrentColumns; - if (column_index < 0) - column_index = columns->Current; - - ImGuiOldColumnData* column = &columns->Columns[column_index]; - PushClipRect(column->ClipRect.Min, column->ClipRect.Max, false); -} - -// Get into the columns background draw command (which is generally the same draw command as before we called BeginColumns) -void ImGui::PushColumnsBackground() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiOldColumns* columns = window->DC.CurrentColumns; - if (columns->Count == 1) - return; - - // Optimization: avoid SetCurrentChannel() + PushClipRect() - columns->HostBackupClipRect = window->ClipRect; - SetWindowClipRectBeforeSetChannel(window, columns->HostInitialClipRect); - columns->Splitter.SetCurrentChannel(window->DrawList, 0); -} - -void ImGui::PopColumnsBackground() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiOldColumns* columns = window->DC.CurrentColumns; - if (columns->Count == 1) - return; - - // Optimization: avoid PopClipRect() + SetCurrentChannel() - SetWindowClipRectBeforeSetChannel(window, columns->HostBackupClipRect); - columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1); -} - -ImGuiOldColumns* ImGui::FindOrCreateColumns(ImGuiWindow* window, ImGuiID id) -{ - // We have few columns per window so for now we don't need bother much with turning this into a faster lookup. - for (int n = 0; n < window->ColumnsStorage.Size; n++) - if (window->ColumnsStorage[n].ID == id) - return &window->ColumnsStorage[n]; - - window->ColumnsStorage.push_back(ImGuiOldColumns()); - ImGuiOldColumns* columns = &window->ColumnsStorage.back(); - columns->ID = id; - return columns; -} - -ImGuiID ImGui::GetColumnsID(const char* str_id, int columns_count) -{ - ImGuiWindow* window = GetCurrentWindow(); - - // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. - // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer. - PushID(0x11223347 + (str_id ? 0 : columns_count)); - ImGuiID id = window->GetID(str_id ? str_id : "columns"); - PopID(); - - return id; -} - -void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - IM_ASSERT(columns_count >= 1); - IM_ASSERT(window->DC.CurrentColumns == NULL); // Nested columns are currently not supported - - // Acquire storage for the columns set - ImGuiID id = GetColumnsID(str_id, columns_count); - ImGuiOldColumns* columns = FindOrCreateColumns(window, id); - IM_ASSERT(columns->ID == id); - columns->Current = 0; - columns->Count = columns_count; - columns->Flags = flags; - window->DC.CurrentColumns = columns; - - columns->HostCursorPosY = window->DC.CursorPos.y; - columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x; - columns->HostInitialClipRect = window->ClipRect; - columns->HostBackupParentWorkRect = window->ParentWorkRect; - window->ParentWorkRect = window->WorkRect; - - // Set state for first column - // We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect - const float column_padding = g.Style.ItemSpacing.x; - const float half_clip_extend_x = ImFloor(ImMax(window->WindowPadding.x * 0.5f, window->WindowBorderSize)); - const float max_1 = window->WorkRect.Max.x + column_padding - ImMax(column_padding - window->WindowPadding.x, 0.0f); - const float max_2 = window->WorkRect.Max.x + half_clip_extend_x; - columns->OffMinX = window->DC.Indent.x - column_padding + ImMax(column_padding - window->WindowPadding.x, 0.0f); - columns->OffMaxX = ImMax(ImMin(max_1, max_2) - window->Pos.x, columns->OffMinX + 1.0f); - columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y; - - // Clear data if columns count changed - if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1) - columns->Columns.resize(0); - - // Initialize default widths - columns->IsFirstFrame = (columns->Columns.Size == 0); - if (columns->Columns.Size == 0) - { - columns->Columns.reserve(columns_count + 1); - for (int n = 0; n < columns_count + 1; n++) - { - ImGuiOldColumnData column; - column.OffsetNorm = n / (float)columns_count; - columns->Columns.push_back(column); - } - } - - for (int n = 0; n < columns_count; n++) - { - // Compute clipping rectangle - ImGuiOldColumnData* column = &columns->Columns[n]; - float clip_x1 = IM_ROUND(window->Pos.x + GetColumnOffset(n)); - float clip_x2 = IM_ROUND(window->Pos.x + GetColumnOffset(n + 1) - 1.0f); - column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX); - column->ClipRect.ClipWithFull(window->ClipRect); - } - - if (columns->Count > 1) - { - columns->Splitter.Split(window->DrawList, 1 + columns->Count); - columns->Splitter.SetCurrentChannel(window->DrawList, 1); - PushColumnClipRect(0); - } - - // We don't generally store Indent.x inside ColumnsOffset because it may be manipulated by the user. - float offset_0 = GetColumnOffset(columns->Current); - float offset_1 = GetColumnOffset(columns->Current + 1); - float width = offset_1 - offset_0; - PushItemWidth(width * 0.65f); - window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f); - window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); - window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding; -} - -void ImGui::NextColumn() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems || window->DC.CurrentColumns == NULL) - return; - - ImGuiContext& g = *GImGui; - ImGuiOldColumns* columns = window->DC.CurrentColumns; - - if (columns->Count == 1) - { - window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); - IM_ASSERT(columns->Current == 0); - return; - } - - // Next column - if (++columns->Current == columns->Count) - columns->Current = 0; - - PopItemWidth(); - - // Optimization: avoid PopClipRect() + SetCurrentChannel() + PushClipRect() - // (which would needlessly attempt to update commands in the wrong channel, then pop or overwrite them), - ImGuiOldColumnData* column = &columns->Columns[columns->Current]; - SetWindowClipRectBeforeSetChannel(window, column->ClipRect); - columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1); - - const float column_padding = g.Style.ItemSpacing.x; - columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); - if (columns->Current > 0) - { - // Columns 1+ ignore IndentX (by canceling it out) - // FIXME-COLUMNS: Unnecessary, could be locked? - window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + column_padding; - } - else - { - // New row/line: column 0 honor IndentX. - window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f); - columns->LineMinY = columns->LineMaxY; - } - window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); - window->DC.CursorPos.y = columns->LineMinY; - window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); - window->DC.CurrLineTextBaseOffset = 0.0f; - - // FIXME-COLUMNS: Share code with BeginColumns() - move code on columns setup. - float offset_0 = GetColumnOffset(columns->Current); - float offset_1 = GetColumnOffset(columns->Current + 1); - float width = offset_1 - offset_0; - PushItemWidth(width * 0.65f); - window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding; -} - -void ImGui::EndColumns() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - ImGuiOldColumns* columns = window->DC.CurrentColumns; - IM_ASSERT(columns != NULL); - - PopItemWidth(); - if (columns->Count > 1) - { - PopClipRect(); - columns->Splitter.Merge(window->DrawList); - } - - const ImGuiOldColumnFlags flags = columns->Flags; - columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); - window->DC.CursorPos.y = columns->LineMaxY; - if (!(flags & ImGuiOldColumnFlags_GrowParentContentsSize)) - window->DC.CursorMaxPos.x = columns->HostCursorMaxPosX; // Restore cursor max pos, as columns don't grow parent - - // Draw columns borders and handle resize - // The IsBeingResized flag ensure we preserve pre-resize columns width so back-and-forth are not lossy - bool is_being_resized = false; - if (!(flags & ImGuiOldColumnFlags_NoBorder) && !window->SkipItems) - { - // We clip Y boundaries CPU side because very long triangles are mishandled by some GPU drivers. - const float y1 = ImMax(columns->HostCursorPosY, window->ClipRect.Min.y); - const float y2 = ImMin(window->DC.CursorPos.y, window->ClipRect.Max.y); - int dragging_column = -1; - for (int n = 1; n < columns->Count; n++) - { - ImGuiOldColumnData* column = &columns->Columns[n]; - float x = window->Pos.x + GetColumnOffset(n); - const ImGuiID column_id = columns->ID + ImGuiID(n); - const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH; - const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); - KeepAliveID(column_id); - if (IsClippedEx(column_hit_rect, column_id)) // FIXME: Can be removed or replaced with a lower-level test - continue; - - bool hovered = false, held = false; - if (!(flags & ImGuiOldColumnFlags_NoResize)) - { - ButtonBehavior(column_hit_rect, column_id, &hovered, &held); - if (hovered || held) - g.MouseCursor = ImGuiMouseCursor_ResizeEW; - if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize)) - dragging_column = n; - } - - // Draw column - const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); - const float xi = IM_FLOOR(x); - window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col); - } - - // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame. - if (dragging_column != -1) - { - if (!columns->IsBeingResized) - for (int n = 0; n < columns->Count + 1; n++) - columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm; - columns->IsBeingResized = is_being_resized = true; - float x = GetDraggedColumnOffset(columns, dragging_column); - SetColumnOffset(dragging_column, x); - } - } - columns->IsBeingResized = is_being_resized; - - window->WorkRect = window->ParentWorkRect; - window->ParentWorkRect = columns->HostBackupParentWorkRect; - window->DC.CurrentColumns = NULL; - window->DC.ColumnsOffset.x = 0.0f; - window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); -} - -void ImGui::Columns(int columns_count, const char* id, bool border) -{ - ImGuiWindow* window = GetCurrentWindow(); - IM_ASSERT(columns_count >= 1); - - ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder); - //flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior - ImGuiOldColumns* columns = window->DC.CurrentColumns; - if (columns != NULL && columns->Count == columns_count && columns->Flags == flags) - return; - - if (columns != NULL) - EndColumns(); - - if (columns_count != 1) - BeginColumns(id, columns_count, flags); -} - -//------------------------------------------------------------------------- - -#endif // #ifndef IMGUI_DISABLE diff --git a/libs/zgui/libs/imgui/imgui_widgets.cpp b/libs/zgui/libs/imgui/imgui_widgets.cpp deleted file mode 100644 index ef91630f2..000000000 --- a/libs/zgui/libs/imgui/imgui_widgets.cpp +++ /dev/null @@ -1,8394 +0,0 @@ -// dear imgui, v1.88 -// (widgets code) - -/* - -Index of this file: - -// [SECTION] Forward Declarations -// [SECTION] Widgets: Text, etc. -// [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.) -// [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.) -// [SECTION] Widgets: ComboBox -// [SECTION] Data Type and Data Formatting Helpers -// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. -// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. -// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. -// [SECTION] Widgets: InputText, InputTextMultiline -// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. -// [SECTION] Widgets: TreeNode, CollapsingHeader, etc. -// [SECTION] Widgets: Selectable -// [SECTION] Widgets: ListBox -// [SECTION] Widgets: PlotLines, PlotHistogram -// [SECTION] Widgets: Value helpers -// [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc. -// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. -// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. -// [SECTION] Widgets: Columns, BeginColumns, EndColumns, etc. - -*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "imgui.h" -#ifndef IMGUI_DISABLE - -#ifndef IMGUI_DEFINE_MATH_OPERATORS -#define IMGUI_DEFINE_MATH_OPERATORS -#endif -#include "imgui_internal.h" - -// System includes -#include // toupper -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else -#include // intptr_t -#endif - -//------------------------------------------------------------------------- -// Warnings -//------------------------------------------------------------------------- - -// Visual Studio warnings -#ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later -#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types -#endif -#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). -#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). -#endif - -// Clang/GCC warnings with -Weverything -#if defined(__clang__) -#if __has_warning("-Wunknown-warning-option") -#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! -#endif -#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' -#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. -#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. -#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. -#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 -#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. -#pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') -#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated -#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision -#elif defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind -#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked -#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead -#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated -#endif - -//------------------------------------------------------------------------- -// Data -//------------------------------------------------------------------------- - -// Widgets -static const float DRAGDROP_HOLD_TO_OPEN_TIMER = 0.70f; // Time for drag-hold to activate items accepting the ImGuiButtonFlags_PressedOnDragDropHold button behavior. -static const float DRAG_MOUSE_THRESHOLD_FACTOR = 0.50f; // Multiplier for the default value of io.MouseDragThreshold to make DragFloat/DragInt react faster to mouse drags. - -// Those MIN/MAX values are not define because we need to point to them -static const signed char IM_S8_MIN = -128; -static const signed char IM_S8_MAX = 127; -static const unsigned char IM_U8_MIN = 0; -static const unsigned char IM_U8_MAX = 0xFF; -static const signed short IM_S16_MIN = -32768; -static const signed short IM_S16_MAX = 32767; -static const unsigned short IM_U16_MIN = 0; -static const unsigned short IM_U16_MAX = 0xFFFF; -static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); -static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) -static const ImU32 IM_U32_MIN = 0; -static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) -#ifdef LLONG_MIN -static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); -static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); -#else -static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; -static const ImS64 IM_S64_MAX = 9223372036854775807LL; -#endif -static const ImU64 IM_U64_MIN = 0; -#ifdef ULLONG_MAX -static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); -#else -static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); -#endif - -//------------------------------------------------------------------------- -// [SECTION] Forward Declarations -//------------------------------------------------------------------------- - -// For InputTextEx() -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source); -static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); - -//------------------------------------------------------------------------- -// [SECTION] Widgets: Text, etc. -//------------------------------------------------------------------------- -// - TextEx() [Internal] -// - TextUnformatted() -// - Text() -// - TextV() -// - TextColored() -// - TextColoredV() -// - TextDisabled() -// - TextDisabledV() -// - TextWrapped() -// - TextWrappedV() -// - LabelText() -// - LabelTextV() -// - BulletText() -// - BulletTextV() -//------------------------------------------------------------------------- - -void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - // Accept null ranges - if (text == text_end) - text = text_end = ""; - - // Calculate length - const char* text_begin = text; - if (text_end == NULL) - text_end = text + strlen(text); // FIXME-OPT - - const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); - const float wrap_pos_x = window->DC.TextWrapPos; - const bool wrap_enabled = (wrap_pos_x >= 0.0f); - if (text_end - text <= 2000 || wrap_enabled) - { - // Common case - const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; - const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); - - ImRect bb(text_pos, text_pos + text_size); - ItemSize(text_size, 0.0f); - if (!ItemAdd(bb, 0)) - return; - - // Render (we don't hide text after ## in this end-user function) - RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); - } - else - { - // Long text! - // Perform manual coarse clipping to optimize for long multi-line text - // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. - // - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. - // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop. - const char* line = text; - const float line_height = GetTextLineHeight(); - ImVec2 text_size(0, 0); - - // Lines to skip (can't skip when logging text) - ImVec2 pos = text_pos; - if (!g.LogEnabled) - { - int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height); - if (lines_skippable > 0) - { - int lines_skipped = 0; - while (line < text_end && lines_skipped < lines_skippable) - { - const char* line_end = (const char*)memchr(line, '\n', text_end - line); - if (!line_end) - line_end = text_end; - if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) - text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - } - - // Lines to render - if (line < text_end) - { - ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); - while (line < text_end) - { - if (IsClippedEx(line_rect, 0)) - break; - - const char* line_end = (const char*)memchr(line, '\n', text_end - line); - if (!line_end) - line_end = text_end; - text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); - RenderText(pos, line, line_end, false); - line = line_end + 1; - line_rect.Min.y += line_height; - line_rect.Max.y += line_height; - pos.y += line_height; - } - - // Count remaining lines - int lines_skipped = 0; - while (line < text_end) - { - const char* line_end = (const char*)memchr(line, '\n', text_end - line); - if (!line_end) - line_end = text_end; - if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) - text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - text_size.y = (pos - text_pos).y; - - ImRect bb(text_pos, text_pos + text_size); - ItemSize(text_size, 0.0f); - ItemAdd(bb, 0); - } -} - -void ImGui::TextUnformatted(const char* text, const char* text_end) -{ - TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); -} - -void ImGui::Text(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextV(fmt, args); - va_end(args); -} - -void ImGui::TextV(const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - // FIXME-OPT: Handle the %s shortcut? - const char* text, *text_end; - ImFormatStringToTempBufferV(&text, &text_end, fmt, args); - TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); -} - -void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextColoredV(col, fmt, args); - va_end(args); -} - -void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) -{ - PushStyleColor(ImGuiCol_Text, col); - if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0) - TextEx(va_arg(args, const char*), NULL, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting - else - TextV(fmt, args); - PopStyleColor(); -} - -void ImGui::TextDisabled(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextDisabledV(fmt, args); - va_end(args); -} - -void ImGui::TextDisabledV(const char* fmt, va_list args) -{ - ImGuiContext& g = *GImGui; - PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0) - TextEx(va_arg(args, const char*), NULL, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting - else - TextV(fmt, args); - PopStyleColor(); -} - -void ImGui::TextWrapped(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextWrappedV(fmt, args); - va_end(args); -} - -void ImGui::TextWrappedV(const char* fmt, va_list args) -{ - ImGuiContext& g = *GImGui; - bool need_backup = (g.CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position if one is already set - if (need_backup) - PushTextWrapPos(0.0f); - if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0) - TextEx(va_arg(args, const char*), NULL, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting - else - TextV(fmt, args); - if (need_backup) - PopTextWrapPos(); -} - -void ImGui::LabelText(const char* label, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - LabelTextV(label, fmt, args); - va_end(args); -} - -// Add a label+text combo aligned to other label+value widgets -void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float w = CalcItemWidth(); - - const char* value_text_begin, *value_text_end; - ImFormatStringToTempBufferV(&value_text_begin, &value_text_end, fmt, args); - const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - const ImVec2 pos = window->DC.CursorPos; - const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2)); - const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, 0)) - return; - - // Render - RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f)); - if (label_size.x > 0.0f) - RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); -} - -void ImGui::BulletText(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - BulletTextV(fmt, args); - va_end(args); -} - -// Text with a little bullet aligned to the typical tree node. -void ImGui::BulletTextV(const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - const char* text_begin, *text_end; - ImFormatStringToTempBufferV(&text_begin, &text_end, fmt, args); - const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); - const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y); // Empty text doesn't add padding - ImVec2 pos = window->DC.CursorPos; - pos.y += window->DC.CurrLineTextBaseOffset; - ItemSize(total_size, 0.0f); - const ImRect bb(pos, pos + total_size); - if (!ItemAdd(bb, 0)) - return; - - // Render - ImU32 text_col = GetColorU32(ImGuiCol_Text); - RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, g.FontSize * 0.5f), text_col); - RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f), text_begin, text_end, false); -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: Main -//------------------------------------------------------------------------- -// - ButtonBehavior() [Internal] -// - Button() -// - SmallButton() -// - InvisibleButton() -// - ArrowButton() -// - CloseButton() [Internal] -// - CollapseButton() [Internal] -// - GetWindowScrollbarID() [Internal] -// - GetWindowScrollbarRect() [Internal] -// - Scrollbar() [Internal] -// - ScrollbarEx() [Internal] -// - Image() -// - ImageButton() -// - Checkbox() -// - CheckboxFlagsT() [Internal] -// - CheckboxFlags() -// - RadioButton() -// - ProgressBar() -// - Bullet() -//------------------------------------------------------------------------- - -// The ButtonBehavior() function is key to many interactions and used by many/most widgets. -// Because we handle so many cases (keyboard/gamepad navigation, drag and drop) and many specific behavior (via ImGuiButtonFlags_), -// this code is a little complex. -// By far the most common path is interacting with the Mouse using the default ImGuiButtonFlags_PressedOnClickRelease button behavior. -// See the series of events below and the corresponding state reported by dear imgui: -//------------------------------------------------------------------------------------------------------------------------------------------------ -// with PressedOnClickRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() -// Frame N+0 (mouse is outside bb) - - - - - - -// Frame N+1 (mouse moves inside bb) - true - - - - -// Frame N+2 (mouse button is down) - true true true - true -// Frame N+3 (mouse button is down) - true true - - - -// Frame N+4 (mouse moves outside bb) - - true - - - -// Frame N+5 (mouse moves inside bb) - true true - - - -// Frame N+6 (mouse button is released) true true - - true - -// Frame N+7 (mouse button is released) - true - - - - -// Frame N+8 (mouse moves outside bb) - - - - - - -//------------------------------------------------------------------------------------------------------------------------------------------------ -// with PressedOnClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() -// Frame N+2 (mouse button is down) true true true true - true -// Frame N+3 (mouse button is down) - true true - - - -// Frame N+6 (mouse button is released) - true - - true - -// Frame N+7 (mouse button is released) - true - - - - -//------------------------------------------------------------------------------------------------------------------------------------------------ -// with PressedOnRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() -// Frame N+2 (mouse button is down) - true - - - true -// Frame N+3 (mouse button is down) - true - - - - -// Frame N+6 (mouse button is released) true true - - - - -// Frame N+7 (mouse button is released) - true - - - - -//------------------------------------------------------------------------------------------------------------------------------------------------ -// with PressedOnDoubleClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() -// Frame N+0 (mouse button is down) - true - - - true -// Frame N+1 (mouse button is down) - true - - - - -// Frame N+2 (mouse button is released) - true - - - - -// Frame N+3 (mouse button is released) - true - - - - -// Frame N+4 (mouse button is down) true true true true - true -// Frame N+5 (mouse button is down) - true true - - - -// Frame N+6 (mouse button is released) - true - - true - -// Frame N+7 (mouse button is released) - true - - - - -//------------------------------------------------------------------------------------------------------------------------------------------------ -// Note that some combinations are supported, -// - PressedOnDragDropHold can generally be associated with any flag. -// - PressedOnDoubleClick can be associated by PressedOnClickRelease/PressedOnRelease, in which case the second release event won't be reported. -//------------------------------------------------------------------------------------------------------------------------------------------------ -// The behavior of the return-value changes when ImGuiButtonFlags_Repeat is set: -// Repeat+ Repeat+ Repeat+ Repeat+ -// PressedOnClickRelease PressedOnClick PressedOnRelease PressedOnDoubleClick -//------------------------------------------------------------------------------------------------------------------------------------------------- -// Frame N+0 (mouse button is down) - true - true -// ... - - - - -// Frame N + RepeatDelay true true - true -// ... - - - - -// Frame N + RepeatDelay + RepeatRate*N true true - true -//------------------------------------------------------------------------------------------------------------------------------------------------- - -bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - // Default only reacts to left mouse button - if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0) - flags |= ImGuiButtonFlags_MouseButtonDefault_; - - // Default behavior requires click + release inside bounding box - if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0) - flags |= ImGuiButtonFlags_PressedOnDefault_; - - ImGuiWindow* backup_hovered_window = g.HoveredWindow; - const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window; - if (flatten_hovered_children) - g.HoveredWindow = window; - -#ifdef IMGUI_ENABLE_TEST_ENGINE - if (id != 0 && g.LastItemData.ID != id) - IMGUI_TEST_ENGINE_ITEM_ADD(bb, id); -#endif - - bool pressed = false; - bool hovered = ItemHoverable(bb, id); - - // Drag source doesn't report as hovered - if (hovered && g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover)) - hovered = false; - - // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button - if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) - if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - { - hovered = true; - SetHoveredID(id); - if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER) - { - pressed = true; - g.DragDropHoldJustPressedId = id; - FocusWindow(window); - } - } - - if (flatten_hovered_children) - g.HoveredWindow = backup_hovered_window; - - // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. - if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) - hovered = false; - - // Mouse handling - if (hovered) - { - if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) - { - // Poll buttons - int mouse_button_clicked = -1; - if ((flags & ImGuiButtonFlags_MouseButtonLeft) && g.IO.MouseClicked[0]) { mouse_button_clicked = 0; } - else if ((flags & ImGuiButtonFlags_MouseButtonRight) && g.IO.MouseClicked[1]) { mouse_button_clicked = 1; } - else if ((flags & ImGuiButtonFlags_MouseButtonMiddle) && g.IO.MouseClicked[2]) { mouse_button_clicked = 2; } - - if (mouse_button_clicked != -1 && g.ActiveId != id) - { - if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere)) - { - SetActiveID(id, window); - g.ActiveIdMouseButton = mouse_button_clicked; - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); - FocusWindow(window); - } - if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2)) - { - pressed = true; - if (flags & ImGuiButtonFlags_NoHoldingActiveId) - ClearActiveID(); - else - SetActiveID(id, window); // Hold on ID - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); - g.ActiveIdMouseButton = mouse_button_clicked; - FocusWindow(window); - } - } - if (flags & ImGuiButtonFlags_PressedOnRelease) - { - int mouse_button_released = -1; - if ((flags & ImGuiButtonFlags_MouseButtonLeft) && g.IO.MouseReleased[0]) { mouse_button_released = 0; } - else if ((flags & ImGuiButtonFlags_MouseButtonRight) && g.IO.MouseReleased[1]) { mouse_button_released = 1; } - else if ((flags & ImGuiButtonFlags_MouseButtonMiddle) && g.IO.MouseReleased[2]) { mouse_button_released = 2; } - if (mouse_button_released != -1) - { - const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior - if (!has_repeated_at_least_once) - pressed = true; - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); - ClearActiveID(); - } - } - - // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). - // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. - if (g.ActiveId == id && (flags & ImGuiButtonFlags_Repeat)) - if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, true)) - pressed = true; - } - - if (pressed) - g.NavDisableHighlight = true; - } - - // Gamepad/Keyboard navigation - // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. - if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) - if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus)) - hovered = true; - if (g.NavActivateDownId == id) - { - bool nav_activated_by_code = (g.NavActivateId == id); - bool nav_activated_by_inputs = IsNavInputTest(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed); - if (nav_activated_by_code || nav_activated_by_inputs) - { - // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. - pressed = true; - SetActiveID(id, window); - g.ActiveIdSource = ImGuiInputSource_Nav; - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); - } - } - - // Process while held - bool held = false; - if (g.ActiveId == id) - { - if (g.ActiveIdSource == ImGuiInputSource_Mouse) - { - if (g.ActiveIdIsJustActivated) - g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; - - const int mouse_button = g.ActiveIdMouseButton; - IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); - if (g.IO.MouseDown[mouse_button]) - { - held = true; - } - else - { - bool release_in = hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease) != 0; - bool release_anywhere = (flags & ImGuiButtonFlags_PressedOnClickReleaseAnywhere) != 0; - if ((release_in || release_anywhere) && !g.DragDropActive) - { - // Report as pressed when releasing the mouse (this is the most common path) - bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; - bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps - if (!is_double_click_release && !is_repeating_already) - pressed = true; - } - ClearActiveID(); - } - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - g.NavDisableHighlight = true; - } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - // When activated using Nav, we hold on the ActiveID until activation button is released - if (g.NavActivateDownId != id) - ClearActiveID(); - } - if (pressed) - g.ActiveIdHasBeenPressedBefore = true; - } - - if (out_hovered) *out_hovered = hovered; - if (out_held) *out_held = held; - - return pressed; -} - -bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - ImVec2 pos = window->DC.CursorPos; - if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) - pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y; - ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); - - const ImRect bb(pos, pos + size); - ItemSize(size, style.FramePadding.y); - if (!ItemAdd(bb, id)) - return false; - - if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) - flags |= ImGuiButtonFlags_Repeat; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); - - // Render - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); - - if (g.LogEnabled) - LogSetNextTextDecoration("[", "]"); - RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); - - // Automatically close popups - //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) - // CloseCurrentPopup(); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); - return pressed; -} - -bool ImGui::Button(const char* label, const ImVec2& size_arg) -{ - return ButtonEx(label, size_arg, ImGuiButtonFlags_None); -} - -// Small buttons fits within text without additional vertical spacing. -bool ImGui::SmallButton(const char* label) -{ - ImGuiContext& g = *GImGui; - float backup_padding_y = g.Style.FramePadding.y; - g.Style.FramePadding.y = 0.0f; - bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine); - g.Style.FramePadding.y = backup_padding_y; - return pressed; -} - -// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. -// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) -bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size. - IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f); - - const ImGuiID id = window->GetID(str_id); - ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(size); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); - return pressed; -} - -bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - const ImGuiID id = window->GetID(str_id); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - const float default_size = GetFrameHeight(); - ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f); - if (!ItemAdd(bb, id)) - return false; - - if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) - flags |= ImGuiButtonFlags_Repeat; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); - - // Render - const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - const ImU32 text_col = GetColorU32(ImGuiCol_Text); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding); - RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); - return pressed; -} - -bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) -{ - float sz = GetFrameHeight(); - return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), ImGuiButtonFlags_None); -} - -// Button to close a window -bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825) - // This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible? - const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f); - ImRect bb_interact = bb; - const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea(); - if (area_to_visible_ratio < 1.5f) - bb_interact.Expand(ImFloor(bb_interact.GetSize() * -0.25f)); - - // Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window. - // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). - bool is_clipped = !ItemAdd(bb_interact, id); - - bool hovered, held; - bool pressed = ButtonBehavior(bb_interact, id, &hovered, &held); - if (is_clipped) - return pressed; - - // Render - // FIXME: Clarify this mess - ImU32 col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); - ImVec2 center = bb.GetCenter(); - if (hovered) - window->DrawList->AddCircleFilled(center, ImMax(2.0f, g.FontSize * 0.5f + 1.0f), col, 12); - - float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; - ImU32 cross_col = GetColorU32(ImGuiCol_Text); - center -= ImVec2(0.5f, 0.5f); - window->DrawList->AddLine(center + ImVec2(+cross_extent, +cross_extent), center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f); - window->DrawList->AddLine(center + ImVec2(+cross_extent, -cross_extent), center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f); - - return pressed; -} - -bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f); - ItemAdd(bb, id); - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); - - // Render - ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - ImU32 text_col = GetColorU32(ImGuiCol_Text); - if (hovered || held) - window->DrawList->AddCircleFilled(bb.GetCenter()/*+ ImVec2(0.0f, -0.5f)*/, g.FontSize * 0.5f + 1.0f, bg_col, 12); - RenderArrow(window->DrawList, bb.Min + g.Style.FramePadding, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); - - // Switch to moving the window after mouse is moved beyond the initial drag threshold - if (IsItemActive() && IsMouseDragging(0)) - StartMouseMovingWindow(window); - - return pressed; -} - -ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis) -{ - return window->GetID(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY"); -} - -// Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set. -ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis) -{ - const ImRect outer_rect = window->Rect(); - const ImRect inner_rect = window->InnerRect; - const float border_size = window->WindowBorderSize; - const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar) - IM_ASSERT(scrollbar_size > 0.0f); - if (axis == ImGuiAxis_X) - return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x, outer_rect.Max.y); - else - return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x, inner_rect.Max.y); -} - -void ImGui::Scrollbar(ImGuiAxis axis) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const ImGuiID id = GetWindowScrollbarID(window, axis); - - // Calculate scrollbar bounding box - ImRect bb = GetWindowScrollbarRect(window, axis); - ImDrawFlags rounding_corners = ImDrawFlags_RoundCornersNone; - if (axis == ImGuiAxis_X) - { - rounding_corners |= ImDrawFlags_RoundCornersBottomLeft; - if (!window->ScrollbarY) - rounding_corners |= ImDrawFlags_RoundCornersBottomRight; - } - else - { - if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) - rounding_corners |= ImDrawFlags_RoundCornersTopRight; - if (!window->ScrollbarX) - rounding_corners |= ImDrawFlags_RoundCornersBottomRight; - } - float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; - float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; - ImS64 scroll = (ImS64)window->Scroll[axis]; - ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_avail, (ImS64)size_contents, rounding_corners); - window->Scroll[axis] = (float)scroll; -} - -// Vertical/Horizontal scrollbar -// The entire piece of code below is rather confusing because: -// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) -// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar -// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. -// Still, the code should probably be made simpler.. -bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_avail_v, ImS64 size_contents_v, ImDrawFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return false; - - KeepAliveID(id); - - const float bb_frame_width = bb_frame.GetWidth(); - const float bb_frame_height = bb_frame.GetHeight(); - if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f) - return false; - - // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab) - float alpha = 1.0f; - if ((axis == ImGuiAxis_Y) && bb_frame_height < g.FontSize + g.Style.FramePadding.y * 2.0f) - alpha = ImSaturate((bb_frame_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f)); - if (alpha <= 0.0f) - return false; - - const ImGuiStyle& style = g.Style; - const bool allow_interaction = (alpha >= 1.0f); - - ImRect bb = bb_frame; - bb.Expand(ImVec2(-ImClamp(IM_FLOOR((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_FLOOR((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f))); - - // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) - const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight(); - - // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) - // But we maintain a minimum size in pixel to allow for the user to still aim inside. - IM_ASSERT(ImMax(size_contents_v, size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. - const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), (ImS64)1); - const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_avail_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v); - const float grab_h_norm = grab_h_pixels / scrollbar_size_v; - - // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). - bool held = false; - bool hovered = false; - ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); - - const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_avail_v); - float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); - float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space - if (held && allow_interaction && grab_h_norm < 1.0f) - { - const float scrollbar_pos_v = bb.Min[axis]; - const float mouse_pos_v = g.IO.MousePos[axis]; - - // Click position in scrollbar normalized space (0.0f->1.0f) - const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); - SetHoveredID(id); - - bool seek_absolute = false; - if (g.ActiveIdIsJustActivated) - { - // On initial click calculate the distance between mouse and the center of the grab - seek_absolute = (clicked_v_norm < grab_v_norm || clicked_v_norm > grab_v_norm + grab_h_norm); - if (seek_absolute) - g.ScrollbarClickDeltaToGrabCenter = 0.0f; - else - g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; - } - - // Apply scroll (p_scroll_v will generally point on one member of window->Scroll) - // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position - const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); - *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max); - - // Update values for rendering - scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); - grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; - - // Update distance to grab now that we have seeked and saturated - if (seek_absolute) - g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; - } - - // Render - const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg); - const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha); - window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, flags); - ImRect grab_rect; - if (axis == ImGuiAxis_X) - grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y); - else - grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels); - window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); - - return held; -} - -void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - if (border_col.w > 0.0f) - bb.Max += ImVec2(2, 2); - ItemSize(bb); - if (!ItemAdd(bb, 0)) - return; - - if (border_col.w > 0.0f) - { - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); - window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col)); - } - else - { - window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col)); - } -} - -// ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390) -// We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API. -bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2); - ItemSize(bb); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - // Render - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding)); - if (bg_col.w > 0.0f) - window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col)); - window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); - - return pressed; -} - -// frame_padding < 0: uses FramePadding from style (default) -// frame_padding = 0: no framing -// frame_padding > 0: set framing size -bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return false; - - // Default to using texture ID as ID. User can still push string/integer prefixes. - PushID((void*)(intptr_t)user_texture_id); - const ImGuiID id = window->GetID("#image"); - PopID(); - - const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : g.Style.FramePadding; - return ImageButtonEx(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col); -} - -bool ImGui::Checkbox(const char* label, bool* v) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - const float square_sz = GetFrameHeight(); - const ImVec2 pos = window->DC.CursorPos; - const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id)) - { - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); - return false; - } - - bool hovered, held; - bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); - if (pressed) - { - *v = !(*v); - MarkItemEdited(id); - } - - const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); - RenderNavHighlight(total_bb, id); - RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); - ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); - bool mixed_value = (g.LastItemData.InFlags & ImGuiItemFlags_MixedValue) != 0; - if (mixed_value) - { - // Undocumented tristate/mixed/indeterminate checkbox (#2644) - // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox) - ImVec2 pad(ImMax(1.0f, IM_FLOOR(square_sz / 3.6f)), ImMax(1.0f, IM_FLOOR(square_sz / 3.6f))); - window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); - } - else if (*v) - { - const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f)); - RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); - } - - ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); - if (g.LogEnabled) - LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]"); - if (label_size.x > 0.0f) - RenderText(label_pos, label); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); - return pressed; -} - -template -bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) -{ - bool all_on = (*flags & flags_value) == flags_value; - bool any_on = (*flags & flags_value) != 0; - bool pressed; - if (!all_on && any_on) - { - ImGuiContext& g = *GImGui; - ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_MixedValue; - pressed = Checkbox(label, &all_on); - g.CurrentItemFlags = backup_item_flags; - } - else - { - pressed = Checkbox(label, &all_on); - - } - if (pressed) - { - if (all_on) - *flags |= flags_value; - else - *flags &= ~flags_value; - } - return pressed; -} - -bool ImGui::CheckboxFlags(const char* label, int* flags, int flags_value) -{ - return CheckboxFlagsT(label, flags, flags_value); -} - -bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) -{ - return CheckboxFlagsT(label, flags, flags_value); -} - -bool ImGui::CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value) -{ - return CheckboxFlagsT(label, flags, flags_value); -} - -bool ImGui::CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value) -{ - return CheckboxFlagsT(label, flags, flags_value); -} - -bool ImGui::RadioButton(const char* label, bool active) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - const float square_sz = GetFrameHeight(); - const ImVec2 pos = window->DC.CursorPos; - const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); - const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id)) - return false; - - ImVec2 center = check_bb.GetCenter(); - center.x = IM_ROUND(center.x); - center.y = IM_ROUND(center.y); - const float radius = (square_sz - 1.0f) * 0.5f; - - bool hovered, held; - bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); - if (pressed) - MarkItemEdited(id); - - RenderNavHighlight(total_bb, id); - window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); - if (active) - { - const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f)); - window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark), 16); - } - - if (style.FrameBorderSize > 0.0f) - { - window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); - window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); - } - - ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); - if (g.LogEnabled) - LogRenderedText(&label_pos, active ? "(x)" : "( )"); - if (label_size.x > 0.0f) - RenderText(label_pos, label); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); - return pressed; -} - -// FIXME: This would work nicely if it was a public template, e.g. 'template RadioButton(const char* label, T* v, T v_button)', but I'm not sure how we would expose it.. -bool ImGui::RadioButton(const char* label, int* v, int v_button) -{ - const bool pressed = RadioButton(label, *v == v_button); - if (pressed) - *v = v_button; - return pressed; -} - -// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size -void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - ImVec2 pos = window->DC.CursorPos; - ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0f); - ImRect bb(pos, pos + size); - ItemSize(size, style.FramePadding.y); - if (!ItemAdd(bb, 0)) - return; - - // Render - fraction = ImSaturate(fraction); - RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); - const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); - RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding); - - // Default displaying the fraction as percentage string, but user can override it - char overlay_buf[32]; - if (!overlay) - { - ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); - overlay = overlay_buf; - } - - ImVec2 overlay_size = CalcTextSize(overlay, NULL); - if (overlay_size.x > 0.0f) - RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb); -} - -void ImGui::Bullet() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), g.FontSize); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); - ItemSize(bb); - if (!ItemAdd(bb, 0)) - { - SameLine(0, style.FramePadding.x * 2); - return; - } - - // Render and stay on same line - ImU32 text_col = GetColorU32(ImGuiCol_Text); - RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, line_height * 0.5f), text_col); - SameLine(0, style.FramePadding.x * 2.0f); -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: Low-level Layout helpers -//------------------------------------------------------------------------- -// - Spacing() -// - Dummy() -// - NewLine() -// - AlignTextToFramePadding() -// - SeparatorEx() [Internal] -// - Separator() -// - SplitterBehavior() [Internal] -// - ShrinkWidths() [Internal] -//------------------------------------------------------------------------- - -void ImGui::Spacing() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ItemSize(ImVec2(0, 0)); -} - -void ImGui::Dummy(const ImVec2& size) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(size); - ItemAdd(bb, 0); -} - -void ImGui::NewLine() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiLayoutType backup_layout_type = window->DC.LayoutType; - window->DC.LayoutType = ImGuiLayoutType_Vertical; - window->DC.IsSameLine = false; - if (window->DC.CurrLineSize.y > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. - ItemSize(ImVec2(0, 0)); - else - ItemSize(ImVec2(0.0f, g.FontSize)); - window->DC.LayoutType = backup_layout_type; -} - -void ImGui::AlignTextToFramePadding() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - window->DC.CurrLineSize.y = ImMax(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2); - window->DC.CurrLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, g.Style.FramePadding.y); -} - -// Horizontal/vertical separating line -void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected - - float thickness_draw = 1.0f; - float thickness_layout = 0.0f; - if (flags & ImGuiSeparatorFlags_Vertical) - { - // Vertical separator, for menu bars (use current line height). Not exposed because it is misleading and it doesn't have an effect on regular layout. - float y1 = window->DC.CursorPos.y; - float y2 = window->DC.CursorPos.y + window->DC.CurrLineSize.y; - const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness_draw, y2)); - ItemSize(ImVec2(thickness_layout, 0.0f)); - if (!ItemAdd(bb, 0)) - return; - - // Draw - window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator)); - if (g.LogEnabled) - LogText(" |"); - } - else if (flags & ImGuiSeparatorFlags_Horizontal) - { - // Horizontal Separator - float x1 = window->Pos.x; - float x2 = window->Pos.x + window->Size.x; - - // FIXME-WORKRECT: old hack (#205) until we decide of consistent behavior with WorkRect/Indent and Separator - if (g.GroupStack.Size > 0 && g.GroupStack.back().WindowID == window->ID) - x1 += window->DC.Indent.x; - - // FIXME-WORKRECT: In theory we should simply be using WorkRect.Min.x/Max.x everywhere but it isn't aesthetically what we want, - // need to introduce a variant of WorkRect for that purpose. (#4787) - if (ImGuiTable* table = g.CurrentTable) - { - x1 = table->Columns[table->CurrentColumn].MinX; - x2 = table->Columns[table->CurrentColumn].MaxX; - } - - ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; - if (columns) - PushColumnsBackground(); - - // We don't provide our width to the layout so that it doesn't get feed back into AutoFit - // FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell) - const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness_draw)); - ItemSize(ImVec2(0.0f, thickness_layout)); - const bool item_visible = ItemAdd(bb, 0); - if (item_visible) - { - // Draw - window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x, bb.Min.y), GetColorU32(ImGuiCol_Separator)); - if (g.LogEnabled) - LogRenderedText(&bb.Min, "--------------------------------\n"); - - } - if (columns) - { - PopColumnsBackground(); - columns->LineMinY = window->DC.CursorPos.y; - } - } -} - -void ImGui::Separator() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return; - - // Those flags should eventually be overridable by the user - ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - flags |= ImGuiSeparatorFlags_SpanAllColumns; // NB: this only applies to legacy Columns() api as they relied on Separator() a lot. - SeparatorEx(flags); -} - -// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. -bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; - bool item_add = ItemAdd(bb, id); - g.CurrentItemFlags = item_flags_backup; - if (!item_add) - return false; - - bool hovered, held; - ImRect bb_interact = bb; - bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); - ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); - if (hovered) - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb - if (g.ActiveId != id) - SetItemAllowOverlap(); - - if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) - SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); - - ImRect bb_render = bb; - if (held) - { - ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min; - float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x; - - // Minimum pane size - float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1); - float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2); - if (mouse_delta < -size_1_maximum_delta) - mouse_delta = -size_1_maximum_delta; - if (mouse_delta > size_2_maximum_delta) - mouse_delta = size_2_maximum_delta; - - // Apply resize - if (mouse_delta != 0.0f) - { - if (mouse_delta < 0.0f) - IM_ASSERT(*size1 + mouse_delta >= min_size1); - if (mouse_delta > 0.0f) - IM_ASSERT(*size2 - mouse_delta >= min_size2); - *size1 += mouse_delta; - *size2 -= mouse_delta; - bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); - MarkItemEdited(id); - } - } - - // Render - const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); - window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f); - - return held; -} - -static int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs) -{ - const ImGuiShrinkWidthItem* a = (const ImGuiShrinkWidthItem*)lhs; - const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs; - if (int d = (int)(b->Width - a->Width)) - return d; - return (b->Index - a->Index); -} - -// Shrink excess width from a set of item, by removing width from the larger items first. -// Set items Width to -1.0f to disable shrinking this item. -void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess) -{ - if (count == 1) - { - if (items[0].Width >= 0.0f) - items[0].Width = ImMax(items[0].Width - width_excess, 1.0f); - return; - } - ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer); - int count_same_width = 1; - while (width_excess > 0.0f && count_same_width < count) - { - while (count_same_width < count && items[0].Width <= items[count_same_width].Width) - count_same_width++; - float max_width_to_remove_per_item = (count_same_width < count && items[count_same_width].Width >= 0.0f) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f); - if (max_width_to_remove_per_item <= 0.0f) - break; - float width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item); - for (int item_n = 0; item_n < count_same_width; item_n++) - items[item_n].Width -= width_to_remove_per_item; - width_excess -= width_to_remove_per_item * count_same_width; - } - - // Round width and redistribute remainder - // Ensure that e.g. the right-most tab of a shrunk tab-bar always reaches exactly at the same distance from the right-most edge of the tab bar separator. - width_excess = 0.0f; - for (int n = 0; n < count; n++) - { - float width_rounded = ImFloor(items[n].Width); - width_excess += items[n].Width - width_rounded; - items[n].Width = width_rounded; - } - while (width_excess > 0.0f) - for (int n = 0; n < count; n++) - if (items[n].Width + 1.0f <= items[n].InitialWidth) - { - items[n].Width += 1.0f; - width_excess -= 1.0f; - } -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: ComboBox -//------------------------------------------------------------------------- -// - CalcMaxPopupHeightFromItemCount() [Internal] -// - BeginCombo() -// - BeginComboPopup() [Internal] -// - EndCombo() -// - BeginComboPreview() [Internal] -// - EndComboPreview() [Internal] -// - Combo() -//------------------------------------------------------------------------- - -static float CalcMaxPopupHeightFromItemCount(int items_count) -{ - ImGuiContext& g = *GImGui; - if (items_count <= 0) - return FLT_MAX; - return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); -} - -bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.Flags; - g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values - if (window->SkipItems) - return false; - - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together - - const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); - const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &bb)) - return false; - - // Open on click - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id); - bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None); - if (pressed && !popup_open) - { - OpenPopupEx(popup_id, ImGuiPopupFlags_None); - popup_open = true; - } - - // Render shape - const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size); - RenderNavHighlight(bb, id); - if (!(flags & ImGuiComboFlags_NoPreview)) - window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft); - if (!(flags & ImGuiComboFlags_NoArrowButton)) - { - ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - ImU32 text_col = GetColorU32(ImGuiCol_Text); - window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight); - if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x) - RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); - } - RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding); - - // Custom preview - if (flags & ImGuiComboFlags_CustomPreview) - { - g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y); - IM_ASSERT(preview_value == NULL || preview_value[0] == 0); - preview_value = NULL; - } - - // Render preview and label - if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) - { - if (g.LogEnabled) - LogSetNextTextDecoration("{", "}"); - RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL); - } - if (label_size.x > 0) - RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label); - - if (!popup_open) - return false; - - g.NextWindowData.Flags = backup_next_window_data_flags; - return BeginComboPopup(popup_id, bb, flags); -} - -bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags) -{ - ImGuiContext& g = *GImGui; - if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None)) - { - g.NextWindowData.ClearFlags(); - return false; - } - - // Set popup size - float w = bb.GetWidth(); - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) - { - g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); - } - else - { - if ((flags & ImGuiComboFlags_HeightMask_) == 0) - flags |= ImGuiComboFlags_HeightRegular; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one - int popup_max_height_in_items = -1; - if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; - else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; - else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; - SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); - } - - // This is essentially a specialized version of BeginPopupEx() - char name[16]; - ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth - - // Set position given a custom constraint (peak into expected window size so we can position it) - // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function? - // FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()? - if (ImGuiWindow* popup_window = FindWindowByName(name)) - if (popup_window->WasActive) - { - // Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us. - ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window); - popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = "Below, Toward Left", Down = "Below, Toward Right (default)" - ImRect r_outer = GetPopupAllowedExtentRect(popup_window); - ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox); - SetNextWindowPos(pos); - } - - // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx() - ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove; - PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(g.Style.FramePadding.x, g.Style.WindowPadding.y)); // Horizontally align ourselves with the framed text - bool ret = Begin(name, NULL, window_flags); - PopStyleVar(); - if (!ret) - { - EndPopup(); - IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above - return false; - } - return true; -} - -void ImGui::EndCombo() -{ - EndPopup(); -} - -// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements -// (Experimental, see GitHub issues: #1658, #4168) -bool ImGui::BeginComboPreview() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; - - if (window->SkipItems || !window->ClipRect.Overlaps(g.LastItemData.Rect)) // FIXME: Because we don't have a ImGuiItemStatusFlags_Visible flag to test last ItemAdd() result - return false; - IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? - if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional) - return false; - - // FIXME: This could be contained in a PushWorkRect() api - preview_data->BackupCursorPos = window->DC.CursorPos; - preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos; - preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine; - preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; - preview_data->BackupLayout = window->DC.LayoutType; - window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding; - window->DC.CursorMaxPos = window->DC.CursorPos; - window->DC.LayoutType = ImGuiLayoutType_Horizontal; - window->DC.IsSameLine = false; - PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true); - - return true; -} - -void ImGui::EndComboPreview() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; - - // FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future - ImDrawList* draw_list = window->DrawList; - if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y) - if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command - { - draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect; - draw_list->_TryMergeDrawCmds(); - } - PopClipRect(); - window->DC.CursorPos = preview_data->BackupCursorPos; - window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos); - window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine; - window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset; - window->DC.LayoutType = preview_data->BackupLayout; - window->DC.IsSameLine = false; - preview_data->PreviewRect = ImRect(); -} - -// Getter for the old Combo() API: const char*[] -static bool Items_ArrayGetter(void* data, int idx, const char** out_text) -{ - const char* const* items = (const char* const*)data; - if (out_text) - *out_text = items[idx]; - return true; -} - -// Getter for the old Combo() API: "item1\0item2\0item3\0" -static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) -{ - // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. - const char* items_separated_by_zeros = (const char*)data; - int items_count = 0; - const char* p = items_separated_by_zeros; - while (*p) - { - if (idx == items_count) - break; - p += strlen(p) + 1; - items_count++; - } - if (!*p) - return false; - if (out_text) - *out_text = p; - return true; -} - -// Old API, prefer using BeginCombo() nowadays if you can. -bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items) -{ - ImGuiContext& g = *GImGui; - - // Call the getter to obtain the preview string which is a parameter to BeginCombo() - const char* preview_value = NULL; - if (*current_item >= 0 && *current_item < items_count) - items_getter(data, *current_item, &preview_value); - - // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here. - if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)) - SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); - - if (!BeginCombo(label, preview_value, ImGuiComboFlags_None)) - return false; - - // Display items - // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed) - bool value_changed = false; - for (int i = 0; i < items_count; i++) - { - PushID(i); - const bool item_selected = (i == *current_item); - const char* item_text; - if (!items_getter(data, i, &item_text)) - item_text = "*Unknown item*"; - if (Selectable(item_text, item_selected)) - { - value_changed = true; - *current_item = i; - } - if (item_selected) - SetItemDefaultFocus(); - PopID(); - } - - EndCombo(); - - if (value_changed) - MarkItemEdited(g.LastItemData.ID); - - return value_changed; -} - -// Combo box helper allowing to pass an array of strings. -bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items) -{ - const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); - return value_changed; -} - -// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0" -bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) -{ - int items_count = 0; - const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open - while (*p) - { - p += strlen(p) + 1; - items_count++; - } - bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); - return value_changed; -} - -//------------------------------------------------------------------------- -// [SECTION] Data Type and Data Formatting Helpers [Internal] -//------------------------------------------------------------------------- -// - PatchFormatStringFloatToInt() -// - DataTypeGetInfo() -// - DataTypeFormatString() -// - DataTypeApplyOp() -// - DataTypeApplyOpFromText() -// - DataTypeClamp() -// - GetMinimumStepAtDecimalPrecision -// - RoundScalarWithFormat<>() -//------------------------------------------------------------------------- - -static const ImGuiDataTypeInfo GDataTypeInfo[] = -{ - { sizeof(char), "S8", "%d", "%d" }, // ImGuiDataType_S8 - { sizeof(unsigned char), "U8", "%u", "%u" }, - { sizeof(short), "S16", "%d", "%d" }, // ImGuiDataType_S16 - { sizeof(unsigned short), "U16", "%u", "%u" }, - { sizeof(int), "S32", "%d", "%d" }, // ImGuiDataType_S32 - { sizeof(unsigned int), "U32", "%u", "%u" }, -#ifdef _MSC_VER - { sizeof(ImS64), "S64", "%I64d","%I64d" }, // ImGuiDataType_S64 - { sizeof(ImU64), "U64", "%I64u","%I64u" }, -#else - { sizeof(ImS64), "S64", "%lld", "%lld" }, // ImGuiDataType_S64 - { sizeof(ImU64), "U64", "%llu", "%llu" }, -#endif - { sizeof(float), "float", "%.3f","%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) - { sizeof(double), "double","%f", "%lf" }, // ImGuiDataType_Double -}; -IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); - -// FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f". -// Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls. -// To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?! -static const char* PatchFormatStringFloatToInt(const char* fmt) -{ - if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '0' && fmt[3] == 'f' && fmt[4] == 0) // Fast legacy path for "%.0f" which is expected to be the most common case. - return "%d"; - const char* fmt_start = ImParseFormatFindStart(fmt); // Find % (if any, and ignore %%) - const char* fmt_end = ImParseFormatFindEnd(fmt_start); // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user). - if (fmt_end > fmt_start && fmt_end[-1] == 'f') - { -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (fmt_start == fmt && fmt_end[0] == 0) - return "%d"; - const char* tmp_format; - ImFormatStringToTempBuffer(&tmp_format, NULL, "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision. - return tmp_format; -#else - IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d" -#endif - } - return fmt; -} - -const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type) -{ - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); - return &GDataTypeInfo[data_type]; -} - -int ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format) -{ - // Signedness doesn't matter when pushing integer arguments - if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) - return ImFormatString(buf, buf_size, format, *(const ImU32*)p_data); - if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) - return ImFormatString(buf, buf_size, format, *(const ImU64*)p_data); - if (data_type == ImGuiDataType_Float) - return ImFormatString(buf, buf_size, format, *(const float*)p_data); - if (data_type == ImGuiDataType_Double) - return ImFormatString(buf, buf_size, format, *(const double*)p_data); - if (data_type == ImGuiDataType_S8) - return ImFormatString(buf, buf_size, format, *(const ImS8*)p_data); - if (data_type == ImGuiDataType_U8) - return ImFormatString(buf, buf_size, format, *(const ImU8*)p_data); - if (data_type == ImGuiDataType_S16) - return ImFormatString(buf, buf_size, format, *(const ImS16*)p_data); - if (data_type == ImGuiDataType_U16) - return ImFormatString(buf, buf_size, format, *(const ImU16*)p_data); - IM_ASSERT(0); - return 0; -} - -void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg1, const void* arg2) -{ - IM_ASSERT(op == '+' || op == '-'); - switch (data_type) - { - case ImGuiDataType_S8: - if (op == '+') { *(ImS8*)output = ImAddClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } - if (op == '-') { *(ImS8*)output = ImSubClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } - return; - case ImGuiDataType_U8: - if (op == '+') { *(ImU8*)output = ImAddClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } - if (op == '-') { *(ImU8*)output = ImSubClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } - return; - case ImGuiDataType_S16: - if (op == '+') { *(ImS16*)output = ImAddClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } - if (op == '-') { *(ImS16*)output = ImSubClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } - return; - case ImGuiDataType_U16: - if (op == '+') { *(ImU16*)output = ImAddClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } - if (op == '-') { *(ImU16*)output = ImSubClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } - return; - case ImGuiDataType_S32: - if (op == '+') { *(ImS32*)output = ImAddClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } - if (op == '-') { *(ImS32*)output = ImSubClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } - return; - case ImGuiDataType_U32: - if (op == '+') { *(ImU32*)output = ImAddClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } - if (op == '-') { *(ImU32*)output = ImSubClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } - return; - case ImGuiDataType_S64: - if (op == '+') { *(ImS64*)output = ImAddClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } - if (op == '-') { *(ImS64*)output = ImSubClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } - return; - case ImGuiDataType_U64: - if (op == '+') { *(ImU64*)output = ImAddClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } - if (op == '-') { *(ImU64*)output = ImSubClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } - return; - case ImGuiDataType_Float: - if (op == '+') { *(float*)output = *(const float*)arg1 + *(const float*)arg2; } - if (op == '-') { *(float*)output = *(const float*)arg1 - *(const float*)arg2; } - return; - case ImGuiDataType_Double: - if (op == '+') { *(double*)output = *(const double*)arg1 + *(const double*)arg2; } - if (op == '-') { *(double*)output = *(const double*)arg1 - *(const double*)arg2; } - return; - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); -} - -// User can input math operators (e.g. +100) to edit a numerical values. -// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. -bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format) -{ - while (ImCharIsBlankA(*buf)) - buf++; - if (!buf[0]) - return false; - - // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. - const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); - ImGuiDataTypeTempStorage data_backup; - memcpy(&data_backup, p_data, type_info->Size); - - // Sanitize format - // For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf - char format_sanitized[32]; - if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) - format = type_info->ScanFmt; - else - format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_ARRAYSIZE(format_sanitized)); - - // Small types need a 32-bit buffer to receive the result from scanf() - int v32 = 0; - if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1) - return false; - if (type_info->Size < 4) - { - if (data_type == ImGuiDataType_S8) - *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); - else if (data_type == ImGuiDataType_U8) - *(ImU8*)p_data = (ImU8)ImClamp(v32, (int)IM_U8_MIN, (int)IM_U8_MAX); - else if (data_type == ImGuiDataType_S16) - *(ImS16*)p_data = (ImS16)ImClamp(v32, (int)IM_S16_MIN, (int)IM_S16_MAX); - else if (data_type == ImGuiDataType_U16) - *(ImU16*)p_data = (ImU16)ImClamp(v32, (int)IM_U16_MIN, (int)IM_U16_MAX); - else - IM_ASSERT(0); - } - - return memcmp(&data_backup, p_data, type_info->Size) != 0; -} - -template -static int DataTypeCompareT(const T* lhs, const T* rhs) -{ - if (*lhs < *rhs) return -1; - if (*lhs > *rhs) return +1; - return 0; -} - -int ImGui::DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2) -{ - switch (data_type) - { - case ImGuiDataType_S8: return DataTypeCompareT((const ImS8* )arg_1, (const ImS8* )arg_2); - case ImGuiDataType_U8: return DataTypeCompareT((const ImU8* )arg_1, (const ImU8* )arg_2); - case ImGuiDataType_S16: return DataTypeCompareT((const ImS16* )arg_1, (const ImS16* )arg_2); - case ImGuiDataType_U16: return DataTypeCompareT((const ImU16* )arg_1, (const ImU16* )arg_2); - case ImGuiDataType_S32: return DataTypeCompareT((const ImS32* )arg_1, (const ImS32* )arg_2); - case ImGuiDataType_U32: return DataTypeCompareT((const ImU32* )arg_1, (const ImU32* )arg_2); - case ImGuiDataType_S64: return DataTypeCompareT((const ImS64* )arg_1, (const ImS64* )arg_2); - case ImGuiDataType_U64: return DataTypeCompareT((const ImU64* )arg_1, (const ImU64* )arg_2); - case ImGuiDataType_Float: return DataTypeCompareT((const float* )arg_1, (const float* )arg_2); - case ImGuiDataType_Double: return DataTypeCompareT((const double*)arg_1, (const double*)arg_2); - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); - return 0; -} - -template -static bool DataTypeClampT(T* v, const T* v_min, const T* v_max) -{ - // Clamp, both sides are optional, return true if modified - if (v_min && *v < *v_min) { *v = *v_min; return true; } - if (v_max && *v > *v_max) { *v = *v_max; return true; } - return false; -} - -bool ImGui::DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max) -{ - switch (data_type) - { - case ImGuiDataType_S8: return DataTypeClampT((ImS8* )p_data, (const ImS8* )p_min, (const ImS8* )p_max); - case ImGuiDataType_U8: return DataTypeClampT((ImU8* )p_data, (const ImU8* )p_min, (const ImU8* )p_max); - case ImGuiDataType_S16: return DataTypeClampT((ImS16* )p_data, (const ImS16* )p_min, (const ImS16* )p_max); - case ImGuiDataType_U16: return DataTypeClampT((ImU16* )p_data, (const ImU16* )p_min, (const ImU16* )p_max); - case ImGuiDataType_S32: return DataTypeClampT((ImS32* )p_data, (const ImS32* )p_min, (const ImS32* )p_max); - case ImGuiDataType_U32: return DataTypeClampT((ImU32* )p_data, (const ImU32* )p_min, (const ImU32* )p_max); - case ImGuiDataType_S64: return DataTypeClampT((ImS64* )p_data, (const ImS64* )p_min, (const ImS64* )p_max); - case ImGuiDataType_U64: return DataTypeClampT((ImU64* )p_data, (const ImU64* )p_min, (const ImU64* )p_max); - case ImGuiDataType_Float: return DataTypeClampT((float* )p_data, (const float* )p_min, (const float* )p_max); - case ImGuiDataType_Double: return DataTypeClampT((double*)p_data, (const double*)p_min, (const double*)p_max); - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); - return false; -} - -static float GetMinimumStepAtDecimalPrecision(int decimal_precision) -{ - static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; - if (decimal_precision < 0) - return FLT_MIN; - return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); -} - -template -TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v) -{ - IM_UNUSED(data_type); - IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); - const char* fmt_start = ImParseFormatFindStart(format); - if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string - return v; - - // Sanitize format - char fmt_sanitized[32]; - ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized)); - fmt_start = fmt_sanitized; - - // Format value with our rounding, and read back - char v_str[64]; - ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v); - const char* p = v_str; - while (*p == ' ') - p++; - v = (TYPE)ImAtof(p); - - return v; -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. -//------------------------------------------------------------------------- -// - DragBehaviorT<>() [Internal] -// - DragBehavior() [Internal] -// - DragScalar() -// - DragScalarN() -// - DragFloat() -// - DragFloat2() -// - DragFloat3() -// - DragFloat4() -// - DragFloatRange2() -// - DragInt() -// - DragInt2() -// - DragInt3() -// - DragInt4() -// - DragIntRange2() -//------------------------------------------------------------------------- - -// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) -template -bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags) -{ - ImGuiContext& g = *GImGui; - const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; - const bool is_clamped = (v_min < v_max); - const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; - const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); - - // Default tweak speed - if (v_speed == 0.0f && is_clamped && (v_max - v_min < FLT_MAX)) - v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio); - - // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings - float adjust_delta = 0.0f; - if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) - { - adjust_delta = g.IO.MouseDelta[axis]; - if (g.IO.KeyAlt) - adjust_delta *= 1.0f / 100.0f; - if (g.IO.KeyShift) - adjust_delta *= 10.0f; - } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; - adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiNavReadMode_RepeatFast, 1.0f / 10.0f, 10.0f)[axis]; - v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); - } - adjust_delta *= v_speed; - - // For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter. - if (axis == ImGuiAxis_Y) - adjust_delta = -adjust_delta; - - // For logarithmic use our range is effectively 0..1 so scale the delta into that range - if (is_logarithmic && (v_max - v_min < FLT_MAX) && ((v_max - v_min) > 0.000001f)) // Epsilon to avoid /0 - adjust_delta /= (float)(v_max - v_min); - - // Clear current value on activation - // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300. - bool is_just_activated = g.ActiveIdIsJustActivated; - bool is_already_past_limits_and_pushing_outward = is_clamped && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f)); - if (is_just_activated || is_already_past_limits_and_pushing_outward) - { - g.DragCurrentAccum = 0.0f; - g.DragCurrentAccumDirty = false; - } - else if (adjust_delta != 0.0f) - { - g.DragCurrentAccum += adjust_delta; - g.DragCurrentAccumDirty = true; - } - - if (!g.DragCurrentAccumDirty) - return false; - - TYPE v_cur = *v; - FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f; - - float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true - const float zero_deadzone_halfsize = 0.0f; // Drag widgets have no deadzone (as it doesn't make sense) - if (is_logarithmic) - { - // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound. - const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1; - logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); - - // Convert to parametric space, apply delta, convert back - float v_old_parametric = ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - float v_new_parametric = v_old_parametric + g.DragCurrentAccum; - v_cur = ScaleValueFromRatioT(data_type, v_new_parametric, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - v_old_ref_for_accum_remainder = v_old_parametric; - } - else - { - v_cur += (SIGNEDTYPE)g.DragCurrentAccum; - } - - // Round to user desired precision based on format string - if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) - v_cur = RoundScalarWithFormatT(format, data_type, v_cur); - - // Preserve remainder after rounding has been applied. This also allow slow tweaking of values. - g.DragCurrentAccumDirty = false; - if (is_logarithmic) - { - // Convert to parametric space, apply delta, convert back - float v_new_parametric = ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder); - } - else - { - g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v); - } - - // Lose zero sign for float/double - if (v_cur == (TYPE)-0) - v_cur = (TYPE)0; - - // Clamp values (+ handle overflow/wrap-around for integer types) - if (*v != v_cur && is_clamped) - { - if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_floating_point)) - v_cur = v_min; - if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_floating_point)) - v_cur = v_max; - } - - // Apply result - if (*v == v_cur) - return false; - *v = v_cur; - return true; -} - -bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) -{ - // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. - IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); - - ImGuiContext& g = *GImGui; - if (g.ActiveId == id) - { - if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) - ClearActiveID(); - else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) - ClearActiveID(); - } - if (g.ActiveId != id) - return false; - if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) - return false; - - switch (data_type) - { - case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS8*) p_min : IM_S8_MIN, p_max ? *(const ImS8*)p_max : IM_S8_MAX, format, flags); if (r) *(ImS8*)p_v = (ImS8)v32; return r; } - case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU8*) p_min : IM_U8_MIN, p_max ? *(const ImU8*)p_max : IM_U8_MAX, format, flags); if (r) *(ImU8*)p_v = (ImU8)v32; return r; } - case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS16*)p_min : IM_S16_MIN, p_max ? *(const ImS16*)p_max : IM_S16_MAX, format, flags); if (r) *(ImS16*)p_v = (ImS16)v32; return r; } - case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU16*)p_min : IM_U16_MIN, p_max ? *(const ImU16*)p_max : IM_U16_MAX, format, flags); if (r) *(ImU16*)p_v = (ImU16)v32; return r; } - case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)p_v, v_speed, p_min ? *(const ImS32* )p_min : IM_S32_MIN, p_max ? *(const ImS32* )p_max : IM_S32_MAX, format, flags); - case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)p_v, v_speed, p_min ? *(const ImU32* )p_min : IM_U32_MIN, p_max ? *(const ImU32* )p_max : IM_U32_MAX, format, flags); - case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)p_v, v_speed, p_min ? *(const ImS64* )p_min : IM_S64_MIN, p_max ? *(const ImS64* )p_max : IM_S64_MAX, format, flags); - case ImGuiDataType_U64: return DragBehaviorT(data_type, (ImU64*)p_v, v_speed, p_min ? *(const ImU64* )p_min : IM_U64_MIN, p_max ? *(const ImU64* )p_max : IM_U64_MAX, format, flags); - case ImGuiDataType_Float: return DragBehaviorT(data_type, (float*)p_v, v_speed, p_min ? *(const float* )p_min : -FLT_MAX, p_max ? *(const float* )p_max : FLT_MAX, format, flags); - case ImGuiDataType_Double: return DragBehaviorT(data_type, (double*)p_v, v_speed, p_min ? *(const double*)p_min : -DBL_MAX, p_max ? *(const double*)p_max : DBL_MAX, format, flags); - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); - return false; -} - -// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a Drag widget, p_min and p_max are optional. -// Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. -bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) - return false; - - // Default format string when passing NULL - if (format == NULL) - format = DataTypeGetInfo(data_type)->PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) - format = PatchFormatStringFloatToInt(format); - - const bool hovered = ItemHoverable(frame_bb, id); - bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); - if (!temp_input_is_active) - { - // Tabbing or CTRL-clicking on Drag turns it into an InputText - const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; - const bool clicked = (hovered && g.IO.MouseClicked[0]); - const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2); - const bool make_active = (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id); - if (make_active && temp_input_allowed) - if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) - temp_input_is_active = true; - - // (Optional) simple click (without moving) turns Drag into an InputText - if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) - if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) - { - g.NavActivateId = g.NavActivateInputId = id; - g.NavActivateFlags = ImGuiActivateFlags_PreferInput; - temp_input_is_active = true; - } - - if (make_active && !temp_input_is_active) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - } - } - - if (temp_input_is_active) - { - // Only clamp CTRL+Click input when ImGuiSliderFlags_AlwaysClamp is set - const bool is_clamp_input = (flags & ImGuiSliderFlags_AlwaysClamp) != 0 && (p_min == NULL || p_max == NULL || DataTypeCompare(data_type, p_min, p_max) < 0); - return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL); - } - - // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); - - // Drag behavior - const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, flags); - if (value_changed) - MarkItemEdited(id); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); - if (g.LogEnabled) - LogSetNextTextDecoration("{", "}"); - RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); - return value_changed; -} - -bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components, CalcItemWidth()); - size_t type_size = GDataTypeInfo[data_type].Size; - for (int i = 0; i < components; i++) - { - PushID(i); - if (i > 0) - SameLine(0, g.Style.ItemInnerSpacing.x); - value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, flags); - PopID(); - PopItemWidth(); - p_data = (void*)((char*)p_data + type_size); - } - PopID(); - - const char* label_end = FindRenderedTextEnd(label); - if (label != label_end) - { - SameLine(0, g.Style.ItemInnerSpacing.x); - TextEx(label, label_end); - } - - EndGroup(); - return value_changed; -} - -bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) -{ - return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, flags); -} - -bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) -{ - return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, flags); -} - -bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) -{ - return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, flags); -} - -bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) -{ - return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, flags); -} - -// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this. -bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, ImGuiSliderFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - PushID(label); - BeginGroup(); - PushMultiItemsWidths(2, CalcItemWidth()); - - float min_min = (v_min >= v_max) ? -FLT_MAX : v_min; - float min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max); - ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0); - bool value_changed = DragScalar("##min", ImGuiDataType_Float, v_current_min, v_speed, &min_min, &min_max, format, min_flags); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - - float max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min); - float max_max = (v_min >= v_max) ? FLT_MAX : v_max; - ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0); - value_changed |= DragScalar("##max", ImGuiDataType_Float, v_current_max, v_speed, &max_min, &max_max, format_max ? format_max : format, max_flags); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - - TextEx(label, FindRenderedTextEnd(label)); - EndGroup(); - PopID(); - - return value_changed; -} - -// NB: v_speed is float to allow adjusting the drag speed with more precision -bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) -{ - return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format, flags); -} - -bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) -{ - return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format, flags); -} - -bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) -{ - return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format, flags); -} - -bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) -{ - return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format, flags); -} - -// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this. -bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max, ImGuiSliderFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - PushID(label); - BeginGroup(); - PushMultiItemsWidths(2, CalcItemWidth()); - - int min_min = (v_min >= v_max) ? INT_MIN : v_min; - int min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max); - ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0); - bool value_changed = DragInt("##min", v_current_min, v_speed, min_min, min_max, format, min_flags); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - - int max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min); - int max_max = (v_min >= v_max) ? INT_MAX : v_max; - ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0); - value_changed |= DragInt("##max", v_current_max, v_speed, max_min, max_max, format_max ? format_max : format, max_flags); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - - TextEx(label, FindRenderedTextEnd(label)); - EndGroup(); - PopID(); - - return value_changed; -} - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -// Obsolete versions with power parameter. See https://github.com/ocornut/imgui/issues/3361 for details. -bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power) -{ - ImGuiSliderFlags drag_flags = ImGuiSliderFlags_None; - if (power != 1.0f) - { - IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!"); - IM_ASSERT(p_min != NULL && p_max != NULL); // When using a power curve the drag needs to have known bounds - drag_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths - } - return DragScalar(label, data_type, p_data, v_speed, p_min, p_max, format, drag_flags); -} - -bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power) -{ - ImGuiSliderFlags drag_flags = ImGuiSliderFlags_None; - if (power != 1.0f) - { - IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!"); - IM_ASSERT(p_min != NULL && p_max != NULL); // When using a power curve the drag needs to have known bounds - drag_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths - } - return DragScalarN(label, data_type, p_data, components, v_speed, p_min, p_max, format, drag_flags); -} - -#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -//------------------------------------------------------------------------- -// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. -//------------------------------------------------------------------------- -// - ScaleRatioFromValueT<> [Internal] -// - ScaleValueFromRatioT<> [Internal] -// - SliderBehaviorT<>() [Internal] -// - SliderBehavior() [Internal] -// - SliderScalar() -// - SliderScalarN() -// - SliderFloat() -// - SliderFloat2() -// - SliderFloat3() -// - SliderFloat4() -// - SliderAngle() -// - SliderInt() -// - SliderInt2() -// - SliderInt3() -// - SliderInt4() -// - VSliderScalar() -// - VSliderFloat() -// - VSliderInt() -//------------------------------------------------------------------------- - -// Convert a value v in the output space of a slider into a parametric position on the slider itself (the logical opposite of ScaleValueFromRatioT) -template -float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) -{ - if (v_min == v_max) - return 0.0f; - IM_UNUSED(data_type); - - const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); - if (is_logarithmic) - { - bool flipped = v_max < v_min; - - if (flipped) // Handle the case where the range is backwards - ImSwap(v_min, v_max); - - // Fudge min/max to avoid getting close to log(0) - FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min; - FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max; - - // Awkward special cases - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon) - if ((v_min == 0.0f) && (v_max < 0.0f)) - v_min_fudged = -logarithmic_zero_epsilon; - else if ((v_max == 0.0f) && (v_min < 0.0f)) - v_max_fudged = -logarithmic_zero_epsilon; - - float result; - if (v_clamped <= v_min_fudged) - result = 0.0f; // Workaround for values that are in-range but below our fudge - else if (v_clamped >= v_max_fudged) - result = 1.0f; // Workaround for values that are in-range but above our fudge - else if ((v_min * v_max) < 0.0f) // Range crosses zero, so split into two portions - { - float zero_point_center = (-(float)v_min) / ((float)v_max - (float)v_min); // The zero point in parametric space. There's an argument we should take the logarithmic nature into account when calculating this, but for now this should do (and the most common case of a symmetrical range works fine) - float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize; - float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize; - if (v == 0.0f) - result = zero_point_center; // Special case for exactly zero - else if (v < 0.0f) - result = (1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(-v_min_fudged / logarithmic_zero_epsilon))) * zero_point_snap_L; - else - result = zero_point_snap_R + ((float)(ImLog((FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(v_max_fudged / logarithmic_zero_epsilon)) * (1.0f - zero_point_snap_R)); - } - else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider - result = 1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / -v_max_fudged) / ImLog(-v_min_fudged / -v_max_fudged)); - else - result = (float)(ImLog((FLOATTYPE)v_clamped / v_min_fudged) / ImLog(v_max_fudged / v_min_fudged)); - - return flipped ? (1.0f - result) : result; - } - else - { - // Linear slider - return (float)((FLOATTYPE)(SIGNEDTYPE)(v_clamped - v_min) / (FLOATTYPE)(SIGNEDTYPE)(v_max - v_min)); - } -} - -// Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT) -template -TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) -{ - // We special-case the extents because otherwise our logarithmic fudging can lead to "mathematically correct" - // but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value. Also generally simpler. - if (t <= 0.0f || v_min == v_max) - return v_min; - if (t >= 1.0f) - return v_max; - - TYPE result = (TYPE)0; - if (is_logarithmic) - { - // Fudge min/max to avoid getting silly results close to zero - FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min; - FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max; - - const bool flipped = v_max < v_min; // Check if range is "backwards" - if (flipped) - ImSwap(v_min_fudged, v_max_fudged); - - // Awkward special case - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon) - if ((v_max == 0.0f) && (v_min < 0.0f)) - v_max_fudged = -logarithmic_zero_epsilon; - - float t_with_flip = flipped ? (1.0f - t) : t; // t, but flipped if necessary to account for us flipping the range - - if ((v_min * v_max) < 0.0f) // Range crosses zero, so we have to do this in two parts - { - float zero_point_center = (-(float)ImMin(v_min, v_max)) / ImAbs((float)v_max - (float)v_min); // The zero point in parametric space - float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize; - float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize; - if (t_with_flip >= zero_point_snap_L && t_with_flip <= zero_point_snap_R) - result = (TYPE)0.0f; // Special case to make getting exactly zero possible (the epsilon prevents it otherwise) - else if (t_with_flip < zero_point_center) - result = (TYPE)-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, (FLOATTYPE)(1.0f - (t_with_flip / zero_point_snap_L)))); - else - result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (FLOATTYPE)((t_with_flip - zero_point_snap_R) / (1.0f - zero_point_snap_R)))); - } - else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider - result = (TYPE)-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, (FLOATTYPE)(1.0f - t_with_flip))); - else - result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, (FLOATTYPE)t_with_flip)); - } - else - { - // Linear slider - const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); - if (is_floating_point) - { - result = ImLerp(v_min, v_max, t); - } - else if (t < 1.0) - { - // - For integer values we want the clicking position to match the grab box so we round above - // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. - // - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64 - // range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits. - FLOATTYPE v_new_off_f = (SIGNEDTYPE)(v_max - v_min) * t; - result = (TYPE)((SIGNEDTYPE)v_min + (SIGNEDTYPE)(v_new_off_f + (FLOATTYPE)(v_min > v_max ? -0.5 : 0.5))); - } - } - - return result; -} - -// FIXME: Try to move more of the code into shared SliderBehavior() -template -bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb) -{ - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; - const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; - const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); - const SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); - - // Calculate bounds - const float grab_padding = 2.0f; // FIXME: Should be part of style. - const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f; - float grab_sz = style.GrabMinSize; - if (!is_floating_point && v_range >= 0) // v_range < 0 may happen on integer overflows - grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit - grab_sz = ImMin(grab_sz, slider_sz); - const float slider_usable_sz = slider_sz - grab_sz; - const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f; - const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f; - - float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true - float zero_deadzone_halfsize = 0.0f; // Only valid when is_logarithmic is true - if (is_logarithmic) - { - // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound. - const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1; - logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); - zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f); - } - - // Process interacting with the slider - bool value_changed = false; - if (g.ActiveId == id) - { - bool set_new_value = false; - float clicked_t = 0.0f; - if (g.ActiveIdSource == ImGuiInputSource_Mouse) - { - if (!g.IO.MouseDown[0]) - { - ClearActiveID(); - } - else - { - const float mouse_abs_pos = g.IO.MousePos[axis]; - if (g.ActiveIdIsJustActivated) - { - float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - if (axis == ImGuiAxis_Y) - grab_t = 1.0f - grab_t; - const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here. - g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f; - } - if (slider_usable_sz > 0.0f) - clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz); - if (axis == ImGuiAxis_Y) - clicked_t = 1.0f - clicked_t; - set_new_value = true; - } - } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - if (g.ActiveIdIsJustActivated) - { - g.SliderCurrentAccum = 0.0f; // Reset any stored nav delta upon activation - g.SliderCurrentAccumDirty = false; - } - - const ImVec2 input_delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); - float input_delta = (axis == ImGuiAxis_X) ? input_delta2.x : -input_delta2.y; - if (input_delta != 0.0f) - { - const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; - if (decimal_precision > 0) - { - input_delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds - if (IsNavInputDown(ImGuiNavInput_TweakSlow)) - input_delta /= 10.0f; - } - else - { - if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow)) - input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps - else - input_delta /= 100.0f; - } - if (IsNavInputDown(ImGuiNavInput_TweakFast)) - input_delta *= 10.0f; - - g.SliderCurrentAccum += input_delta; - g.SliderCurrentAccumDirty = true; - } - - float delta = g.SliderCurrentAccum; - if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) - { - ClearActiveID(); - } - else if (g.SliderCurrentAccumDirty) - { - clicked_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - - if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits - { - set_new_value = false; - g.SliderCurrentAccum = 0.0f; // If pushing up against the limits, don't continue to accumulate - } - else - { - set_new_value = true; - float old_clicked_t = clicked_t; - clicked_t = ImSaturate(clicked_t + delta); - - // Calculate what our "new" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator - TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) - v_new = RoundScalarWithFormatT(format, data_type, v_new); - float new_clicked_t = ScaleRatioFromValueT(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - - if (delta > 0) - g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta); - else - g.SliderCurrentAccum -= ImMax(new_clicked_t - old_clicked_t, delta); - } - - g.SliderCurrentAccumDirty = false; - } - } - - if (set_new_value) - { - TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - - // Round to user desired precision based on format string - if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) - v_new = RoundScalarWithFormatT(format, data_type, v_new); - - // Apply result - if (*v != v_new) - { - *v = v_new; - value_changed = true; - } - } - } - - if (slider_sz < 1.0f) - { - *out_grab_bb = ImRect(bb.Min, bb.Min); - } - else - { - // Output grab position so it can be displayed by the caller - float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - if (axis == ImGuiAxis_Y) - grab_t = 1.0f - grab_t; - const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - if (axis == ImGuiAxis_X) - *out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding); - else - *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f); - } - - return value_changed; -} - -// For 32-bit and larger types, slider bounds are limited to half the natural type range. -// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok. -// It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders. -bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb) -{ - // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. - IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); - - ImGuiContext& g = *GImGui; - if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) - return false; - - switch (data_type) - { - case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min, *(const ImS8*)p_max, format, flags, out_grab_bb); if (r) *(ImS8*)p_v = (ImS8)v32; return r; } - case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)p_min, *(const ImU8*)p_max, format, flags, out_grab_bb); if (r) *(ImU8*)p_v = (ImU8)v32; return r; } - case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)p_min, *(const ImS16*)p_max, format, flags, out_grab_bb); if (r) *(ImS16*)p_v = (ImS16)v32; return r; } - case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)p_min, *(const ImU16*)p_max, format, flags, out_grab_bb); if (r) *(ImU16*)p_v = (ImU16)v32; return r; } - case ImGuiDataType_S32: - IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN / 2 && *(const ImS32*)p_max <= IM_S32_MAX / 2); - return SliderBehaviorT(bb, id, data_type, (ImS32*)p_v, *(const ImS32*)p_min, *(const ImS32*)p_max, format, flags, out_grab_bb); - case ImGuiDataType_U32: - IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX / 2); - return SliderBehaviorT(bb, id, data_type, (ImU32*)p_v, *(const ImU32*)p_min, *(const ImU32*)p_max, format, flags, out_grab_bb); - case ImGuiDataType_S64: - IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN / 2 && *(const ImS64*)p_max <= IM_S64_MAX / 2); - return SliderBehaviorT(bb, id, data_type, (ImS64*)p_v, *(const ImS64*)p_min, *(const ImS64*)p_max, format, flags, out_grab_bb); - case ImGuiDataType_U64: - IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX / 2); - return SliderBehaviorT(bb, id, data_type, (ImU64*)p_v, *(const ImU64*)p_min, *(const ImU64*)p_max, format, flags, out_grab_bb); - case ImGuiDataType_Float: - IM_ASSERT(*(const float*)p_min >= -FLT_MAX / 2.0f && *(const float*)p_max <= FLT_MAX / 2.0f); - return SliderBehaviorT(bb, id, data_type, (float*)p_v, *(const float*)p_min, *(const float*)p_max, format, flags, out_grab_bb); - case ImGuiDataType_Double: - IM_ASSERT(*(const double*)p_min >= -DBL_MAX / 2.0f && *(const double*)p_max <= DBL_MAX / 2.0f); - return SliderBehaviorT(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, flags, out_grab_bb); - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); - return false; -} - -// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required. -// Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. -bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) - return false; - - // Default format string when passing NULL - if (format == NULL) - format = DataTypeGetInfo(data_type)->PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) - format = PatchFormatStringFloatToInt(format); - - const bool hovered = ItemHoverable(frame_bb, id); - bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); - if (!temp_input_is_active) - { - // Tabbing or CTRL-clicking on Slider turns it into an input box - const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; - const bool clicked = (hovered && g.IO.MouseClicked[0]); - const bool make_active = (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id); - if (make_active && temp_input_allowed) - if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id) - temp_input_is_active = true; - - if (make_active && !temp_input_is_active) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - } - } - - if (temp_input_is_active) - { - // Only clamp CTRL+Click input when ImGuiSliderFlags_AlwaysClamp is set - const bool is_clamp_input = (flags & ImGuiSliderFlags_AlwaysClamp) != 0; - return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL); - } - - // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); - - // Slider behavior - ImRect grab_bb; - const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags, &grab_bb); - if (value_changed) - MarkItemEdited(id); - - // Render grab - if (grab_bb.Max.x > grab_bb.Min.x) - window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); - if (g.LogEnabled) - LogSetNextTextDecoration("{", "}"); - RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); - return value_changed; -} - -// Add multiple sliders on 1 line for compact edition of multiple components -bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, ImGuiSliderFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components, CalcItemWidth()); - size_t type_size = GDataTypeInfo[data_type].Size; - for (int i = 0; i < components; i++) - { - PushID(i); - if (i > 0) - SameLine(0, g.Style.ItemInnerSpacing.x); - value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, flags); - PopID(); - PopItemWidth(); - v = (void*)((char*)v + type_size); - } - PopID(); - - const char* label_end = FindRenderedTextEnd(label); - if (label != label_end) - { - SameLine(0, g.Style.ItemInnerSpacing.x); - TextEx(label, label_end); - } - - EndGroup(); - return value_changed; -} - -bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) -{ - return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, flags); -} - -bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) -{ - return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, flags); -} - -bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) -{ - return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, flags); -} - -bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) -{ - return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, flags); -} - -bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags) -{ - if (format == NULL) - format = "%.0f deg"; - float v_deg = (*v_rad) * 360.0f / (2 * IM_PI); - bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags); - *v_rad = v_deg * (2 * IM_PI) / 360.0f; - return value_changed; -} - -bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) -{ - return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format, flags); -} - -bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) -{ - return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format, flags); -} - -bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) -{ - return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format, flags); -} - -bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) -{ - return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags); -} - -bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); - const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(frame_bb, id)) - return false; - - // Default format string when passing NULL - if (format == NULL) - format = DataTypeGetInfo(data_type)->PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) - format = PatchFormatStringFloatToInt(format); - - const bool hovered = ItemHoverable(frame_bb, id); - if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavActivateInputId == id) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - } - - // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); - - // Slider behavior - ImRect grab_bb; - const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags | ImGuiSliderFlags_Vertical, &grab_bb); - if (value_changed) - MarkItemEdited(id); - - // Render grab - if (grab_bb.Max.y > grab_bb.Min.y) - window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - // For the vertical slider we allow centered text to overlap the frame padding - char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); - RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f)); - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - return value_changed; -} - -bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) -{ - return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, flags); -} - -bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) -{ - return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format, flags); -} - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -// Obsolete versions with power parameter. See https://github.com/ocornut/imgui/issues/3361 for details. -bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power) -{ - ImGuiSliderFlags slider_flags = ImGuiSliderFlags_None; - if (power != 1.0f) - { - IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!"); - slider_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths - } - return SliderScalar(label, data_type, p_data, p_min, p_max, format, slider_flags); -} - -bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiSliderFlags slider_flags = ImGuiSliderFlags_None; - if (power != 1.0f) - { - IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!"); - slider_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths - } - return SliderScalarN(label, data_type, v, components, v_min, v_max, format, slider_flags); -} - -#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -//------------------------------------------------------------------------- -// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. -//------------------------------------------------------------------------- -// - ImParseFormatFindStart() [Internal] -// - ImParseFormatFindEnd() [Internal] -// - ImParseFormatTrimDecorations() [Internal] -// - ImParseFormatSanitizeForPrinting() [Internal] -// - ImParseFormatSanitizeForScanning() [Internal] -// - ImParseFormatPrecision() [Internal] -// - TempInputTextScalar() [Internal] -// - InputScalar() -// - InputScalarN() -// - InputFloat() -// - InputFloat2() -// - InputFloat3() -// - InputFloat4() -// - InputInt() -// - InputInt2() -// - InputInt3() -// - InputInt4() -// - InputDouble() -//------------------------------------------------------------------------- - -// We don't use strchr() because our strings are usually very short and often start with '%' -const char* ImParseFormatFindStart(const char* fmt) -{ - while (char c = fmt[0]) - { - if (c == '%' && fmt[1] != '%') - return fmt; - else if (c == '%') - fmt++; - fmt++; - } - return fmt; -} - -const char* ImParseFormatFindEnd(const char* fmt) -{ - // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format. - if (fmt[0] != '%') - return fmt; - const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A')); - const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a')); - for (char c; (c = *fmt) != 0; fmt++) - { - if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0) - return fmt + 1; - if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0) - return fmt + 1; - } - return fmt; -} - -// Extract the format out of a format string with leading or trailing decorations -// fmt = "blah blah" -> return fmt -// fmt = "%.3f" -> return fmt -// fmt = "hello %.3f" -> return fmt + 6 -// fmt = "%.3f hello" -> return buf written with "%.3f" -const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_size) -{ - const char* fmt_start = ImParseFormatFindStart(fmt); - if (fmt_start[0] != '%') - return fmt; - const char* fmt_end = ImParseFormatFindEnd(fmt_start); - if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data. - return fmt_start; - ImStrncpy(buf, fmt_start, ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size)); - return buf; -} - -// Sanitize format -// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi -// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi. -void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size) -{ - const char* fmt_end = ImParseFormatFindEnd(fmt_in); - IM_UNUSED(fmt_out_size); - IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! - while (fmt_in < fmt_end) - { - char c = *fmt_in++; - if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. - *(fmt_out++) = c; - } - *fmt_out = 0; // Zero-terminate -} - -// - For scanning we need to remove all width and precision fields "%3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d" -const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size) -{ - const char* fmt_end = ImParseFormatFindEnd(fmt_in); - const char* fmt_out_begin = fmt_out; - IM_UNUSED(fmt_out_size); - IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! - bool has_type = false; - while (fmt_in < fmt_end) - { - char c = *fmt_in++; - if (!has_type && ((c >= '0' && c <= '9') || c == '.')) - continue; - has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits - if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. - *(fmt_out++) = c; - } - *fmt_out = 0; // Zero-terminate - return fmt_out_begin; -} - -template -static const char* ImAtoi(const char* src, TYPE* output) -{ - int negative = 0; - if (*src == '-') { negative = 1; src++; } - if (*src == '+') { src++; } - TYPE v = 0; - while (*src >= '0' && *src <= '9') - v = (v * 10) + (*src++ - '0'); - *output = negative ? -v : v; - return src; -} - -// Parse display precision back from the display format string -// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed. -int ImParseFormatPrecision(const char* fmt, int default_precision) -{ - fmt = ImParseFormatFindStart(fmt); - if (fmt[0] != '%') - return default_precision; - fmt++; - while (*fmt >= '0' && *fmt <= '9') - fmt++; - int precision = INT_MAX; - if (*fmt == '.') - { - fmt = ImAtoi(fmt + 1, &precision); - if (precision < 0 || precision > 99) - precision = default_precision; - } - if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation - precision = -1; - if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX) - precision = -1; - return (precision == INT_MAX) ? default_precision : precision; -} - -// Create text input in place of another active widget (e.g. used when doing a CTRL+Click on drag/slider widgets) -// FIXME: Facilitate using this in variety of other situations. -bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags) -{ - // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id. - // We clear ActiveID on the first frame to allow the InputText() taking it back. - ImGuiContext& g = *GImGui; - const bool init = (g.TempInputId != id); - if (init) - ClearActiveID(); - - g.CurrentWindow->DC.CursorPos = bb.Min; - bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); - if (init) - { - // First frame we started displaying the InputText widget, we expect it to take the active id. - IM_ASSERT(g.ActiveId == id); - g.TempInputId = g.ActiveId; - } - return value_changed; -} - -static inline ImGuiInputTextFlags InputScalar_DefaultCharsFilter(ImGuiDataType data_type, const char* format) -{ - if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) - return ImGuiInputTextFlags_CharsScientific; - const char format_last_char = format[0] ? format[strlen(format) - 1] : 0; - return (format_last_char == 'x' || format_last_char == 'X') ? ImGuiInputTextFlags_CharsHexadecimal : ImGuiInputTextFlags_CharsDecimal; -} - -// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set! -// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility. -// However this may not be ideal for all uses, as some user code may break on out of bound values. -bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max) -{ - char fmt_buf[32]; - char data_buf[32]; - format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); - DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); - ImStrTrimBlanks(data_buf); - - ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; - flags |= InputScalar_DefaultCharsFilter(data_type, format); - - bool value_changed = false; - if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) - { - // Backup old value - size_t data_type_size = DataTypeGetInfo(data_type)->Size; - ImGuiDataTypeTempStorage data_backup; - memcpy(&data_backup, p_data, data_type_size); - - // Apply new value (or operations) then clamp - DataTypeApplyFromText(data_buf, data_type, p_data, format); - if (p_clamp_min || p_clamp_max) - { - if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0) - ImSwap(p_clamp_min, p_clamp_max); - DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max); - } - - // Only mark as edited if new value is different - value_changed = memcmp(&data_backup, p_data, data_type_size) != 0; - if (value_changed) - MarkItemEdited(id); - } - return value_changed; -} - -// Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional. -// Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. -bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - - if (format == NULL) - format = DataTypeGetInfo(data_type)->PrintFmt; - - char buf[64]; - DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); - - // Testing ActiveId as a minor optimization as filtering is not needed until active - if (g.ActiveId == 0 && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) - flags |= InputScalar_DefaultCharsFilter(data_type, format); - flags |= ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. - - bool value_changed = false; - if (p_step != NULL) - { - const float button_size = GetFrameHeight(); - - BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() - PushID(label); - SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); - if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view - value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); - - // Step buttons - const ImVec2 backup_frame_padding = style.FramePadding; - style.FramePadding.x = style.FramePadding.y; - ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups; - if (flags & ImGuiInputTextFlags_ReadOnly) - BeginDisabled(); - SameLine(0, style.ItemInnerSpacing.x); - if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) - { - DataTypeApplyOp(data_type, '-', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); - value_changed = true; - } - SameLine(0, style.ItemInnerSpacing.x); - if (ButtonEx("+", ImVec2(button_size, button_size), button_flags)) - { - DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); - value_changed = true; - } - if (flags & ImGuiInputTextFlags_ReadOnly) - EndDisabled(); - - const char* label_end = FindRenderedTextEnd(label); - if (label != label_end) - { - SameLine(0, style.ItemInnerSpacing.x); - TextEx(label, label_end); - } - style.FramePadding = backup_frame_padding; - - PopID(); - EndGroup(); - } - else - { - if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) - value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); - } - if (value_changed) - MarkItemEdited(g.LastItemData.ID); - - return value_changed; -} - -bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components, CalcItemWidth()); - size_t type_size = GDataTypeInfo[data_type].Size; - for (int i = 0; i < components; i++) - { - PushID(i); - if (i > 0) - SameLine(0, g.Style.ItemInnerSpacing.x); - value_changed |= InputScalar("", data_type, p_data, p_step, p_step_fast, format, flags); - PopID(); - PopItemWidth(); - p_data = (void*)((char*)p_data + type_size); - } - PopID(); - - const char* label_end = FindRenderedTextEnd(label); - if (label != label_end) - { - SameLine(0.0f, g.Style.ItemInnerSpacing.x); - TextEx(label, label_end); - } - - EndGroup(); - return value_changed; -} - -bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags) -{ - flags |= ImGuiInputTextFlags_CharsScientific; - return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags); -} - -bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags) -{ - return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags); -} - -bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags) -{ - return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags); -} - -bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags) -{ - return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags); -} - -bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags) -{ - // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. - const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; - return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step > 0 ? &step : NULL), (void*)(step_fast > 0 ? &step_fast : NULL), format, flags); -} - -bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags) -{ - return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", flags); -} - -bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags) -{ - return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", flags); -} - -bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags) -{ - return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", flags); -} - -bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags) -{ - flags |= ImGuiInputTextFlags_CharsScientific; - return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step > 0.0 ? &step : NULL), (void*)(step_fast > 0.0 ? &step_fast : NULL), format, flags); -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint -//------------------------------------------------------------------------- -// - InputText() -// - InputTextWithHint() -// - InputTextMultiline() -// - InputTextGetCharInfo() [Internal] -// - InputTextReindexLines() [Internal] -// - InputTextReindexLinesRange() [Internal] -// - InputTextEx() [Internal] -// - DebugNodeInputTextState() [Internal] -//------------------------------------------------------------------------- - -bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) -{ - IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() - return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); -} - -bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) -{ - return InputTextEx(label, NULL, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); -} - -bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) -{ - IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() - return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); -} - -static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) -{ - int line_count = 0; - const char* s = text_begin; - while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding - if (c == '\n') - line_count++; - s--; - if (s[0] != '\n' && s[0] != '\r') - line_count++; - *out_text_end = s; - return line_count; -} - -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) -{ - ImGuiContext& g = *GImGui; - ImFont* font = g.Font; - const float line_height = g.FontSize; - const float scale = line_height / font->FontSize; - - ImVec2 text_size = ImVec2(0, 0); - float line_width = 0.0f; - - const ImWchar* s = text_begin; - while (s < text_end) - { - unsigned int c = (unsigned int)(*s++); - if (c == '\n') - { - text_size.x = ImMax(text_size.x, line_width); - text_size.y += line_height; - line_width = 0.0f; - if (stop_on_new_line) - break; - continue; - } - if (c == '\r') - continue; - - const float char_width = font->GetCharAdvance((ImWchar)c) * scale; - line_width += char_width; - } - - if (text_size.x < line_width) - text_size.x = line_width; - - if (out_offset) - *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n - - if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n - text_size.y += line_height; - - if (remaining) - *remaining = s; - - return text_size; -} - -// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) -namespace ImStb -{ - -static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; } -static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; } -static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } -static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; } -static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; -static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) -{ - const ImWchar* text = obj->TextW.Data; - const ImWchar* text_remaining = NULL; - const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); - r->x0 = 0.0f; - r->x1 = size.x; - r->baseline_y_delta = size.y; - r->ymin = 0.0f; - r->ymax = size.y; - r->num_chars = (int)(text_remaining - (text + line_start_idx)); -} - -// When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. -static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r'; } -static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } -static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } -static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } -#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h -#ifdef __APPLE__ // FIXME: Move setting to IO structure -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC -#else -static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN -#endif - -static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) -{ - ImWchar* dst = obj->TextW.Data + pos; - - // We maintain our buffer length in both UTF-8 and wchar formats - obj->Edited = true; - obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); - obj->CurLenW -= n; - - // Offset remaining text (FIXME-OPT: Use memmove) - const ImWchar* src = obj->TextW.Data + pos + n; - while (ImWchar c = *src++) - *dst++ = c; - *dst = '\0'; -} - -static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ImWchar* new_text, int new_text_len) -{ - const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; - const int text_len = obj->CurLenW; - IM_ASSERT(pos <= text_len); - - const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); - if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA)) - return false; - - // Grow internal buffer if needed - if (new_text_len + text_len + 1 > obj->TextW.Size) - { - if (!is_resizable) - return false; - IM_ASSERT(text_len < obj->TextW.Size); - obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1); - } - - ImWchar* text = obj->TextW.Data; - if (pos != text_len) - memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); - memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); - - obj->Edited = true; - obj->CurLenW += new_text_len; - obj->CurLenA += new_text_len_utf8; - obj->TextW[obj->CurLenW] = '\0'; - - return true; -} - -// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) -#define STB_TEXTEDIT_K_LEFT 0x200000 // keyboard input to move cursor left -#define STB_TEXTEDIT_K_RIGHT 0x200001 // keyboard input to move cursor right -#define STB_TEXTEDIT_K_UP 0x200002 // keyboard input to move cursor up -#define STB_TEXTEDIT_K_DOWN 0x200003 // keyboard input to move cursor down -#define STB_TEXTEDIT_K_LINESTART 0x200004 // keyboard input to move cursor to start of line -#define STB_TEXTEDIT_K_LINEEND 0x200005 // keyboard input to move cursor to end of line -#define STB_TEXTEDIT_K_TEXTSTART 0x200006 // keyboard input to move cursor to start of text -#define STB_TEXTEDIT_K_TEXTEND 0x200007 // keyboard input to move cursor to end of text -#define STB_TEXTEDIT_K_DELETE 0x200008 // keyboard input to delete selection or character under cursor -#define STB_TEXTEDIT_K_BACKSPACE 0x200009 // keyboard input to delete selection or character left of cursor -#define STB_TEXTEDIT_K_UNDO 0x20000A // keyboard input to perform undo -#define STB_TEXTEDIT_K_REDO 0x20000B // keyboard input to perform redo -#define STB_TEXTEDIT_K_WORDLEFT 0x20000C // keyboard input to move cursor left one word -#define STB_TEXTEDIT_K_WORDRIGHT 0x20000D // keyboard input to move cursor right one word -#define STB_TEXTEDIT_K_PGUP 0x20000E // keyboard input to move cursor up a page -#define STB_TEXTEDIT_K_PGDOWN 0x20000F // keyboard input to move cursor down a page -#define STB_TEXTEDIT_K_SHIFT 0x400000 - -#define STB_TEXTEDIT_IMPLEMENTATION -#include "imstb_textedit.h" - -// stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling -// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?) -static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) -{ - stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len); - ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW); - if (text_len <= 0) - return; - if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len)) - { - state->cursor = text_len; - state->has_preferred_x = 0; - return; - } - IM_ASSERT(0); // Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace() -} - -} // namespace ImStb - -void ImGuiInputTextState::OnKeyPressed(int key) -{ - stb_textedit_key(this, &Stb, key); - CursorFollow = true; - CursorAnimReset(); -} - -ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() -{ - memset(this, 0, sizeof(*this)); -} - -// Public API to manipulate UTF-8 text -// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) -// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. -void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) -{ - IM_ASSERT(pos + bytes_count <= BufTextLen); - char* dst = Buf + pos; - const char* src = Buf + pos + bytes_count; - while (char c = *src++) - *dst++ = c; - *dst = '\0'; - - if (CursorPos >= pos + bytes_count) - CursorPos -= bytes_count; - else if (CursorPos >= pos) - CursorPos = pos; - SelectionStart = SelectionEnd = CursorPos; - BufDirty = true; - BufTextLen -= bytes_count; -} - -void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) -{ - const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; - const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); - if (new_text_len + BufTextLen >= BufSize) - { - if (!is_resizable) - return; - - // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the mildly similar code (until we remove the U16 buffer altogether!) - ImGuiContext& g = *GImGui; - ImGuiInputTextState* edit_state = &g.InputTextState; - IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID); - IM_ASSERT(Buf == edit_state->TextA.Data); - int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; - edit_state->TextA.reserve(new_buf_size + 1); - Buf = edit_state->TextA.Data; - BufSize = edit_state->BufCapacityA = new_buf_size; - } - - if (BufTextLen != pos) - memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); - memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); - Buf[BufTextLen + new_text_len] = '\0'; - - if (CursorPos >= pos) - CursorPos += new_text_len; - SelectionStart = SelectionEnd = CursorPos; - BufDirty = true; - BufTextLen += new_text_len; -} - -// Return false to discard a character. -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source) -{ - IM_ASSERT(input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Clipboard); - unsigned int c = *p_char; - - // Filter non-printable (NB: isprint is unreliable! see #2467) - bool apply_named_filters = true; - if (c < 0x20) - { - bool pass = false; - pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code) - pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); - if (!pass) - return false; - apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted. - } - - if (input_source != ImGuiInputSource_Clipboard) - { - // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817) - if (c == 127) - return false; - - // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME) - if (c >= 0xE000 && c <= 0xF8FF) - return false; - } - - // Filter Unicode ranges we are not handling in this build - if (c > IM_UNICODE_CODEPOINT_MAX) - return false; - - // Generic named filters - if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))) - { - // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'. - // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. - // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. - // Change the default decimal_point with: - // ImGui::GetCurrentContext()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point; - // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. - ImGuiContext& g = *GImGui; - const unsigned c_decimal_point = (unsigned int)g.PlatformLocaleDecimalPoint; - - // Allow 0-9 . - + * / - if (flags & ImGuiInputTextFlags_CharsDecimal) - if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/')) - return false; - - // Allow 0-9 . - + * / e E - if (flags & ImGuiInputTextFlags_CharsScientific) - if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) - return false; - - // Allow 0-9 a-F A-F - if (flags & ImGuiInputTextFlags_CharsHexadecimal) - if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) - return false; - - // Turn a-z into A-Z - if (flags & ImGuiInputTextFlags_CharsUppercase) - if (c >= 'a' && c <= 'z') - *p_char = (c += (unsigned int)('A' - 'a')); - - if (flags & ImGuiInputTextFlags_CharsNoBlank) - if (ImCharIsBlankW(c)) - return false; - } - - // Custom callback filter - if (flags & ImGuiInputTextFlags_CallbackCharFilter) - { - ImGuiInputTextCallbackData callback_data; - memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData)); - callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; - callback_data.EventChar = (ImWchar)c; - callback_data.Flags = flags; - callback_data.UserData = user_data; - if (callback(&callback_data) != 0) - return false; - *p_char = callback_data.EventChar; - if (!callback_data.EventChar) - return false; - } - - return true; -} - -// Find the shortest single replacement we can make to get the new text from the old text. -// Important: needs to be run before TextW is rewritten with the new characters because calling STB_TEXTEDIT_GETCHAR() at the end. -// FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly. -static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a) -{ - ImGuiContext& g = *GImGui; - const ImWchar* old_buf = state->TextW.Data; - const int old_length = state->CurLenW; - const int new_length = ImTextCountCharsFromUtf8(new_buf_a, new_buf_a + new_length_a); - g.TempBuffer.reserve_discard((new_length + 1) * sizeof(ImWchar)); - ImWchar* new_buf = (ImWchar*)(void*)g.TempBuffer.Data; - ImTextStrFromUtf8(new_buf, new_length + 1, new_buf_a, new_buf_a + new_length_a); - - const int shorter_length = ImMin(old_length, new_length); - int first_diff; - for (first_diff = 0; first_diff < shorter_length; first_diff++) - if (old_buf[first_diff] != new_buf[first_diff]) - break; - if (first_diff == old_length && first_diff == new_length) - return; - - int old_last_diff = old_length - 1; - int new_last_diff = new_length - 1; - for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--) - if (old_buf[old_last_diff] != new_buf[new_last_diff]) - break; - - const int insert_len = new_last_diff - first_diff + 1; - const int delete_len = old_last_diff - first_diff + 1; - if (insert_len > 0 || delete_len > 0) - if (STB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb.undostate, first_diff, delete_len, insert_len)) - for (int i = 0; i < delete_len; i++) - p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, first_diff + i); -} - -// Edit a string of text -// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". -// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match -// Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. -// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. -// - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h -// (FIXME: Rather confusing and messy function, among the worse part of our codebase, expecting to rewrite a V2 at some point.. Partly because we are -// doing UTF8 > U16 > UTF8 conversions on the go to easily interface with stb_textedit. Ideally should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) -bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - IM_ASSERT(buf != NULL && buf_size >= 0); - IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) - IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) - - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - const ImGuiStyle& style = g.Style; - - const bool RENDER_SELECTION_WHEN_INACTIVE = false; - const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; - const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0; - const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; - const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; - const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; - if (is_resizable) - IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! - - if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar) - BeginGroup(); - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line - const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y); - - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); - const ImRect total_bb(frame_bb.Min, frame_bb.Min + total_size); - - ImGuiWindow* draw_window = window; - ImVec2 inner_size = frame_size; - ImGuiItemStatusFlags item_status_flags = 0; - ImGuiLastItemData item_data_backup; - if (is_multiline) - { - ImVec2 backup_pos = window->DC.CursorPos; - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) - { - EndGroup(); - return false; - } - item_status_flags = g.LastItemData.StatusFlags; - item_data_backup = g.LastItemData; - window->DC.CursorPos = backup_pos; - - // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. - // FIXME-NAV: Pressing NavActivate will trigger general child activation right before triggering our own below. Harmless but bizarre. - PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); - PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); - PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); - PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges - bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove); - PopStyleVar(3); - PopStyleColor(); - if (!child_visible) - { - EndChild(); - EndGroup(); - return false; - } - draw_window = g.CurrentWindow; // Child window - draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. - draw_window->DC.CursorPos += style.FramePadding; - inner_size.x -= draw_window->ScrollbarSizes.x; - } - else - { - // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd) - ItemSize(total_bb, style.FramePadding.y); - if (!(flags & ImGuiInputTextFlags_MergedItem)) - if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) - return false; - item_status_flags = g.LastItemData.StatusFlags; - } - const bool hovered = ItemHoverable(frame_bb, id); - if (hovered) - g.MouseCursor = ImGuiMouseCursor_TextInput; - - // We are only allowed to access the state if we are already the active widget. - ImGuiInputTextState* state = GetInputTextState(id); - - const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; - const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); - - const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); - const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); - bool clear_active_id = false; - bool select_all = false; - - float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; - - const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); - const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing); - const bool init_state = (init_make_active || user_scroll_active); - if ((init_state && g.ActiveId != id) || init_changed_specs) - { - // Access state even if we don't own it yet. - state = &g.InputTextState; - state->CursorAnimReset(); - - // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) - // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) - const int buf_len = (int)strlen(buf); - state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. - memcpy(state->InitialTextA.Data, buf, buf_len + 1); - - // Preserve cursor position and undo/redo stack if we come back to same widget - // FIXME: Since we reworked this on 2022/06, may want to differenciate recycle_cursor vs recycle_undostate? - bool recycle_state = (state->ID == id && !init_changed_specs); - if (recycle_state && (state->CurLenA != buf_len || (state->TextAIsValid && strncmp(state->TextA.Data, buf, buf_len) != 0))) - recycle_state = false; - - // Start edition - const char* buf_end = NULL; - state->ID = id; - state->TextW.resize(buf_size + 1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string. - state->TextA.resize(0); - state->TextAIsValid = false; // TextA is not valid yet (we will display buf until then) - state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, buf_size, buf, NULL, &buf_end); - state->CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. - - if (recycle_state) - { - // Recycle existing cursor/selection/undo stack but clamp position - // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. - state->CursorClamp(); - } - else - { - state->ScrollX = 0.0f; - stb_textedit_initialize_state(&state->Stb, !is_multiline); - } - - if (!is_multiline) - { - if (flags & ImGuiInputTextFlags_AutoSelectAll) - select_all = true; - if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState))) - select_all = true; - if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl)) - select_all = true; - } - - if (flags & ImGuiInputTextFlags_AlwaysOverwrite) - state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863) - } - - if (g.ActiveId != id && init_make_active) - { - IM_ASSERT(state && state->ID == id); - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - - // Declare our inputs - IM_ASSERT(ImGuiNavInput_COUNT < 32); - g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory)) - g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel); - SetActiveIdUsingKey(ImGuiKey_Home); - SetActiveIdUsingKey(ImGuiKey_End); - if (is_multiline) - { - SetActiveIdUsingKey(ImGuiKey_PageUp); - SetActiveIdUsingKey(ImGuiKey_PageDown); - } - if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character. - { - SetActiveIdUsingKey(ImGuiKey_Tab); - } - } - - // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function) - if (g.ActiveId == id && state == NULL) - ClearActiveID(); - - // Release focus when we click outside - if (g.ActiveId == id && io.MouseClicked[0] && !init_state && !init_make_active) //-V560 - clear_active_id = true; - - // Lock the decision of whether we are going to take the path displaying the cursor or selection - const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); - bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); - bool value_changed = false; - bool enter_pressed = false; - - // When read-only we always use the live data passed to the function - // FIXME-OPT: Because our selection/cursor code currently needs the wide text we need to convert it when active, which is not ideal :( - if (is_readonly && state != NULL && (render_cursor || render_selection)) - { - const char* buf_end = NULL; - state->TextW.resize(buf_size + 1); - state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, buf, NULL, &buf_end); - state->CurLenA = (int)(buf_end - buf); - state->CursorClamp(); - render_selection &= state->HasSelection(); - } - - // Select the buffer to render. - const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state && state->TextAIsValid; - const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0); - - // Password pushes a temporary font with only a fallback glyph - if (is_password && !is_displaying_hint) - { - const ImFontGlyph* glyph = g.Font->FindGlyph('*'); - ImFont* password_font = &g.InputTextPasswordFont; - password_font->FontSize = g.Font->FontSize; - password_font->Scale = g.Font->Scale; - password_font->Ascent = g.Font->Ascent; - password_font->Descent = g.Font->Descent; - password_font->ContainerAtlas = g.Font->ContainerAtlas; - password_font->FallbackGlyph = glyph; - password_font->FallbackAdvanceX = glyph->AdvanceX; - IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); - PushFont(password_font); - } - - // Process mouse inputs and character inputs - int backup_current_text_length = 0; - if (g.ActiveId == id) - { - IM_ASSERT(state != NULL); - backup_current_text_length = state->CurLenA; - state->Edited = false; - state->BufCapacityA = buf_size; - state->Flags = flags; - - // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. - // Down the line we should have a cleaner library-wide concept of Selected vs Active. - g.ActiveIdAllowOverlap = !io.MouseDown[0]; - g.WantTextInputNextFrame = 1; - - // Edit in progress - const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->ScrollX; - const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); - - const bool is_osx = io.ConfigMacOSXBehaviors; - if (select_all) - { - state->SelectAll(); - state->SelectedAllMouseLock = true; - } - else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift) - { - stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); - const int multiclick_count = (io.MouseClickedCount[0] - 2); - if ((multiclick_count % 2) == 0) - { - // Double-click: Select word - // We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant: - // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) - const bool is_bol = (state->Stb.cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor - 1) == '\n'; - if (STB_TEXT_HAS_SELECTION(&state->Stb) || !is_bol) - state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); - if (!STB_TEXT_HAS_SELECTION(&state->Stb)) - ImStb::stb_textedit_prep_selection_at_cursor(&state->Stb); - state->Stb.cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb.cursor); - state->Stb.select_end = state->Stb.cursor; - ImStb::stb_textedit_clamp(state, &state->Stb); - } - else - { - // Triple-click: Select line - const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor) == '\n'; - state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); - state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); - state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); - if (!is_eol && is_multiline) - { - ImSwap(state->Stb.select_start, state->Stb.select_end); - state->Stb.cursor = state->Stb.select_end; - } - state->CursorFollow = false; - } - state->CursorAnimReset(); - } - else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) - { - // FIXME: unselect on late click could be done release? - if (hovered) - { - stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); - state->CursorAnimReset(); - } - } - else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) - { - stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y); - state->CursorAnimReset(); - state->CursorFollow = true; - } - if (state->SelectedAllMouseLock && !io.MouseDown[0]) - state->SelectedAllMouseLock = false; - - // We except backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336) - // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes) - const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); - if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressed(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly) - { - unsigned int c = '\t'; // Insert TAB - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) - state->OnKeyPressed((int)c); - } - - // Process regular text input (before we check for Return because using some IME will effectively send a Return?) - // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. - if (io.InputQueueCharacters.Size > 0) - { - if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav) - for (int n = 0; n < io.InputQueueCharacters.Size; n++) - { - // Insert character if they pass filtering - unsigned int c = (unsigned int)io.InputQueueCharacters[n]; - if (c == '\t') // Skip Tab, see above. - continue; - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) - state->OnKeyPressed((int)c); - } - - // Consume characters - io.InputQueueCharacters.resize(0); - } - } - - // Process other shortcuts/key-presses - bool cancel_edit = false; - if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) - { - IM_ASSERT(state != NULL); - - const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1); - state->Stb.row_count_per_page = row_count_per_page; - - const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); - const bool is_osx = io.ConfigMacOSXBehaviors; - const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiModFlags_Super | ImGuiModFlags_Shift)); - const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl - const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End - const bool is_ctrl_key_only = (io.KeyMods == ImGuiModFlags_Ctrl); - const bool is_shift_key_only = (io.KeyMods == ImGuiModFlags_Shift); - const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiModFlags_Super) : (io.KeyMods == ImGuiModFlags_Ctrl); - - const bool is_cut = ((is_shortcut_key && IsKeyPressed(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); - const bool is_copy = ((is_shortcut_key && IsKeyPressed(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection()); - const bool is_paste = ((is_shortcut_key && IsKeyPressed(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_readonly; - const bool is_undo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Z)) && !is_readonly && is_undoable); - const bool is_redo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressed(ImGuiKey_Z))) && !is_readonly && is_undoable; - - // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. - const bool is_validate_enter = IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_KeypadEnter); - const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiNavReadMode_Pressed) && !IsKeyPressed(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiNavReadMode_Pressed); - const bool is_cancel = IsKeyPressed(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiNavReadMode_Pressed); - - if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } - else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } - else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } - else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } - else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; } - else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } - else if (IsKeyPressed(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } - else if (IsKeyPressed(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } - else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly) - { - if (!state->HasSelection()) - { - if (is_wordmove_key_down) - state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT); - else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) - state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT); - } - state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); - } - else if (is_validate_enter) - { - bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; - if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) - { - enter_pressed = clear_active_id = true; - } - else if (!is_readonly) - { - unsigned int c = '\n'; // Insert new line - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) - state->OnKeyPressed((int)c); - } - } - else if (is_validate_nav) - { - IM_ASSERT(!is_validate_enter); - enter_pressed = clear_active_id = true; - } - else if (is_cancel) - { - clear_active_id = cancel_edit = true; - } - else if (is_undo || is_redo) - { - state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); - state->ClearSelection(); - } - else if (is_shortcut_key && IsKeyPressed(ImGuiKey_A)) - { - state->SelectAll(); - state->CursorFollow = true; - } - else if (is_cut || is_copy) - { - // Cut, Copy - if (io.SetClipboardTextFn) - { - const int ib = state->HasSelection() ? ImMin(state->Stb.select_start, state->Stb.select_end) : 0; - const int ie = state->HasSelection() ? ImMax(state->Stb.select_start, state->Stb.select_end) : state->CurLenW; - const int clipboard_data_len = ImTextCountUtf8BytesFromStr(state->TextW.Data + ib, state->TextW.Data + ie) + 1; - char* clipboard_data = (char*)IM_ALLOC(clipboard_data_len * sizeof(char)); - ImTextStrToUtf8(clipboard_data, clipboard_data_len, state->TextW.Data + ib, state->TextW.Data + ie); - SetClipboardText(clipboard_data); - MemFree(clipboard_data); - } - if (is_cut) - { - if (!state->HasSelection()) - state->SelectAll(); - state->CursorFollow = true; - stb_textedit_cut(state, &state->Stb); - } - } - else if (is_paste) - { - if (const char* clipboard = GetClipboardText()) - { - // Filter pasted buffer - const int clipboard_len = (int)strlen(clipboard); - ImWchar* clipboard_filtered = (ImWchar*)IM_ALLOC((clipboard_len + 1) * sizeof(ImWchar)); - int clipboard_filtered_len = 0; - for (const char* s = clipboard; *s; ) - { - unsigned int c; - s += ImTextCharFromUtf8(&c, s, NULL); - if (c == 0) - break; - if (!InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Clipboard)) - continue; - clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; - } - clipboard_filtered[clipboard_filtered_len] = 0; - if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation - { - stb_textedit_paste(state, &state->Stb, clipboard_filtered, clipboard_filtered_len); - state->CursorFollow = true; - } - MemFree(clipboard_filtered); - } - } - - // Update render selection flag after events have been handled, so selection highlight can be displayed during the same frame. - render_selection |= state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); - } - - // Process callbacks and apply result back to user's buffer. - const char* apply_new_text = NULL; - int apply_new_text_length = 0; - if (g.ActiveId == id) - { - IM_ASSERT(state != NULL); - if (cancel_edit) - { - // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. - if (!is_readonly && strcmp(buf, state->InitialTextA.Data) != 0) - { - // Push records into the undo stack so we can CTRL+Z the revert operation itself - apply_new_text = state->InitialTextA.Data; - apply_new_text_length = state->InitialTextA.Size - 1; - ImVector w_text; - if (apply_new_text_length > 0) - { - w_text.resize(ImTextCountCharsFromUtf8(apply_new_text, apply_new_text + apply_new_text_length) + 1); - ImTextStrFromUtf8(w_text.Data, w_text.Size, apply_new_text, apply_new_text + apply_new_text_length); - } - stb_textedit_replace(state, &state->Stb, w_text.Data, (apply_new_text_length > 0) ? (w_text.Size - 1) : 0); - } - } - - // Apply ASCII value - if (!is_readonly) - { - state->TextAIsValid = true; - state->TextA.resize(state->TextW.Size * 4 + 1); - ImTextStrToUtf8(state->TextA.Data, state->TextA.Size, state->TextW.Data, NULL); - } - - // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. - // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. - // This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize). - const bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); - if (apply_edit_back_to_user_buffer) - { - // Apply new value immediately - copy modified buffer back - // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer - // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. - // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. - - // User callback - if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackAlways)) != 0) - { - IM_ASSERT(callback != NULL); - - // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. - ImGuiInputTextFlags event_flag = 0; - ImGuiKey event_key = ImGuiKey_None; - if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressed(ImGuiKey_Tab)) - { - event_flag = ImGuiInputTextFlags_CallbackCompletion; - event_key = ImGuiKey_Tab; - } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow)) - { - event_flag = ImGuiInputTextFlags_CallbackHistory; - event_key = ImGuiKey_UpArrow; - } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow)) - { - event_flag = ImGuiInputTextFlags_CallbackHistory; - event_key = ImGuiKey_DownArrow; - } - else if ((flags & ImGuiInputTextFlags_CallbackEdit) && state->Edited) - { - event_flag = ImGuiInputTextFlags_CallbackEdit; - } - else if (flags & ImGuiInputTextFlags_CallbackAlways) - { - event_flag = ImGuiInputTextFlags_CallbackAlways; - } - - if (event_flag) - { - ImGuiInputTextCallbackData callback_data; - memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData)); - callback_data.EventFlag = event_flag; - callback_data.Flags = flags; - callback_data.UserData = callback_user_data; - - char* callback_buf = is_readonly ? buf : state->TextA.Data; - callback_data.EventKey = event_key; - callback_data.Buf = callback_buf; - callback_data.BufTextLen = state->CurLenA; - callback_data.BufSize = state->BufCapacityA; - callback_data.BufDirty = false; - - // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) - ImWchar* text = state->TextW.Data; - const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + state->Stb.cursor); - const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_start); - const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_end); - - // Call user code - callback(&callback_data); - - // Read back what user may have modified - callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback - IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields - IM_ASSERT(callback_data.BufSize == state->BufCapacityA); - IM_ASSERT(callback_data.Flags == flags); - const bool buf_dirty = callback_data.BufDirty; - if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; } - if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb.select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb.cursor : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } - if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb.select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb.select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } - if (buf_dirty) - { - IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! - InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ? - if (callback_data.BufTextLen > backup_current_text_length && is_resizable) - state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); // Worse case scenario resize - state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, callback_data.Buf, NULL); - state->CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() - state->CursorAnimReset(); - } - } - } - - // Will copy result string if modified - if (!is_readonly && strcmp(state->TextA.Data, buf) != 0) - { - apply_new_text = state->TextA.Data; - apply_new_text_length = state->CurLenA; - } - } - - // Clear temporary user storage - state->Flags = ImGuiInputTextFlags_None; - } - - // Copy result to user buffer. This can currently only happen when (g.ActiveId == id) - if (apply_new_text != NULL) - { - // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size - // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used - // without any storage on user's side. - IM_ASSERT(apply_new_text_length >= 0); - if (is_resizable) - { - ImGuiInputTextCallbackData callback_data; - callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; - callback_data.Flags = flags; - callback_data.Buf = buf; - callback_data.BufTextLen = apply_new_text_length; - callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); - callback_data.UserData = callback_user_data; - callback(&callback_data); - buf = callback_data.Buf; - buf_size = callback_data.BufSize; - apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); - IM_ASSERT(apply_new_text_length <= buf_size); - } - //IMGUI_DEBUG_PRINT("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); - - // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. - ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); - value_changed = true; - } - - // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) - if (clear_active_id && g.ActiveId == id) - ClearActiveID(); - - // Render frame - if (!is_multiline) - { - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - } - - const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size - ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; - ImVec2 text_size(0.0f, 0.0f); - - // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line - // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. - // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash. - const int buf_display_max_length = 2 * 1024 * 1024; - const char* buf_display = buf_display_from_state ? state->TextA.Data : buf; //-V595 - const char* buf_display_end = NULL; // We have specialized paths below for setting the length - if (is_displaying_hint) - { - buf_display = hint; - buf_display_end = hint + strlen(hint); - } - - // Render text. We currently only render selection when the widget is active or while scrolling. - // FIXME: We could remove the '&& render_cursor' to keep rendering selection when inactive. - if (render_cursor || render_selection) - { - IM_ASSERT(state != NULL); - if (!is_displaying_hint) - buf_display_end = buf_display + state->CurLenA; - - // Render text (with cursor and selection) - // This is going to be messy. We need to: - // - Display the text (this alone can be more easily clipped) - // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) - // - Measure text height (for scrollbar) - // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) - // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. - const ImWchar* text_begin = state->TextW.Data; - ImVec2 cursor_offset, select_start_offset; - - { - // Find lines numbers straddling 'cursor' (slot 0) and 'select_start' (slot 1) positions. - const ImWchar* searches_input_ptr[2] = { NULL, NULL }; - int searches_result_line_no[2] = { -1000, -1000 }; - int searches_remaining = 0; - if (render_cursor) - { - searches_input_ptr[0] = text_begin + state->Stb.cursor; - searches_result_line_no[0] = -1; - searches_remaining++; - } - if (render_selection) - { - searches_input_ptr[1] = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); - searches_result_line_no[1] = -1; - searches_remaining++; - } - - // Iterate all lines to find our line numbers - // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. - searches_remaining += is_multiline ? 1 : 0; - int line_count = 0; - //for (const ImWchar* s = text_begin; (s = (const ImWchar*)wcschr((const wchar_t*)s, (wchar_t)'\n')) != NULL; s++) // FIXME-OPT: Could use this when wchar_t are 16-bit - for (const ImWchar* s = text_begin; *s != 0; s++) - if (*s == '\n') - { - line_count++; - if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; } - if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; } - } - line_count++; - if (searches_result_line_no[0] == -1) - searches_result_line_no[0] = line_count; - if (searches_result_line_no[1] == -1) - searches_result_line_no[1] = line_count; - - // Calculate 2d position by finding the beginning of the line and measuring distance - cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; - cursor_offset.y = searches_result_line_no[0] * g.FontSize; - if (searches_result_line_no[1] >= 0) - { - select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; - select_start_offset.y = searches_result_line_no[1] * g.FontSize; - } - - // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) - if (is_multiline) - text_size = ImVec2(inner_size.x, line_count * g.FontSize); - } - - // Scroll - if (render_cursor && state->CursorFollow) - { - // Horizontal scroll in chunks of quarter width - if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) - { - const float scroll_increment_x = inner_size.x * 0.25f; - const float visible_width = inner_size.x - style.FramePadding.x; - if (cursor_offset.x < state->ScrollX) - state->ScrollX = IM_FLOOR(ImMax(0.0f, cursor_offset.x - scroll_increment_x)); - else if (cursor_offset.x - visible_width >= state->ScrollX) - state->ScrollX = IM_FLOOR(cursor_offset.x - visible_width + scroll_increment_x); - } - else - { - state->ScrollX = 0.0f; - } - - // Vertical scroll - if (is_multiline) - { - // Test if cursor is vertically visible - if (cursor_offset.y - g.FontSize < scroll_y) - scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); - else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) - scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; - const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); - scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y); - draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag - draw_window->Scroll.y = scroll_y; - } - - state->CursorFollow = false; - } - - // Draw selection - const ImVec2 draw_scroll = ImVec2(state->ScrollX, 0.0f); - if (render_selection) - { - const ImWchar* text_selected_begin = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); - const ImWchar* text_selected_end = text_begin + ImMax(state->Stb.select_start, state->Stb.select_end); - - ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. - float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. - float bg_offy_dn = is_multiline ? 0.0f : 2.0f; - ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll; - for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) - { - if (rect_pos.y > clip_rect.w + g.FontSize) - break; - if (rect_pos.y < clip_rect.y) - { - //p = (const ImWchar*)wmemchr((const wchar_t*)p, '\n', text_selected_end - p); // FIXME-OPT: Could use this when wchar_t are 16-bit - //p = p ? p + 1 : text_selected_end; - while (p < text_selected_end) - if (*p++ == '\n') - break; - } - else - { - ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); - if (rect_size.x <= 0.0f) rect_size.x = IM_FLOOR(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines - ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn)); - rect.ClipWith(clip_rect); - if (rect.Overlaps(clip_rect)) - draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); - } - rect_pos.x = draw_pos.x - draw_scroll.x; - rect_pos.y += g.FontSize; - } - } - - // We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash. - if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) - { - ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); - draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); - } - - // Draw blinking cursor - if (render_cursor) - { - state->CursorAnim += io.DeltaTime; - bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll); - ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); - if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); - - // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) - if (!is_readonly) - { - g.PlatformImeData.WantVisible = true; - g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); - g.PlatformImeData.InputLineHeight = g.FontSize; - } - } - } - else - { - // Render text only (no selection, no cursor) - if (is_multiline) - text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) * g.FontSize); // We don't need width - else if (!is_displaying_hint && g.ActiveId == id) - buf_display_end = buf_display + state->CurLenA; - else if (!is_displaying_hint) - buf_display_end = buf_display + strlen(buf_display); - - if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) - { - ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); - draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); - } - } - - if (is_password && !is_displaying_hint) - PopFont(); - - if (is_multiline) - { - // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (ref issue #4761)... - Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y)); - ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; - EndChild(); - item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); - g.CurrentItemFlags = backup_item_flags; - - // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active... - // FIXME: This quite messy/tricky, should attempt to get rid of the child window. - EndGroup(); - if (g.LastItemData.ID == 0) - { - g.LastItemData.ID = id; - g.LastItemData.InFlags = item_data_backup.InFlags; - g.LastItemData.StatusFlags = item_data_backup.StatusFlags; - } - } - - // Log as text - if (g.LogEnabled && (!is_password || is_displaying_hint)) - { - LogSetNextTextDecoration("{", "}"); - LogRenderedText(&draw_pos, buf_display, buf_display_end); - } - - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - if (value_changed && !(flags & ImGuiInputTextFlags_NoMarkEdited)) - MarkItemEdited(id); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); - if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) - return enter_pressed; - else - return value_changed; -} - -void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) -{ -#ifndef IMGUI_DISABLE_DEBUG_TOOLS - ImGuiContext& g = *GImGui; - ImStb::STB_TexteditState* stb_state = &state->Stb; - ImStb::StbUndoState* undo_state = &stb_state->undostate; - Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId); - Text("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, state->CurLenW, stb_state->cursor, stb_state->select_start, stb_state->select_end); - Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point); - if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 15), true)) // Visualize undo state - { - PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - for (int n = 0; n < STB_TEXTEDIT_UNDOSTATECOUNT; n++) - { - ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n]; - const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' '; - if (undo_rec_type == ' ') - BeginDisabled(); - char buf[64] = ""; - if (undo_rec_type != ' ' && undo_rec->char_storage != -1) - ImTextStrToUtf8(buf, IM_ARRAYSIZE(buf), undo_state->undo_char + undo_rec->char_storage, undo_state->undo_char + undo_rec->char_storage + undo_rec->insert_length); - Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%s\"", - undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf); - if (undo_rec_type == ' ') - EndDisabled(); - } - PopStyleVar(); - } - EndChild(); -#else - IM_UNUSED(state); -#endif -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. -//------------------------------------------------------------------------- -// - ColorEdit3() -// - ColorEdit4() -// - ColorPicker3() -// - RenderColorRectWithAlphaCheckerboard() [Internal] -// - ColorPicker4() -// - ColorButton() -// - SetColorEditOptions() -// - ColorTooltip() [Internal] -// - ColorEditOptionsPopup() [Internal] -// - ColorPickerOptionsPopup() [Internal] -//------------------------------------------------------------------------- - -bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) -{ - return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); -} - -// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation. -// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting. -static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V) -{ - // This check is optional. Suppose we have two color widgets side by side, both widgets display different colors, but both colors have hue and/or saturation undefined. - // With color check: hue/saturation is preserved in one widget. Editing color in one widget would reset hue/saturation in another one. - // Without color check: common hue/saturation would be displayed in all widgets that have hue/saturation undefined. - // g.ColorEditLastColor is stored as ImU32 RGB value: this essentially gives us color equality check with reduced precision. - // Tiny external color changes would not be detected and this check would still pass. This is OK, since we only restore hue/saturation _only_ if they are undefined, - // therefore this change flipping hue/saturation from undefined to a very tiny value would still be represented in color picker. - ImGuiContext& g = *GImGui; - if (g.ColorEditLastColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) - return; - - // When S == 0, H is undefined. - // When H == 1 it wraps around to 0. - if (*S == 0.0f || (*H == 0.0f && g.ColorEditLastHue == 1)) - *H = g.ColorEditLastHue; - - // When V == 0, S is undefined. - if (*V == 0.0f) - *S = g.ColorEditLastSat; -} - -// Edit colors components (each component in 0.0f..1.0f range). -// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -// With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. -bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float square_sz = GetFrameHeight(); - const float w_full = CalcItemWidth(); - const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); - const float w_inputs = w_full - w_button; - const char* label_display_end = FindRenderedTextEnd(label); - g.NextItemData.ClearFlags(); - - BeginGroup(); - PushID(label); - - // If we're not showing any slider there's no point in doing any HSV conversions - const ImGuiColorEditFlags flags_untouched = flags; - if (flags & ImGuiColorEditFlags_NoInputs) - flags = (flags & (~ImGuiColorEditFlags_DisplayMask_)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; - - // Context menu: display and modify options (before defaults are applied) - if (!(flags & ImGuiColorEditFlags_NoOptions)) - ColorEditOptionsPopup(col, flags); - - // Read stored options - if (!(flags & ImGuiColorEditFlags_DisplayMask_)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_); - if (!(flags & ImGuiColorEditFlags_DataTypeMask_)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_); - if (!(flags & ImGuiColorEditFlags_PickerMask_)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_); - if (!(flags & ImGuiColorEditFlags_InputMask_)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_); - flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_)); - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected - - const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; - const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; - const int components = alpha ? 4 : 3; - - // Convert to the formats we need - float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; - if ((flags & ImGuiColorEditFlags_InputHSV) && (flags & ImGuiColorEditFlags_DisplayRGB)) - ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV)) - { - // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. - ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - ColorEditRestoreHS(col, &f[0], &f[1], &f[2]); - } - int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; - - bool value_changed = false; - bool value_changed_as_float = false; - - const ImVec2 pos = window->DC.CursorPos; - const float inputs_offset_x = (style.ColorButtonPosition == ImGuiDir_Left) ? w_button : 0.0f; - window->DC.CursorPos.x = pos.x + inputs_offset_x; - - if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) - { - // RGB/HSV 0..255 Sliders - const float w_item_one = ImMax(1.0f, IM_FLOOR((w_inputs - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components)); - const float w_item_last = ImMax(1.0f, IM_FLOOR(w_inputs - (w_item_one + style.ItemInnerSpacing.x) * (components - 1))); - - const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); - static const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; - static const char* fmt_table_int[3][4] = - { - { "%3d", "%3d", "%3d", "%3d" }, // Short display - { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA - { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA - }; - static const char* fmt_table_float[3][4] = - { - { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display - { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA - { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA - }; - const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1; - - for (int n = 0; n < components; n++) - { - if (n > 0) - SameLine(0, style.ItemInnerSpacing.x); - SetNextItemWidth((n + 1 < components) ? w_item_one : w_item_last); - - // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0. - if (flags & ImGuiColorEditFlags_Float) - { - value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); - value_changed_as_float |= value_changed; - } - else - { - value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); - } - } - else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) - { - // RGB Hexadecimal Input - char buf[64]; - if (alpha) - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255)); - else - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255)); - SetNextItemWidth(w_inputs); - if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) - { - value_changed = true; - char* p = buf; - while (*p == '#' || ImCharIsBlankA(*p)) - p++; - i[0] = i[1] = i[2] = 0; - i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha) - int r; - if (alpha) - r = sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) - else - r = sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); - IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'. - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); - } - - ImGuiWindow* picker_active_window = NULL; - if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) - { - const float button_offset_x = ((flags & ImGuiColorEditFlags_NoInputs) || (style.ColorButtonPosition == ImGuiDir_Left)) ? 0.0f : w_inputs + style.ItemInnerSpacing.x; - window->DC.CursorPos = ImVec2(pos.x + button_offset_x, pos.y); - - const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); - if (ColorButton("##ColorButton", col_v4, flags)) - { - if (!(flags & ImGuiColorEditFlags_NoPicker)) - { - // Store current color and open a picker - g.ColorPickerRef = col_v4; - OpenPopup("picker"); - SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y)); - } - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); - - if (BeginPopup("picker")) - { - picker_active_window = g.CurrentWindow; - if (label != label_display_end) - { - TextEx(label, label_display_end); - Spacing(); - } - ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; - ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; - SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? - value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); - EndPopup(); - } - } - - if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) - { - SameLine(0.0f, style.ItemInnerSpacing.x); - TextEx(label, label_display_end); - } - - // Convert back - if (value_changed && picker_active_window == NULL) - { - if (!value_changed_as_float) - for (int n = 0; n < 4; n++) - f[n] = i[n] / 255.0f; - if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB)) - { - g.ColorEditLastHue = f[0]; - g.ColorEditLastSat = f[1]; - ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0)); - } - if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) - ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - - col[0] = f[0]; - col[1] = f[1]; - col[2] = f[2]; - if (alpha) - col[3] = f[3]; - } - - PopID(); - EndGroup(); - - // Drag and Drop Target - // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. - if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) - { - bool accepted_drag_drop = false; - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) - { - memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 - value_changed = accepted_drag_drop = true; - } - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) - { - memcpy((float*)col, payload->Data, sizeof(float) * components); - value_changed = accepted_drag_drop = true; - } - - // Drag-drop payloads are always RGB - if (accepted_drag_drop && (flags & ImGuiColorEditFlags_InputHSV)) - ColorConvertRGBtoHSV(col[0], col[1], col[2], col[0], col[1], col[2]); - EndDragDropTarget(); - } - - // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). - if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) - g.LastItemData.ID = g.ActiveId; - - if (value_changed) - MarkItemEdited(g.LastItemData.ID); - - return value_changed; -} - -bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) -{ - float col4[4] = { col[0], col[1], col[2], 1.0f }; - if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) - return false; - col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; - return true; -} - -// Helper for ColorPicker4() -static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w, float alpha) -{ - ImU32 alpha8 = IM_F32_TO_INT8_SAT(alpha); - ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32(0,0,0,alpha8)); - ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32(255,255,255,alpha8)); - ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32(0,0,0,alpha8)); - ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32(255,255,255,alpha8)); -} - -// Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -// (In C++ the 'float col[4]' notation for a function argument is equivalent to 'float* col', we only specify a size to facilitate understanding of the code.) -// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) -// FIXME: this is trying to be aware of style.Alpha but not fully correct. Also, the color wheel will have overlapping glitches with (style.Alpha < 1.0) -bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImDrawList* draw_list = window->DrawList; - ImGuiStyle& style = g.Style; - ImGuiIO& io = g.IO; - - const float width = CalcItemWidth(); - g.NextItemData.ClearFlags(); - - PushID(label); - BeginGroup(); - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - flags |= ImGuiColorEditFlags_NoSmallPreview; - - // Context menu: display and store options. - if (!(flags & ImGuiColorEditFlags_NoOptions)) - ColorPickerOptionsPopup(col, flags); - - // Read stored options - if (!(flags & ImGuiColorEditFlags_PickerMask_)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_; - if (!(flags & ImGuiColorEditFlags_InputMask_)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected - if (!(flags & ImGuiColorEditFlags_NoOptions)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); - - // Setup - int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; - bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); - ImVec2 picker_pos = window->DC.CursorPos; - float square_sz = GetFrameHeight(); - float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars - float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box - float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; - float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; - float bars_triangles_half_sz = IM_FLOOR(bars_width * 0.20f); - - float backup_initial_col[4]; - memcpy(backup_initial_col, col, components * sizeof(float)); - - float wheel_thickness = sv_picker_size * 0.08f; - float wheel_r_outer = sv_picker_size * 0.50f; - float wheel_r_inner = wheel_r_outer - wheel_thickness; - ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f); - - // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. - float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); - ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. - ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. - ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. - - float H = col[0], S = col[1], V = col[2]; - float R = col[0], G = col[1], B = col[2]; - if (flags & ImGuiColorEditFlags_InputRGB) - { - // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. - ColorConvertRGBtoHSV(R, G, B, H, S, V); - ColorEditRestoreHS(col, &H, &S, &V); - } - else if (flags & ImGuiColorEditFlags_InputHSV) - { - ColorConvertHSVtoRGB(H, S, V, R, G, B); - } - - bool value_changed = false, value_changed_h = false, value_changed_sv = false; - - PushItemFlag(ImGuiItemFlags_NoNav, true); - if (flags & ImGuiColorEditFlags_PickerHueWheel) - { - // Hue wheel + SV triangle logic - InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); - if (IsItemActive()) - { - ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; - ImVec2 current_off = g.IO.MousePos - wheel_center; - float initial_dist2 = ImLengthSqr(initial_off); - if (initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1)) - { - // Interactive with Hue wheel - H = ImAtan2(current_off.y, current_off.x) / IM_PI * 0.5f; - if (H < 0.0f) - H += 1.0f; - value_changed = value_changed_h = true; - } - float cos_hue_angle = ImCos(-H * 2.0f * IM_PI); - float sin_hue_angle = ImSin(-H * 2.0f * IM_PI); - if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) - { - // Interacting with SV triangle - ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); - if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) - current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); - float uu, vv, ww; - ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); - V = ImClamp(1.0f - vv, 0.0001f, 1.0f); - S = ImClamp(uu / V, 0.0001f, 1.0f); - value_changed = value_changed_sv = true; - } - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); - } - else if (flags & ImGuiColorEditFlags_PickerHueBar) - { - // SV rectangle logic - InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); - if (IsItemActive()) - { - S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1)); - V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); - - // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square. - if (g.ColorEditLastColor == ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) - H = g.ColorEditLastHue; - value_changed = value_changed_sv = true; - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); - - // Hue bar logic - SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); - InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); - if (IsItemActive()) - { - H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); - value_changed = value_changed_h = true; - } - } - - // Alpha bar logic - if (alpha_bar) - { - SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); - InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); - if (IsItemActive()) - { - col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); - value_changed = true; - } - } - PopItemFlag(); // ImGuiItemFlags_NoNav - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - { - SameLine(0, style.ItemInnerSpacing.x); - BeginGroup(); - } - - if (!(flags & ImGuiColorEditFlags_NoLabel)) - { - const char* label_display_end = FindRenderedTextEnd(label); - if (label != label_display_end) - { - if ((flags & ImGuiColorEditFlags_NoSidePreview)) - SameLine(0, style.ItemInnerSpacing.x); - TextEx(label, label_display_end); - } - } - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - { - PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); - ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); - if ((flags & ImGuiColorEditFlags_NoLabel)) - Text("Current"); - - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; - ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); - if (ref_col != NULL) - { - Text("Original"); - ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); - if (ColorButton("##original", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2))) - { - memcpy(col, ref_col, components * sizeof(float)); - value_changed = true; - } - } - PopItemFlag(); - EndGroup(); - } - - // Convert back color to RGB - if (value_changed_h || value_changed_sv) - { - if (flags & ImGuiColorEditFlags_InputRGB) - { - ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]); - g.ColorEditLastHue = H; - g.ColorEditLastSat = S; - g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); - } - else if (flags & ImGuiColorEditFlags_InputHSV) - { - col[0] = H; - col[1] = S; - col[2] = V; - } - } - - // R,G,B and H,S,V slider color editor - bool value_changed_fix_hue_wrap = false; - if ((flags & ImGuiColorEditFlags_NoInputs) == 0) - { - PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; - ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; - if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) - if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) - { - // FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. - // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050) - value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); - value_changed = true; - } - if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) - value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV); - if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) - value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex); - PopItemWidth(); - } - - // Try to cancel hue wrap (after ColorEdit4 call), if any - if (value_changed_fix_hue_wrap && (flags & ImGuiColorEditFlags_InputRGB)) - { - float new_H, new_S, new_V; - ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); - if (new_H <= 0 && H > 0) - { - if (new_V <= 0 && V != new_V) - ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); - else if (new_S <= 0) - ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); - } - } - - if (value_changed) - { - if (flags & ImGuiColorEditFlags_InputRGB) - { - R = col[0]; - G = col[1]; - B = col[2]; - ColorConvertRGBtoHSV(R, G, B, H, S, V); - ColorEditRestoreHS(col, &H, &S, &V); // Fix local Hue as display below will use it immediately. - } - else if (flags & ImGuiColorEditFlags_InputHSV) - { - H = col[0]; - S = col[1]; - V = col[2]; - ColorConvertHSVtoRGB(H, S, V, R, G, B); - } - } - - const int style_alpha8 = IM_F32_TO_INT8_SAT(style.Alpha); - const ImU32 col_black = IM_COL32(0,0,0,style_alpha8); - const ImU32 col_white = IM_COL32(255,255,255,style_alpha8); - const ImU32 col_midgrey = IM_COL32(128,128,128,style_alpha8); - const ImU32 col_hues[6 + 1] = { IM_COL32(255,0,0,style_alpha8), IM_COL32(255,255,0,style_alpha8), IM_COL32(0,255,0,style_alpha8), IM_COL32(0,255,255,style_alpha8), IM_COL32(0,0,255,style_alpha8), IM_COL32(255,0,255,style_alpha8), IM_COL32(255,0,0,style_alpha8) }; - - ImVec4 hue_color_f(1, 1, 1, style.Alpha); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); - ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); - ImU32 user_col32_striped_of_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, style.Alpha)); // Important: this is still including the main rendering/style alpha!! - - ImVec2 sv_cursor_pos; - - if (flags & ImGuiColorEditFlags_PickerHueWheel) - { - // Render Hue Wheel - const float aeps = 0.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). - const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); - for (int n = 0; n < 6; n++) - { - const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps; - const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; - const int vert_start_idx = draw_list->VtxBuffer.Size; - draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); - draw_list->PathStroke(col_white, 0, wheel_thickness); - const int vert_end_idx = draw_list->VtxBuffer.Size; - - // Paint colors over existing vertices - ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner); - ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner); - ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n + 1]); - } - - // Render Cursor + preview on Hue Wheel - float cos_hue_angle = ImCos(H * 2.0f * IM_PI); - float sin_hue_angle = ImSin(H * 2.0f * IM_PI); - ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f); - float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; - int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32); - draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); - draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments); - draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments); - - // Render SV triangle (rotated according to hue) - ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); - ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); - ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); - ImVec2 uv_white = GetFontTexUvWhitePixel(); - draw_list->PrimReserve(6, 6); - draw_list->PrimVtx(tra, uv_white, hue_color32); - draw_list->PrimVtx(trb, uv_white, hue_color32); - draw_list->PrimVtx(trc, uv_white, col_white); - draw_list->PrimVtx(tra, uv_white, 0); - draw_list->PrimVtx(trb, uv_white, col_black); - draw_list->PrimVtx(trc, uv_white, 0); - draw_list->AddTriangle(tra, trb, trc, col_midgrey, 1.5f); - sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); - } - else if (flags & ImGuiColorEditFlags_PickerHueBar) - { - // Render SV Square - draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), col_white, hue_color32, hue_color32, col_white); - draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0, 0, col_black, col_black); - RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0f); - sv_cursor_pos.x = ImClamp(IM_ROUND(picker_pos.x + ImSaturate(S) * sv_picker_size), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much - sv_cursor_pos.y = ImClamp(IM_ROUND(picker_pos.y + ImSaturate(1 - V) * sv_picker_size), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); - - // Render Hue Bar - for (int i = 0; i < 6; ++i) - draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), col_hues[i], col_hues[i], col_hues[i + 1], col_hues[i + 1]); - float bar0_line_y = IM_ROUND(picker_pos.y + H * sv_picker_size); - RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); - RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha); - } - - // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) - float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f; - draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, 12); - - // Render alpha bar - if (alpha_bar) - { - float alpha = ImSaturate(col[3]); - ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); - RenderColorRectWithAlphaCheckerboard(draw_list, bar1_bb.Min, bar1_bb.Max, 0, bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); - draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, user_col32_striped_of_alpha, user_col32_striped_of_alpha, user_col32_striped_of_alpha & ~IM_COL32_A_MASK, user_col32_striped_of_alpha & ~IM_COL32_A_MASK); - float bar1_line_y = IM_ROUND(picker_pos.y + (1.0f - alpha) * sv_picker_size); - RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); - RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha); - } - - EndGroup(); - - if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) - value_changed = false; - if (value_changed) - MarkItemEdited(g.LastItemData.ID); - - PopID(); - - return value_changed; -} - -// A little color square. Return true when clicked. -// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. -// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. -// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set. -bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiID id = window->GetID(desc_id); - const float default_size = GetFrameHeight(); - const ImVec2 size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - if (flags & ImGuiColorEditFlags_NoAlpha) - flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); - - ImVec4 col_rgb = col; - if (flags & ImGuiColorEditFlags_InputHSV) - ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.x, col_rgb.y, col_rgb.z); - - ImVec4 col_rgb_without_alpha(col_rgb.x, col_rgb.y, col_rgb.z, 1.0f); - float grid_step = ImMin(size.x, size.y) / 2.99f; - float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); - ImRect bb_inner = bb; - float off = 0.0f; - if ((flags & ImGuiColorEditFlags_NoBorder) == 0) - { - off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. - bb_inner.Expand(off); - } - if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f) - { - float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f); - RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight); - window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft); - } - else - { - // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha - ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col_rgb : col_rgb_without_alpha; - if (col_source.w < 1.0f) - RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); - else - window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding); - } - RenderNavHighlight(bb, id); - if ((flags & ImGuiColorEditFlags_NoBorder) == 0) - { - if (g.Style.FrameBorderSize > 0.0f) - RenderFrameBorder(bb.Min, bb.Max, rounding); - else - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border - } - - // Drag and Drop Source - // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test. - if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource()) - { - if (flags & ImGuiColorEditFlags_NoAlpha) - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb, sizeof(float) * 3, ImGuiCond_Once); - else - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb, sizeof(float) * 4, ImGuiCond_Once); - ColorButton(desc_id, col, flags); - SameLine(); - TextEx("Color"); - EndDragDropSource(); - } - - // Tooltip - if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); - - return pressed; -} - -// Initialize/override default color options -void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) -{ - ImGuiContext& g = *GImGui; - if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0) - flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_; - if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0) - flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_; - if ((flags & ImGuiColorEditFlags_PickerMask_) == 0) - flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_; - if ((flags & ImGuiColorEditFlags_InputMask_) == 0) - flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check only 1 option is selected - g.ColorEditOptions = flags; -} - -// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) -{ - ImGuiContext& g = *GImGui; - - BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); - const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; - if (text_end > text) - { - TextEx(text, text_end); - Separator(); - } - - ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); - ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); - int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); - SameLine(); - if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_)) - { - if (flags & ImGuiColorEditFlags_NoAlpha) - Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); - else - Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); - } - else if (flags & ImGuiColorEditFlags_InputHSV) - { - if (flags & ImGuiColorEditFlags_NoAlpha) - Text("H: %.3f, S: %.3f, V: %.3f", col[0], col[1], col[2]); - else - Text("H: %.3f, S: %.3f, V: %.3f, A: %.3f", col[0], col[1], col[2], col[3]); - } - EndTooltip(); -} - -void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) -{ - bool allow_opt_inputs = !(flags & ImGuiColorEditFlags_DisplayMask_); - bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_); - if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) - return; - ImGuiContext& g = *GImGui; - ImGuiColorEditFlags opts = g.ColorEditOptions; - if (allow_opt_inputs) - { - if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB; - if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHSV; - if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHex; - } - if (allow_opt_datatype) - { - if (allow_opt_inputs) Separator(); - if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8; - if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float; - } - - if (allow_opt_inputs || allow_opt_datatype) - Separator(); - if (Button("Copy as..", ImVec2(-1, 0))) - OpenPopup("Copy"); - if (BeginPopup("Copy")) - { - int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - char buf[64]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); - if (Selectable(buf)) - SetClipboardText(buf); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); - if (Selectable(buf)) - SetClipboardText(buf); - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb); - if (Selectable(buf)) - SetClipboardText(buf); - if (!(flags & ImGuiColorEditFlags_NoAlpha)) - { - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", cr, cg, cb, ca); - if (Selectable(buf)) - SetClipboardText(buf); - } - EndPopup(); - } - - g.ColorEditOptions = opts; - EndPopup(); -} - -void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) -{ - bool allow_opt_picker = !(flags & ImGuiColorEditFlags_PickerMask_); - bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); - if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context")) - return; - ImGuiContext& g = *GImGui; - if (allow_opt_picker) - { - ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function - PushItemWidth(picker_size.x); - for (int picker_type = 0; picker_type < 2; picker_type++) - { - // Draw small/thumbnail version of each picker type (over an invisible button for selection) - if (picker_type > 0) Separator(); - PushID(picker_type); - ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoSidePreview | (flags & ImGuiColorEditFlags_NoAlpha); - if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; - if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; - ImVec2 backup_pos = GetCursorScreenPos(); - if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup - g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_); - SetCursorScreenPos(backup_pos); - ImVec4 previewing_ref_col; - memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); - ColorPicker4("##previewing_picker", &previewing_ref_col.x, picker_flags); - PopID(); - } - PopItemWidth(); - } - if (allow_opt_alpha_bar) - { - if (allow_opt_picker) Separator(); - CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); - } - EndPopup(); -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: TreeNode, CollapsingHeader, etc. -//------------------------------------------------------------------------- -// - TreeNode() -// - TreeNodeV() -// - TreeNodeEx() -// - TreeNodeExV() -// - TreeNodeBehavior() [Internal] -// - TreePush() -// - TreePop() -// - GetTreeNodeToLabelSpacing() -// - SetNextItemOpen() -// - CollapsingHeader() -//------------------------------------------------------------------------- - -bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(str_id, 0, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const char* label) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - return TreeNodeBehavior(window->GetID(label), 0, label, NULL); -} - -bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) -{ - return TreeNodeExV(str_id, 0, fmt, args); -} - -bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) -{ - return TreeNodeExV(ptr_id, 0, fmt, args); -} - -bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - return TreeNodeBehavior(window->GetID(label), flags, label, NULL); -} - -bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(str_id, flags, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - const char* label, *label_end; - ImFormatStringToTempBufferV(&label, &label_end, fmt, args); - return TreeNodeBehavior(window->GetID(str_id), flags, label, label_end); -} - -bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - const char* label, *label_end; - ImFormatStringToTempBufferV(&label, &label_end, fmt, args); - return TreeNodeBehavior(window->GetID(ptr_id), flags, label, label_end); -} - -bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) -{ - if (flags & ImGuiTreeNodeFlags_Leaf) - return true; - - // We only write to the tree storage if the user clicks (or explicitly use the SetNextItemOpen function) - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiStorage* storage = window->DC.StateStorage; - - bool is_open; - if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasOpen) - { - if (g.NextItemData.OpenCond & ImGuiCond_Always) - { - is_open = g.NextItemData.OpenVal; - storage->SetInt(id, is_open); - } - else - { - // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. - const int stored_value = storage->GetInt(id, -1); - if (stored_value == -1) - { - is_open = g.NextItemData.OpenVal; - storage->SetInt(id, is_open); - } - else - { - is_open = stored_value != 0; - } - } - } - else - { - is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; - } - - // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). - // NB- If we are above max depth we still allow manually opened nodes to be logged. - if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && (window->DC.TreeDepth - g.LogDepthRef) < g.LogDepthToExpand) - is_open = true; - - return is_open; -} - -bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; - const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); - - if (!label_end) - label_end = FindRenderedTextEnd(label); - const ImVec2 label_size = CalcTextSize(label, label_end, false); - - // We vertically grow up to current line height up the typical widget height. - const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2); - ImRect frame_bb; - frame_bb.Min.x = (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; - frame_bb.Min.y = window->DC.CursorPos.y; - frame_bb.Max.x = window->WorkRect.Max.x; - frame_bb.Max.y = window->DC.CursorPos.y + frame_height; - if (display_frame) - { - // Framed header expand a little outside the default padding, to the edge of InnerClipRect - // (FIXME: May remove this at some point and make InnerClipRect align with WindowPadding.x instead of WindowPadding.x*0.5f) - frame_bb.Min.x -= IM_FLOOR(window->WindowPadding.x * 0.5f - 1.0f); - frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f); - } - - const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapser arrow width + Spacing - const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it - const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f); // Include collapser - ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y); - ItemSize(ImVec2(text_width, frame_height), padding.y); - - // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing - ImRect interact_bb = frame_bb; - if (!display_frame && (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth)) == 0) - interact_bb.Max.x = frame_bb.Min.x + text_width + style.ItemSpacing.x * 2.0f; - - // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. - // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). - // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. - const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; - bool is_open = TreeNodeBehaviorIsOpen(id, flags); - if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); - - bool item_add = ItemAdd(interact_bb, id); - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; - g.LastItemData.DisplayRect = frame_bb; - - if (!item_add) - { - if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushOverrideID(id); - IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); - return is_open; - } - - ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None; - if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) - button_flags |= ImGuiButtonFlags_AllowItemOverlap; - if (!is_leaf) - button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; - - // We allow clicking on the arrow section with keyboard modifiers held, in order to easily - // allow browsing a tree while preserving selection with code implementing multi-selection patterns. - // When clicking on the rest of the tree node we always disallow keyboard modifiers. - const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x; - const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x; - const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2); - if (window != g.HoveredWindow || !is_mouse_x_over_arrow) - button_flags |= ImGuiButtonFlags_NoKeyModifiers; - - // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags. - // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support. - // - Single-click on label = Toggle on MouseUp (default, when _OpenOnArrow=0) - // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=0) - // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1) - // - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1) - // - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and _OpenOnArrow=0) - // It is rather standard that arrow click react on Down rather than Up. - // We set ImGuiButtonFlags_PressedOnClickRelease on OpenOnDoubleClick because we want the item to be active on the initial MouseDown in order for drag and drop to work. - if (is_mouse_x_over_arrow) - button_flags |= ImGuiButtonFlags_PressedOnClick; - else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) - button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; - else - button_flags |= ImGuiButtonFlags_PressedOnClickRelease; - - bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0; - const bool was_selected = selected; - - bool hovered, held; - bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); - bool toggled = false; - if (!is_leaf) - { - if (pressed && g.DragDropHoldJustPressedId != id) - { - if ((flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) == 0 || (g.NavActivateId == id)) - toggled = true; - if (flags & ImGuiTreeNodeFlags_OpenOnArrow) - toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job - if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) - toggled = true; - } - else if (pressed && g.DragDropHoldJustPressedId == id) - { - IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold); - if (!is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. - toggled = true; - } - - if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) - { - toggled = true; - NavMoveRequestCancel(); - } - if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? - { - toggled = true; - NavMoveRequestCancel(); - } - - if (toggled) - { - is_open = !is_open; - window->DC.StateStorage->SetInt(id, is_open); - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; - } - } - if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) - SetItemAllowOverlap(); - - // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. - if (selected != was_selected) //-V547 - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; - - // Render - const ImU32 text_col = GetColorU32(ImGuiCol_Text); - ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_TypeThin; - if (display_frame) - { - // Framed type - const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); - RenderNavHighlight(frame_bb, id, nav_highlight_flags); - if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col); - else if (!is_leaf) - RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); - else // Leaf without bullet, left-adjusted text - text_pos.x -= text_offset_x; - if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) - frame_bb.Max.x -= g.FontSize + style.FramePadding.x; - - if (g.LogEnabled) - LogSetNextTextDecoration("###", "###"); - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - } - else - { - // Unframed typed for tree nodes - if (hovered || selected) - { - const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); - } - RenderNavHighlight(frame_bb, id, nav_highlight_flags); - if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); - else if (!is_leaf) - RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); - if (g.LogEnabled) - LogSetNextTextDecoration(">", NULL); - RenderText(text_pos, label, label_end, false); - } - - if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushOverrideID(id); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); - return is_open; -} - -void ImGui::TreePush(const char* str_id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - PushID(str_id); -} - -void ImGui::TreePush(const void* ptr_id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - PushID(ptr_id); -} - -void ImGui::TreePushOverrideID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - Indent(); - window->DC.TreeDepth++; - PushOverrideID(id); -} - -void ImGui::TreePop() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - Unindent(); - - window->DC.TreeDepth--; - ImU32 tree_depth_mask = (1 << window->DC.TreeDepth); - - // Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled) - if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) - if (g.NavIdIsAlive && (window->DC.TreeJumpToParentOnPopMask & tree_depth_mask)) - { - SetNavID(window->IDStack.back(), g.NavLayer, 0, ImRect()); - NavMoveRequestCancel(); - } - window->DC.TreeJumpToParentOnPopMask &= tree_depth_mask - 1; - - IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much. - PopID(); -} - -// Horizontal distance preceding label when using TreeNode() or Bullet() -float ImGui::GetTreeNodeToLabelSpacing() -{ - ImGuiContext& g = *GImGui; - return g.FontSize + (g.Style.FramePadding.x * 2.0f); -} - -// Set next TreeNode/CollapsingHeader open state. -void ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - if (g.CurrentWindow->SkipItems) - return; - g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasOpen; - g.NextItemData.OpenVal = is_open; - g.NextItemData.OpenCond = cond ? cond : ImGuiCond_Always; -} - -// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). -// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). -bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label); -} - -// p_visible == NULL : regular collapsing header -// p_visible != NULL && *p_visible == true : show a small close button on the corner of the header, clicking the button will set *p_visible = false -// p_visible != NULL && *p_visible == false : do not show the header at all -// Do not mistake this with the Open state of the header itself, which you can adjust with SetNextItemOpen() or ImGuiTreeNodeFlags_DefaultOpen. -bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - if (p_visible && !*p_visible) - return false; - - ImGuiID id = window->GetID(label); - flags |= ImGuiTreeNodeFlags_CollapsingHeader; - if (p_visible) - flags |= ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_ClipLabelForTrailingButton; - bool is_open = TreeNodeBehavior(id, flags, label); - if (p_visible != NULL) - { - // Create a small overlapping close button - // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. - // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow. - ImGuiContext& g = *GImGui; - ImGuiLastItemData last_item_backup = g.LastItemData; - float button_size = g.FontSize; - float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x * 2.0f - button_size); - float button_y = g.LastItemData.Rect.Min.y; - ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id); - if (CloseButton(close_button_id, ImVec2(button_x, button_y))) - *p_visible = false; - g.LastItemData = last_item_backup; - } - - return is_open; -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: Selectable -//------------------------------------------------------------------------- -// - Selectable() -//------------------------------------------------------------------------- - -// Tip: pass a non-visible label (e.g. "##hello") then you can use the space to draw other text or image. -// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id. -// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowItemOverlap are also frequently used flags. -// FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported. -bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle. - ImGuiID id = window->GetID(label); - ImVec2 label_size = CalcTextSize(label, NULL, true); - ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); - ImVec2 pos = window->DC.CursorPos; - pos.y += window->DC.CurrLineTextBaseOffset; - ItemSize(size, 0.0f); - - // Fill horizontal space - // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets. - const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0; - const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x; - const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; - if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) - size.x = ImMax(label_size.x, max_x - min_x); - - // Text stays at the submission position, but bounding box may be extended on both sides - const ImVec2 text_min = pos; - const ImVec2 text_max(min_x + size.x, pos.y + size.y); - - // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. - ImRect bb(min_x, pos.y, text_max.x, text_max.y); - if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) - { - const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; - const float spacing_y = style.ItemSpacing.y; - const float spacing_L = IM_FLOOR(spacing_x * 0.50f); - const float spacing_U = IM_FLOOR(spacing_y * 0.50f); - bb.Min.x -= spacing_L; - bb.Min.y -= spacing_U; - bb.Max.x += (spacing_x - spacing_L); - bb.Max.y += (spacing_y - spacing_U); - } - //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); } - - // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable.. - const float backup_clip_rect_min_x = window->ClipRect.Min.x; - const float backup_clip_rect_max_x = window->ClipRect.Max.x; - if (span_all_columns) - { - window->ClipRect.Min.x = window->ParentWorkRect.Min.x; - window->ClipRect.Max.x = window->ParentWorkRect.Max.x; - } - - const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; - const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? ImGuiItemFlags_Disabled : ImGuiItemFlags_None); - if (span_all_columns) - { - window->ClipRect.Min.x = backup_clip_rect_min_x; - window->ClipRect.Max.x = backup_clip_rect_max_x; - } - - if (!item_add) - return false; - - const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; - if (disabled_item && !disabled_global) // Only testing this as an optimization - BeginDisabled(); - - // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, - // which would be advantageous since most selectable are not selected. - if (span_all_columns && window->DC.CurrentColumns) - PushColumnsBackground(); - else if (span_all_columns && g.CurrentTable) - TablePushBackgroundChannel(); - - // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries - ImGuiButtonFlags button_flags = 0; - if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } - if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } - if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } - if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } - if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } - - const bool was_selected = selected; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); - - // Auto-select when moved into - // - This will be more fully fleshed in the range-select branch - // - This is not exposed as it won't nicely work with some user side handling of shift/control - // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons - // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) - // - (2) usage will fail with clipped items - // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. - if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent) - if (g.NavJustMovedToId == id) - selected = pressed = true; - - // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard - if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) - { - if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) - { - SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, WindowRectAbsToRel(window, bb)); // (bb == NavRect) - g.NavDisableHighlight = true; - } - } - if (pressed) - MarkItemEdited(id); - - if (flags & ImGuiSelectableFlags_AllowItemOverlap) - SetItemAllowOverlap(); - - // In this branch, Selectable() cannot toggle the selection so this will never trigger. - if (selected != was_selected) //-V547 - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; - - // Render - if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) - hovered = true; - if (hovered || selected) - { - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(bb.Min, bb.Max, col, false, 0.0f); - } - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); - - if (span_all_columns && window->DC.CurrentColumns) - PopColumnsBackground(); - else if (span_all_columns && g.CurrentTable) - TablePopBackgroundChannel(); - - RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); - - // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup)) - CloseCurrentPopup(); - - if (disabled_item && !disabled_global) - EndDisabled(); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); - return pressed; //-V1020 -} - -bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) -{ - if (Selectable(label, *p_selected, flags, size_arg)) - { - *p_selected = !*p_selected; - return true; - } - return false; -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: ListBox -//------------------------------------------------------------------------- -// - BeginListBox() -// - EndListBox() -// - ListBox() -//------------------------------------------------------------------------- - -// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty" -// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height). -bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - const ImGuiStyle& style = g.Style; - const ImGuiID id = GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - // Size default to hold ~7.25 items. - // Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. - ImVec2 size = ImFloor(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f)); - ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); - ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); - ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - g.NextItemData.ClearFlags(); - - if (!IsRectVisible(bb.Min, bb.Max)) - { - ItemSize(bb.GetSize(), style.FramePadding.y); - ItemAdd(bb, 0, &frame_bb); - return false; - } - - // FIXME-OPT: We could omit the BeginGroup() if label_size.x but would need to omit the EndGroup() as well. - BeginGroup(); - if (label_size.x > 0.0f) - { - ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y); - RenderText(label_pos, label); - window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size); - } - - BeginChildFrame(id, frame_bb.GetSize()); - return true; -} - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -// OBSOLETED in 1.81 (from February 2021) -bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) -{ - // If height_in_items == -1, default height is maximum 7. - ImGuiContext& g = *GImGui; - float height_in_items_f = (height_in_items < 0 ? ImMin(items_count, 7) : height_in_items) + 0.25f; - ImVec2 size; - size.x = 0.0f; - size.y = GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f; - return BeginListBox(label, size); -} -#endif - -void ImGui::EndListBox() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && "Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?"); - IM_UNUSED(window); - - EndChildFrame(); - EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label -} - -bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) -{ - const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); - return value_changed; -} - -// This is merely a helper around BeginListBox(), EndListBox(). -// Considering using those directly to submit custom data or store selection differently. -bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) -{ - ImGuiContext& g = *GImGui; - - // Calculate size from "height_in_items" - if (height_in_items < 0) - height_in_items = ImMin(items_count, 7); - float height_in_items_f = height_in_items + 0.25f; - ImVec2 size(0.0f, ImFloor(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f)); - - if (!BeginListBox(label, size)) - return false; - - // Assume all items have even height (= 1 line of text). If you need items of different height, - // you can create a custom version of ListBox() in your code without using the clipper. - bool value_changed = false; - ImGuiListClipper clipper; - clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. - while (clipper.Step()) - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - { - const char* item_text; - if (!items_getter(data, i, &item_text)) - item_text = "*Unknown item*"; - - PushID(i); - const bool item_selected = (i == *current_item); - if (Selectable(item_text, item_selected)) - { - *current_item = i; - value_changed = true; - } - if (item_selected) - SetItemDefaultFocus(); - PopID(); - } - EndListBox(); - - if (value_changed) - MarkItemEdited(g.LastItemData.ID); - - return value_changed; -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: PlotLines, PlotHistogram -//------------------------------------------------------------------------- -// - PlotEx() [Internal] -// - PlotLines() -// - PlotHistogram() -//------------------------------------------------------------------------- -// Plot/Graph widgets are not very good. -// Consider writing your own, or using a third-party one, see: -// - ImPlot https://github.com/epezent/implot -// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions -//------------------------------------------------------------------------- - -int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return -1; - - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - if (frame_size.x == 0.0f) - frame_size.x = CalcItemWidth(); - if (frame_size.y == 0.0f) - frame_size.y = label_size.y + (style.FramePadding.y * 2); - - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); - const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, 0, &frame_bb)) - return -1; - const bool hovered = ItemHoverable(frame_bb, id); - - // Determine scale from values if not specified - if (scale_min == FLT_MAX || scale_max == FLT_MAX) - { - float v_min = FLT_MAX; - float v_max = -FLT_MAX; - for (int i = 0; i < values_count; i++) - { - const float v = values_getter(data, i); - if (v != v) // Ignore NaN values - continue; - v_min = ImMin(v_min, v); - v_max = ImMax(v_max, v); - } - if (scale_min == FLT_MAX) - scale_min = v_min; - if (scale_max == FLT_MAX) - scale_max = v_max; - } - - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - - const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1; - int idx_hovered = -1; - if (values_count >= values_count_min) - { - int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); - int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); - - // Tooltip on hover - if (hovered && inner_bb.Contains(g.IO.MousePos)) - { - const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); - const int v_idx = (int)(t * item_count); - IM_ASSERT(v_idx >= 0 && v_idx < values_count); - - const float v0 = values_getter(data, (v_idx + values_offset) % values_count); - const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); - if (plot_type == ImGuiPlotType_Lines) - SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1); - else if (plot_type == ImGuiPlotType_Histogram) - SetTooltip("%d: %8.4g", v_idx, v0); - idx_hovered = v_idx; - } - - const float t_step = 1.0f / (float)res_w; - const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); - - float v0 = values_getter(data, (0 + values_offset) % values_count); - float t0 = 0.0f; - ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle - float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands - - const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); - const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); - - for (int n = 0; n < res_w; n++) - { - const float t1 = t0 + t_step; - const int v1_idx = (int)(t0 * item_count + 0.5f); - IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); - const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); - const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) ); - - // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. - ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); - ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t)); - if (plot_type == ImGuiPlotType_Lines) - { - window->DrawList->AddLine(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base); - } - else if (plot_type == ImGuiPlotType_Histogram) - { - if (pos1.x >= pos0.x + 2.0f) - pos1.x -= 1.0f; - window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base); - } - - t0 = t1; - tp0 = tp1; - } - } - - // Text overlay - if (overlay_text) - RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); - - // Return hovered index or -1 if none are hovered. - // This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx(). - return idx_hovered; -} - -struct ImGuiPlotArrayGetterData -{ - const float* Values; - int Stride; - - ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } -}; - -static float Plot_ArrayGetter(void* data, int idx) -{ - ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; - const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); - return v; -} - -void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) -{ - ImGuiPlotArrayGetterData data(values, stride); - PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) -{ - ImGuiPlotArrayGetterData data(values, stride); - PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: Value helpers -// Those is not very useful, legacy API. -//------------------------------------------------------------------------- -// - Value() -//------------------------------------------------------------------------- - -void ImGui::Value(const char* prefix, bool b) -{ - Text("%s: %s", prefix, (b ? "true" : "false")); -} - -void ImGui::Value(const char* prefix, int v) -{ - Text("%s: %d", prefix, v); -} - -void ImGui::Value(const char* prefix, unsigned int v) -{ - Text("%s: %d", prefix, v); -} - -void ImGui::Value(const char* prefix, float v, const char* float_format) -{ - if (float_format) - { - char fmt[64]; - ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); - Text(fmt, prefix, v); - } - else - { - Text("%s: %.3f", prefix, v); - } -} - -//------------------------------------------------------------------------- -// [SECTION] MenuItem, BeginMenu, EndMenu, etc. -//------------------------------------------------------------------------- -// - ImGuiMenuColumns [Internal] -// - BeginMenuBar() -// - EndMenuBar() -// - BeginMainMenuBar() -// - EndMainMenuBar() -// - BeginMenu() -// - EndMenu() -// - MenuItemEx() [Internal] -// - MenuItem() -//------------------------------------------------------------------------- - -// Helpers for internal use -void ImGuiMenuColumns::Update(float spacing, bool window_reappearing) -{ - if (window_reappearing) - memset(Widths, 0, sizeof(Widths)); - Spacing = (ImU16)spacing; - CalcNextTotalWidth(true); - memset(Widths, 0, sizeof(Widths)); - TotalWidth = NextTotalWidth; - NextTotalWidth = 0; -} - -void ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets) -{ - ImU16 offset = 0; - bool want_spacing = false; - for (int i = 0; i < IM_ARRAYSIZE(Widths); i++) - { - ImU16 width = Widths[i]; - if (want_spacing && width > 0) - offset += Spacing; - want_spacing |= (width > 0); - if (update_offsets) - { - if (i == 1) { OffsetLabel = offset; } - if (i == 2) { OffsetShortcut = offset; } - if (i == 3) { OffsetMark = offset; } - } - offset += width; - } - NextTotalWidth = offset; -} - -float ImGuiMenuColumns::DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark) -{ - Widths[0] = ImMax(Widths[0], (ImU16)w_icon); - Widths[1] = ImMax(Widths[1], (ImU16)w_label); - Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut); - Widths[3] = ImMax(Widths[3], (ImU16)w_mark); - CalcNextTotalWidth(false); - return (float)ImMax(TotalWidth, NextTotalWidth); -} - -// FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere.. -// Currently the main responsibility of this function being to setup clip-rect + horizontal layout + menu navigation layer. -// Ideally we also want this to be responsible for claiming space out of the main window scrolling rectangle, in which case ImGuiWindowFlags_MenuBar will become unnecessary. -// Then later the same system could be used for multiple menu-bars, scrollbars, side-bars. -bool ImGui::BeginMenuBar() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - if (!(window->Flags & ImGuiWindowFlags_MenuBar)) - return false; - - IM_ASSERT(!window->DC.MenuBarAppending); - BeginGroup(); // Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore - PushID("##menubar"); - - // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. - // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. - ImRect bar_rect = window->MenuBarRect(); - ImRect clip_rect(IM_ROUND(bar_rect.Min.x + window->WindowBorderSize), IM_ROUND(bar_rect.Min.y + window->WindowBorderSize), IM_ROUND(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, window->WindowBorderSize))), IM_ROUND(bar_rect.Max.y)); - clip_rect.ClipWith(window->OuterRectClipped); - PushClipRect(clip_rect.Min, clip_rect.Max, false); - - // We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analogous here, maybe a BeginGroupEx() with flags). - window->DC.CursorPos = window->DC.CursorMaxPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); - window->DC.LayoutType = ImGuiLayoutType_Horizontal; - window->DC.IsSameLine = false; - window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; - window->DC.MenuBarAppending = true; - AlignTextToFramePadding(); - return true; -} - -void ImGui::EndMenuBar() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. - if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - { - // Try to find out if the request is for one of our child menu - ImGuiWindow* nav_earliest_child = g.NavWindow; - while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) - nav_earliest_child = nav_earliest_child->ParentWindow; - if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) - { - // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. - // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) - const ImGuiNavLayer layer = ImGuiNavLayer_Menu; - IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check - FocusWindow(window); - SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); - g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. - g.NavDisableMouseHover = g.NavMousePosDirty = true; - NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat - } - } - - IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" - IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); - IM_ASSERT(window->DC.MenuBarAppending); - PopClipRect(); - PopID(); - window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. - g.GroupStack.back().EmitItem = false; - EndGroup(); // Restore position on layer 0 - window->DC.LayoutType = ImGuiLayoutType_Vertical; - window->DC.IsSameLine = false; - window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.MenuBarAppending = false; -} - -// Important: calling order matters! -// FIXME: Somehow overlapping with docking tech. -// FIXME: The "rect-cut" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts) -bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags) -{ - IM_ASSERT(dir != ImGuiDir_None); - - ImGuiWindow* bar_window = FindWindowByName(name); - if (bar_window == NULL || bar_window->BeginCount == 0) - { - // Calculate and set window size/position - ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport()); - ImRect avail_rect = viewport->GetBuildWorkRect(); - ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; - ImVec2 pos = avail_rect.Min; - if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) - pos[axis] = avail_rect.Max[axis] - axis_size; - ImVec2 size = avail_rect.GetSize(); - size[axis] = axis_size; - SetNextWindowPos(pos); - SetNextWindowSize(size); - - // Report our size into work area (for next frame) using actual window size - if (dir == ImGuiDir_Up || dir == ImGuiDir_Left) - viewport->BuildWorkOffsetMin[axis] += axis_size; - else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right) - viewport->BuildWorkOffsetMax[axis] -= axis_size; - } - - window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; - PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint - bool is_open = Begin(name, NULL, window_flags); - PopStyleVar(2); - - return is_open; -} - -bool ImGui::BeginMainMenuBar() -{ - ImGuiContext& g = *GImGui; - ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); - - // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. - // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea? - // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings. - g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; - float height = GetFrameHeight(); - bool is_open = BeginViewportSideBar("##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags); - g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); - - if (is_open) - BeginMenuBar(); - else - End(); - return is_open; -} - -void ImGui::EndMainMenuBar() -{ - EndMenuBar(); - - // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window - // FIXME: With this strategy we won't be able to restore a NULL focus. - ImGuiContext& g = *GImGui; - if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest) - FocusTopMostWindowUnderOne(g.NavWindow, NULL); - - End(); -} - -static bool IsRootOfOpenMenuSet() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu)) - return false; - - // Initially we used 'upper_popup->OpenParentId == window->IDStack.back()' to differentiate multiple menu sets from each others - // (e.g. inside menu bar vs loose menu items) based on parent ID. - // This would however prevent the use of e.g. PuhsID() user code submitting menus. - // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, - // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. - // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup - // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first child menu. - // In the end, lack of ID check made it so we could no longer differentiate between separate menu sets. To compensate for that, we at least check parent window nav layer. - // This fixes the most common case of menu opening on hover when moving between window content and menu bar. Multiple different menu sets in same nav layer would still - // open on hover, but that should be a lesser problem, because if such menus are close in proximity in window content then it won't feel weird and if they are far apart - // it likely won't be a problem anyone runs into. - const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; - return (window->DC.NavLayerCurrent == upper_popup->ParentNavLayer && upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); -} - -bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); - - // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) - // The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. - ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; - if (window->Flags & ImGuiWindowFlags_ChildMenu) - flags |= ImGuiWindowFlags_ChildWindow; - - // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). - // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame. - // If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used. - if (g.MenusIdSubmittedThisFrame.contains(id)) - { - if (menu_is_open) - menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - else - g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values - return menu_is_open; - } - - // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu - g.MenusIdSubmittedThisFrame.push_back(id); - - ImVec2 label_size = CalcTextSize(label, NULL, true); - - // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) - const bool menuset_is_open = IsRootOfOpenMenuSet(); - ImGuiWindow* backed_nav_window = g.NavWindow; - if (menuset_is_open) - g.NavWindow = window; - - // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, - // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). - // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. - ImVec2 popup_pos, pos = window->DC.CursorPos; - PushID(label); - if (!enabled) - BeginDisabled(); - const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; - bool pressed; - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups; - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) - { - // Menu inside an horizontal menu bar - // Selectable extend their highlight by half ItemSpacing in each direction. - // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() - popup_pos = ImVec2(pos.x - 1.0f - IM_FLOOR(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight()); - window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); - PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); - float w = label_size.x; - ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); - pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, 0.0f)); - RenderText(text_pos, label); - PopStyleVar(); - window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). - } - else - { - // Menu inside a regular/vertical menu - // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. - // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. - popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); - float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; - float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); - float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame - float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); - pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); - RenderText(text_pos, label); - if (icon_w > 0.0f) - RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); - RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); - } - if (!enabled) - EndDisabled(); - - const bool hovered = (g.HoveredId == id) && enabled && !g.NavDisableMouseHover; - if (menuset_is_open) - g.NavWindow = backed_nav_window; - - bool want_open = false; - bool want_close = false; - if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) - { - // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu - // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. - bool moving_toward_child_menu = false; - ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL; - if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) - { - float ref_unit = g.FontSize; // FIXME-DPI - ImRect next_window_rect = child_menu_window->Rect(); - ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta); - ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); - ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack. - ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues (FIXME: ??) - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle has maximum height to limit the slope and the bias toward large sub-menus - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f); - moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_other_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] - } - - // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not) - // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon. - // (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.) - if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu) - want_close = true; - - // Open - if (!menu_is_open && pressed) // Click/activate to open - want_open = true; - else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open - want_open = true; - if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open - { - want_open = true; - NavMoveRequestCancel(); - } - } - else - { - // Menu bar - if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it - { - want_close = true; - want_open = menu_is_open = false; - } - else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others - { - want_open = true; - } - else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open - { - want_open = true; - NavMoveRequestCancel(); - } - } - - if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' - want_close = true; - if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None)) - ClosePopupToLevel(g.BeginPopupStack.Size, true); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); - PopID(); - - if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) - { - // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. - OpenPopup(label); - return false; - } - - menu_is_open |= want_open; - if (want_open) - OpenPopup(label); - - if (menu_is_open) - { - SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos. - PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding - menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - PopStyleVar(); - } - else - { - g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values - } - - return menu_is_open; -} - -bool ImGui::BeginMenu(const char* label, bool enabled) -{ - return BeginMenuEx(label, NULL, enabled); -} - -void ImGui::EndMenu() -{ - // Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu). - // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs. - // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) - if (g.NavWindow && (g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow == window) - { - ClosePopupToLevel(g.BeginPopupStack.Size, true); - NavMoveRequestCancel(); - } - - EndPopup(); -} - -bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut, bool selected, bool enabled) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - ImVec2 pos = window->DC.CursorPos; - ImVec2 label_size = CalcTextSize(label, NULL, true); - - const bool menuset_is_open = IsRootOfOpenMenuSet(); - ImGuiWindow* backed_nav_window = g.NavWindow; - if (menuset_is_open) - g.NavWindow = window; - - // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), - // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. - bool pressed; - PushID(label); - if (!enabled) - BeginDisabled(); - - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; - const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) - { - // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful - // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. - float w = label_size.x; - window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); - ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); - PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); - pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); - PopStyleVar(); - RenderText(text_pos, label); - window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). - } - else - { - // Menu item inside a vertical menu - // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. - // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. - float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; - float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; - float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); - float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame - float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); - RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); - if (icon_w > 0.0f) - RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); - if (shortcut_w > 0.0f) - { - PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); - RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); - PopStyleColor(); - } - if (selected) - RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); - } - IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); - if (!enabled) - EndDisabled(); - PopID(); - if (menuset_is_open) - g.NavWindow = backed_nav_window; - - return pressed; -} - -bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) -{ - return MenuItemEx(label, NULL, shortcut, selected, enabled); -} - -bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) -{ - if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled)) - { - if (p_selected) - *p_selected = !*p_selected; - return true; - } - return false; -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. -//------------------------------------------------------------------------- -// - BeginTabBar() -// - BeginTabBarEx() [Internal] -// - EndTabBar() -// - TabBarLayout() [Internal] -// - TabBarCalcTabID() [Internal] -// - TabBarCalcMaxTabWidth() [Internal] -// - TabBarFindTabById() [Internal] -// - TabBarRemoveTab() [Internal] -// - TabBarCloseTab() [Internal] -// - TabBarScrollClamp() [Internal] -// - TabBarScrollToTab() [Internal] -// - TabBarQueueChangeTabOrder() [Internal] -// - TabBarScrollingButtons() [Internal] -// - TabBarTabListPopupButton() [Internal] -//------------------------------------------------------------------------- - -struct ImGuiTabBarSection -{ - int TabCount; // Number of tabs in this section. - float Width; // Sum of width of tabs in this section (after shrinking down) - float Spacing; // Horizontal spacing at the end of the section. - - ImGuiTabBarSection() { memset(this, 0, sizeof(*this)); } -}; - -namespace ImGui -{ - static void TabBarLayout(ImGuiTabBar* tab_bar); - static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label); - static float TabBarCalcMaxTabWidth(); - static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); - static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections); - static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar); - static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar); -} - -ImGuiTabBar::ImGuiTabBar() -{ - memset(this, 0, sizeof(*this)); - CurrFrameVisible = PrevFrameVisible = -1; - LastTabItemIdx = -1; -} - -static inline int TabItemGetSectionIdx(const ImGuiTabItem* tab) -{ - return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; -} - -static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs) -{ - const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; - const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; - const int a_section = TabItemGetSectionIdx(a); - const int b_section = TabItemGetSectionIdx(b); - if (a_section != b_section) - return a_section - b_section; - return (int)(a->IndexDuringLayout - b->IndexDuringLayout); -} - -static int IMGUI_CDECL TabItemComparerByBeginOrder(const void* lhs, const void* rhs) -{ - const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; - const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; - return (int)(a->BeginOrder - b->BeginOrder); -} - -static ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiPtrOrIndex& ref) -{ - ImGuiContext& g = *GImGui; - return ref.Ptr ? (ImGuiTabBar*)ref.Ptr : g.TabBars.GetByIndex(ref.Index); -} - -static ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar) -{ - ImGuiContext& g = *GImGui; - if (g.TabBars.Contains(tab_bar)) - return ImGuiPtrOrIndex(g.TabBars.GetIndex(tab_bar)); - return ImGuiPtrOrIndex(tab_bar); -} - -bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return false; - - ImGuiID id = window->GetID(str_id); - ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id); - ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); - tab_bar->ID = id; - return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused); -} - -bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return false; - - if ((flags & ImGuiTabBarFlags_DockNode) == 0) - PushOverrideID(tab_bar->ID); - - // Add to stack - g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar)); - g.CurrentTabBar = tab_bar; - - // Append with multiple BeginTabBar()/EndTabBar() pairs. - tab_bar->BackupCursorPos = window->DC.CursorPos; - if (tab_bar->CurrFrameVisible == g.FrameCount) - { - window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY); - tab_bar->BeginCount++; - return true; - } - - // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable - if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable))) - ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); - tab_bar->TabsAddedNew = false; - - // Flags - if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) - flags |= ImGuiTabBarFlags_FittingPolicyDefault_; - - tab_bar->Flags = flags; - tab_bar->BarRect = tab_bar_bb; - tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab() - tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible; - tab_bar->CurrFrameVisible = g.FrameCount; - tab_bar->PrevTabsContentsHeight = tab_bar->CurrTabsContentsHeight; - tab_bar->CurrTabsContentsHeight = 0.0f; - tab_bar->ItemSpacingY = g.Style.ItemSpacing.y; - tab_bar->FramePadding = g.Style.FramePadding; - tab_bar->TabsActiveCount = 0; - tab_bar->BeginCount = 1; - - // Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap - window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY); - - // Draw separator - const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive); - const float y = tab_bar->BarRect.Max.y - 1.0f; - { - const float separator_min_x = tab_bar->BarRect.Min.x - IM_FLOOR(window->WindowPadding.x * 0.5f); - const float separator_max_x = tab_bar->BarRect.Max.x + IM_FLOOR(window->WindowPadding.x * 0.5f); - window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); - } - return true; -} - -void ImGui::EndTabBar() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return; - - ImGuiTabBar* tab_bar = g.CurrentTabBar; - if (tab_bar == NULL) - { - IM_ASSERT_USER_ERROR(tab_bar != NULL, "Mismatched BeginTabBar()/EndTabBar()!"); - return; - } - - // Fallback in case no TabItem have been submitted - if (tab_bar->WantLayout) - TabBarLayout(tab_bar); - - // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed(). - const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); - if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing) - { - tab_bar->CurrTabsContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, tab_bar->CurrTabsContentsHeight); - window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->CurrTabsContentsHeight; - } - else - { - window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->PrevTabsContentsHeight; - } - if (tab_bar->BeginCount > 1) - window->DC.CursorPos = tab_bar->BackupCursorPos; - - if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) - PopID(); - - g.CurrentTabBarStack.pop_back(); - g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back()); -} - -// This is called only once a frame before by the first call to ItemTab() -// The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions. -static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) -{ - ImGuiContext& g = *GImGui; - tab_bar->WantLayout = false; - - // Garbage collect by compacting list - // Detect if we need to sort out tab list (e.g. in rare case where a tab changed section) - int tab_dst_n = 0; - bool need_sort_by_section = false; - ImGuiTabBarSection sections[3]; // Layout sections: Leading, Central, Trailing - for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++) - { - ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n]; - if (tab->LastFrameVisible < tab_bar->PrevFrameVisible || tab->WantClose) - { - // Remove tab - if (tab_bar->VisibleTabId == tab->ID) { tab_bar->VisibleTabId = 0; } - if (tab_bar->SelectedTabId == tab->ID) { tab_bar->SelectedTabId = 0; } - if (tab_bar->NextSelectedTabId == tab->ID) { tab_bar->NextSelectedTabId = 0; } - continue; - } - if (tab_dst_n != tab_src_n) - tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n]; - - tab = &tab_bar->Tabs[tab_dst_n]; - tab->IndexDuringLayout = (ImS16)tab_dst_n; - - // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another) - int curr_tab_section_n = TabItemGetSectionIdx(tab); - if (tab_dst_n > 0) - { - ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1]; - int prev_tab_section_n = TabItemGetSectionIdx(prev_tab); - if (curr_tab_section_n == 0 && prev_tab_section_n != 0) - need_sort_by_section = true; - if (prev_tab_section_n == 2 && curr_tab_section_n != 2) - need_sort_by_section = true; - } - - sections[curr_tab_section_n].TabCount++; - tab_dst_n++; - } - if (tab_bar->Tabs.Size != tab_dst_n) - tab_bar->Tabs.resize(tab_dst_n); - - if (need_sort_by_section) - ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection); - - // Calculate spacing between sections - sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; - sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; - - // Setup next selected tab - ImGuiID scroll_to_tab_id = 0; - if (tab_bar->NextSelectedTabId) - { - tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; - tab_bar->NextSelectedTabId = 0; - scroll_to_tab_id = tab_bar->SelectedTabId; - } - - // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot). - if (tab_bar->ReorderRequestTabId != 0) - { - if (TabBarProcessReorder(tab_bar)) - if (tab_bar->ReorderRequestTabId == tab_bar->SelectedTabId) - scroll_to_tab_id = tab_bar->ReorderRequestTabId; - tab_bar->ReorderRequestTabId = 0; - } - - // Tab List Popup (will alter tab_bar->BarRect and therefore the available width!) - const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0; - if (tab_list_popup_button) - if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Min.x! - scroll_to_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; - - // Leading/Trailing tabs will be shrink only if central one aren't visible anymore, so layout the shrink data as: leading, trailing, central - // (whereas our tabs are stored as: leading, central, trailing) - int shrink_buffer_indexes[3] = { 0, sections[0].TabCount + sections[2].TabCount, sections[0].TabCount }; - g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size); - - // Compute ideal tabs widths + store them into shrink buffer - ImGuiTabItem* most_recently_selected_tab = NULL; - int curr_section_n = -1; - bool found_selected_tab_id = false; - for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) - { - ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible); - - if ((most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) && !(tab->Flags & ImGuiTabItemFlags_Button)) - most_recently_selected_tab = tab; - if (tab->ID == tab_bar->SelectedTabId) - found_selected_tab_id = true; - if (scroll_to_tab_id == 0 && g.NavJustMovedToId == tab->ID) - scroll_to_tab_id = tab->ID; - - // Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar. - // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, - // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window. - const char* tab_name = tab_bar->GetTabName(tab); - const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) ? false : true; - tab->ContentWidth = (tab->RequestedWidth > 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button).x; - - int section_n = TabItemGetSectionIdx(tab); - ImGuiTabBarSection* section = §ions[section_n]; - section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); - curr_section_n = section_n; - - // Store data so we can build an array sorted by width if we need to shrink tabs down - IM_MSVC_WARNING_SUPPRESS(6385); - ImGuiShrinkWidthItem* shrink_width_item = &g.ShrinkWidthBuffer[shrink_buffer_indexes[section_n]++]; - shrink_width_item->Index = tab_n; - shrink_width_item->Width = shrink_width_item->InitialWidth = tab->ContentWidth; - - IM_ASSERT(tab->ContentWidth > 0.0f); - tab->Width = tab->ContentWidth; - } - - // Compute total ideal width (used for e.g. auto-resizing a window) - tab_bar->WidthAllTabsIdeal = 0.0f; - for (int section_n = 0; section_n < 3; section_n++) - tab_bar->WidthAllTabsIdeal += sections[section_n].Width + sections[section_n].Spacing; - - // Horizontal scrolling buttons - // (note that TabBarScrollButtons() will alter BarRect.Max.x) - if ((tab_bar->WidthAllTabsIdeal > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll)) - if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar)) - { - scroll_to_tab_id = scroll_and_select_tab->ID; - if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0) - tab_bar->SelectedTabId = scroll_to_tab_id; - } - - // Shrink widths if full tabs don't fit in their allocated space - float section_0_w = sections[0].Width + sections[0].Spacing; - float section_1_w = sections[1].Width + sections[1].Spacing; - float section_2_w = sections[2].Width + sections[2].Spacing; - bool central_section_is_visible = (section_0_w + section_2_w) < tab_bar->BarRect.GetWidth(); - float width_excess; - if (central_section_is_visible) - width_excess = ImMax(section_1_w - (tab_bar->BarRect.GetWidth() - section_0_w - section_2_w), 0.0f); // Excess used to shrink central section - else - width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section - - // With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore - if (width_excess > 0.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible)) - { - int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount); - int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0); - ShrinkWidths(g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess); - - // Apply shrunk values into tabs and sections - for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++) - { - ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index]; - float shrinked_width = IM_FLOOR(g.ShrinkWidthBuffer[tab_n].Width); - if (shrinked_width < 0.0f) - continue; - - int section_n = TabItemGetSectionIdx(tab); - sections[section_n].Width -= (tab->Width - shrinked_width); - tab->Width = shrinked_width; - } - } - - // Layout all active tabs - int section_tab_index = 0; - float tab_offset = 0.0f; - tab_bar->WidthAllTabs = 0.0f; - for (int section_n = 0; section_n < 3; section_n++) - { - ImGuiTabBarSection* section = §ions[section_n]; - if (section_n == 2) - tab_offset = ImMin(ImMax(0.0f, tab_bar->BarRect.GetWidth() - section->Width), tab_offset); - - for (int tab_n = 0; tab_n < section->TabCount; tab_n++) - { - ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n]; - tab->Offset = tab_offset; - tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f); - } - tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f); - tab_offset += section->Spacing; - section_tab_index += section->TabCount; - } - - // If we have lost the selected tab, select the next most recently active one - if (found_selected_tab_id == false) - tab_bar->SelectedTabId = 0; - if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) - scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; - - // Lock in visible tab - tab_bar->VisibleTabId = tab_bar->SelectedTabId; - tab_bar->VisibleTabWasSubmitted = false; - - // Update scrolling - if (scroll_to_tab_id != 0) - TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections); - tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); - tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); - if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) - { - // Scrolling speed adjust itself so we can always reach our target in 1/3 seconds. - // Teleport if we are aiming far off the visible line - tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, 70.0f * g.FontSize); - tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, ImFabs(tab_bar->ScrollingTarget - tab_bar->ScrollingAnim) / 0.3f); - const bool teleport = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) || (tab_bar->ScrollingTargetDistToVisibility > 10.0f * g.FontSize); - tab_bar->ScrollingAnim = teleport ? tab_bar->ScrollingTarget : ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, g.IO.DeltaTime * tab_bar->ScrollingSpeed); - } - else - { - tab_bar->ScrollingSpeed = 0.0f; - } - tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing; - tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing; - - // Clear name buffers - if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) - tab_bar->TabsNames.Buf.resize(0); - - // Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame) - ImGuiWindow* window = g.CurrentWindow; - window->DC.CursorPos = tab_bar->BarRect.Min; - ItemSize(ImVec2(tab_bar->WidthAllTabs, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y); - window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal); -} - -// Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack. -static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label) -{ - if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) - { - ImGuiID id = ImHashStr(label); - KeepAliveID(id); - return id; - } - else - { - ImGuiWindow* window = GImGui->CurrentWindow; - return window->GetID(label); - } -} - -static float ImGui::TabBarCalcMaxTabWidth() -{ - ImGuiContext& g = *GImGui; - return g.FontSize * 20.0f; -} - -ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id) -{ - if (tab_id != 0) - for (int n = 0; n < tab_bar->Tabs.Size; n++) - if (tab_bar->Tabs[n].ID == tab_id) - return &tab_bar->Tabs[n]; - return NULL; -} - -// The *TabId fields be already set by the docking system _before_ the actual TabItem was created, so we clear them regardless. -void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) -{ - if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) - tab_bar->Tabs.erase(tab); - if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; } - if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; } - if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; } -} - -// Called on manual closure attempt -void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) -{ - IM_ASSERT(!(tab->Flags & ImGuiTabItemFlags_Button)); - if (!(tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) - { - // This will remove a frame of lag for selecting another tab on closure. - // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure - tab->WantClose = true; - if (tab_bar->VisibleTabId == tab->ID) - { - tab->LastFrameVisible = -1; - tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0; - } - } - else - { - // Actually select before expecting closure attempt (on an UnsavedDocument tab user is expect to e.g. show a popup) - if (tab_bar->VisibleTabId != tab->ID) - tab_bar->NextSelectedTabId = tab->ID; - } -} - -static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling) -{ - scrolling = ImMin(scrolling, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth()); - return ImMax(scrolling, 0.0f); -} - -// Note: we may scroll to tab that are not selected! e.g. using keyboard arrow keys -static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections) -{ - ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id); - if (tab == NULL) - return; - if (tab->Flags & ImGuiTabItemFlags_SectionMask_) - return; - - ImGuiContext& g = *GImGui; - float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar) - int order = tab_bar->GetTabOrder(tab); - - // Scrolling happens only in the central section (leading/trailing sections are not scrolling) - // FIXME: This is all confusing. - float scrollable_width = tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing; - - // We make all tabs positions all relative Sections[0].Width to make code simpler - float tab_x1 = tab->Offset - sections[0].Width + (order > sections[0].TabCount - 1 ? -margin : 0.0f); - float tab_x2 = tab->Offset - sections[0].Width + tab->Width + (order + 1 < tab_bar->Tabs.Size - sections[2].TabCount ? margin : 1.0f); - tab_bar->ScrollingTargetDistToVisibility = 0.0f; - if (tab_bar->ScrollingTarget > tab_x1 || (tab_x2 - tab_x1 >= scrollable_width)) - { - // Scroll to the left - tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f); - tab_bar->ScrollingTarget = tab_x1; - } - else if (tab_bar->ScrollingTarget < tab_x2 - scrollable_width) - { - // Scroll to the right - tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - scrollable_width) - tab_bar->ScrollingAnim, 0.0f); - tab_bar->ScrollingTarget = tab_x2 - scrollable_width; - } -} - -void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset) -{ - IM_ASSERT(offset != 0); - IM_ASSERT(tab_bar->ReorderRequestTabId == 0); - tab_bar->ReorderRequestTabId = tab->ID; - tab_bar->ReorderRequestOffset = (ImS16)offset; -} - -void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* src_tab, ImVec2 mouse_pos) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(tab_bar->ReorderRequestTabId == 0); - if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) - return; - - const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; - const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); - - // Count number of contiguous tabs we are crossing over - const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1; - const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab); - int dst_idx = src_idx; - for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir) - { - // Reordered tabs must share the same section - const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i]; - if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder) - break; - if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_)) - break; - dst_idx = i; - - // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. - const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x; - const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x; - //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); - if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) - break; - } - - if (dst_idx != src_idx) - TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx); -} - -bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) -{ - ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId); - if (tab1 == NULL || (tab1->Flags & ImGuiTabItemFlags_NoReorder)) - return false; - - //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools - int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestOffset; - if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size) - return false; - - // Reordered tabs must share the same section - // (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too) - ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; - if (tab2->Flags & ImGuiTabItemFlags_NoReorder) - return false; - if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_)) - return false; - - ImGuiTabItem item_tmp = *tab1; - ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2; - ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1; - const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset; - memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem)); - *tab2 = item_tmp; - - if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) - MarkIniSettingsDirty(); - return true; -} - -static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f); - const float scrolling_buttons_width = arrow_button_size.x * 2.0f; - - const ImVec2 backup_cursor_pos = window->DC.CursorPos; - //window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255)); - - int select_dir = 0; - ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; - arrow_col.w *= 0.5f; - - PushStyleColor(ImGuiCol_Text, arrow_col); - PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - const float backup_repeat_delay = g.IO.KeyRepeatDelay; - const float backup_repeat_rate = g.IO.KeyRepeatRate; - g.IO.KeyRepeatDelay = 0.250f; - g.IO.KeyRepeatRate = 0.200f; - float x = ImMax(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.x - scrolling_buttons_width); - window->DC.CursorPos = ImVec2(x, tab_bar->BarRect.Min.y); - if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat)) - select_dir = -1; - window->DC.CursorPos = ImVec2(x + arrow_button_size.x, tab_bar->BarRect.Min.y); - if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat)) - select_dir = +1; - PopStyleColor(2); - g.IO.KeyRepeatRate = backup_repeat_rate; - g.IO.KeyRepeatDelay = backup_repeat_delay; - - ImGuiTabItem* tab_to_scroll_to = NULL; - if (select_dir != 0) - if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) - { - int selected_order = tab_bar->GetTabOrder(tab_item); - int target_order = selected_order + select_dir; - - // Skip tab item buttons until another tab item is found or end is reached - while (tab_to_scroll_to == NULL) - { - // If we are at the end of the list, still scroll to make our tab visible - tab_to_scroll_to = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; - - // Cross through buttons - // (even if first/last item is a button, return it so we can update the scroll) - if (tab_to_scroll_to->Flags & ImGuiTabItemFlags_Button) - { - target_order += select_dir; - selected_order += select_dir; - tab_to_scroll_to = (target_order < 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL; - } - } - } - window->DC.CursorPos = backup_cursor_pos; - tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f; - - return tab_to_scroll_to; -} - -static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // We use g.Style.FramePadding.y to match the square ArrowButton size - const float tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y; - const ImVec2 backup_cursor_pos = window->DC.CursorPos; - window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x - g.Style.FramePadding.y, tab_bar->BarRect.Min.y); - tab_bar->BarRect.Min.x += tab_list_popup_button_width; - - ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; - arrow_col.w *= 0.5f; - PushStyleColor(ImGuiCol_Text, arrow_col); - PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLargest); - PopStyleColor(2); - - ImGuiTabItem* tab_to_select = NULL; - if (open) - { - for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) - { - ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - if (tab->Flags & ImGuiTabItemFlags_Button) - continue; - - const char* tab_name = tab_bar->GetTabName(tab); - if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID)) - tab_to_select = tab; - } - EndCombo(); - } - - window->DC.CursorPos = backup_cursor_pos; - return tab_to_select; -} - -//------------------------------------------------------------------------- -// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. -//------------------------------------------------------------------------- -// - BeginTabItem() -// - EndTabItem() -// - TabItemButton() -// - TabItemEx() [Internal] -// - SetTabItemClosed() -// - TabItemCalcSize() [Internal] -// - TabItemBackground() [Internal] -// - TabItemLabelAndCloseButton() [Internal] -//------------------------------------------------------------------------- - -bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return false; - - ImGuiTabBar* tab_bar = g.CurrentTabBar; - if (tab_bar == NULL) - { - IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!"); - return false; - } - IM_ASSERT(!(flags & ImGuiTabItemFlags_Button)); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! - - bool ret = TabItemEx(tab_bar, label, p_open, flags); - if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) - { - ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; - PushOverrideID(tab->ID); // We already hashed 'label' so push into the ID stack directly instead of doing another hash through PushID(label) - } - return ret; -} - -void ImGui::EndTabItem() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return; - - ImGuiTabBar* tab_bar = g.CurrentTabBar; - if (tab_bar == NULL) - { - IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); - return; - } - IM_ASSERT(tab_bar->LastTabItemIdx >= 0); - ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; - if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) - PopID(); -} - -bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return false; - - ImGuiTabBar* tab_bar = g.CurrentTabBar; - if (tab_bar == NULL) - { - IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); - return false; - } - return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder); -} - -bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags) -{ - // Layout whole tab bar if not already done - ImGuiContext& g = *GImGui; - if (tab_bar->WantLayout) - { - ImGuiNextItemData backup_next_item_data = g.NextItemData; - TabBarLayout(tab_bar); - g.NextItemData = backup_next_item_data; - } - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return false; - - const ImGuiStyle& style = g.Style; - const ImGuiID id = TabBarCalcTabID(tab_bar, label); - - // If the user called us with *p_open == false, we early out and don't render. - // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); - if (p_open && !*p_open) - { - ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); - return false; - } - - IM_ASSERT(!p_open || !(flags & ImGuiTabItemFlags_Button)); - IM_ASSERT((flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)); // Can't use both Leading and Trailing - - // Store into ImGuiTabItemFlags_NoCloseButton, also honor ImGuiTabItemFlags_NoCloseButton passed by user (although not documented) - if (flags & ImGuiTabItemFlags_NoCloseButton) - p_open = NULL; - else if (p_open == NULL) - flags |= ImGuiTabItemFlags_NoCloseButton; - - // Acquire tab data - ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id); - bool tab_is_new = false; - if (tab == NULL) - { - tab_bar->Tabs.push_back(ImGuiTabItem()); - tab = &tab_bar->Tabs.back(); - tab->ID = id; - tab_bar->TabsAddedNew = tab_is_new = true; - } - tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab); - - // Calculate tab contents size - ImVec2 size = TabItemCalcSize(label, p_open != NULL); - tab->RequestedWidth = -1.0f; - if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth) - size.x = tab->RequestedWidth = g.NextItemData.Width; - if (tab_is_new) - tab->Width = size.x; - tab->ContentWidth = size.x; - tab->BeginOrder = tab_bar->TabsActiveCount++; - - const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); - const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0; - const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount); - const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0; - tab->LastFrameVisible = g.FrameCount; - tab->Flags = flags; - - // Append name with zero-terminator - tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); - tab_bar->TabsNames.append(label, label + strlen(label) + 1); - - // Update selected tab - if (!is_tab_button) - { - if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0) - if (!tab_bar_appearing || tab_bar->SelectedTabId == 0) - tab_bar->NextSelectedTabId = id; // New tabs gets activated - if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // _SetSelected can only be passed on explicit tab bar - tab_bar->NextSelectedTabId = id; - } - - // Lock visibility - // (Note: tab_contents_visible != tab_selected... because CTRL+TAB operations may preview some tabs without selecting them!) - bool tab_contents_visible = (tab_bar->VisibleTabId == id); - if (tab_contents_visible) - tab_bar->VisibleTabWasSubmitted = true; - - // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches - if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing) - if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs)) - tab_contents_visible = true; - - // Note that tab_is_new is not necessarily the same as tab_appearing! When a tab bar stops being submitted - // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'. - if (tab_appearing && (!tab_bar_appearing || tab_is_new)) - { - ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); - if (is_tab_button) - return false; - return tab_contents_visible; - } - - if (tab_bar->SelectedTabId == id) - tab->LastFrameSelected = g.FrameCount; - - // Backup current layout position - const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; - - // Layout - const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; - size.x = tab->Width; - if (is_central_section) - window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f); - else - window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f); - ImVec2 pos = window->DC.CursorPos; - ImRect bb(pos, pos + size); - - // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation) - const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX); - if (want_clip_rect) - PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true); - - ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos; - ItemSize(bb.GetSize(), style.FramePadding.y); - window->DC.CursorMaxPos = backup_cursor_max_pos; - - if (!ItemAdd(bb, id)) - { - if (want_clip_rect) - PopClipRect(); - window->DC.CursorPos = backup_main_cursor_pos; - return tab_contents_visible; - } - - // Click to Select a tab - ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowItemOverlap); - if (g.DragDropActive) - button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); - if (pressed && !is_tab_button) - tab_bar->NextSelectedTabId = id; - - // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) - if (g.ActiveId != id) - SetItemAllowOverlap(); - - // Drag and drop: re-order tabs - if (held && !tab_appearing && IsMouseDragging(0)) - { - if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) - { - // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x - if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) - { - TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); - } - else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) - { - TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); - } - } - } - -#if 0 - if (hovered && g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth) - { - // Enlarge tab display when hovering - bb.Max.x = bb.Min.x + IM_FLOOR(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f))); - display_draw_list = GetForegroundDrawList(window); - TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive)); - } -#endif - - // Render tab shape - ImDrawList* display_draw_list = window->DrawList; - const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabUnfocused)); - TabItemBackground(display_draw_list, bb, flags, tab_col); - RenderNavHighlight(bb, id); - - // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. - const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); - if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1))) - if (!is_tab_button) - tab_bar->NextSelectedTabId = id; - - if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) - flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; - - // Render tab label, process close button - const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, id) : 0; - bool just_closed; - bool text_clipped; - TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); - if (just_closed && p_open != NULL) - { - *p_open = false; - TabBarCloseTab(tab_bar, tab); - } - - // Restore main window position so user can draw there - if (want_clip_rect) - PopClipRect(); - window->DC.CursorPos = backup_main_cursor_pos; - - // Tooltip - // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) - // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) - // FIXME: This is a mess. - // FIXME: We may want disabled tab to still display the tooltip? - if (text_clipped && g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay && IsItemHovered()) - if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) - SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); - - IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected - if (is_tab_button) - return pressed; - return tab_contents_visible; -} - -// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed. -// To use it to need to call the function SetTabItemClosed() between BeginTabBar() and EndTabBar(). -// Tabs closed by the close button will automatically be flagged to avoid this issue. -void ImGui::SetTabItemClosed(const char* label) -{ - ImGuiContext& g = *GImGui; - bool is_within_manual_tab_bar = g.CurrentTabBar && !(g.CurrentTabBar->Flags & ImGuiTabBarFlags_DockNode); - if (is_within_manual_tab_bar) - { - ImGuiTabBar* tab_bar = g.CurrentTabBar; - ImGuiID tab_id = TabBarCalcTabID(tab_bar, label); - if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) - tab->WantClose = true; // Will be processed by next call to TabBarLayout() - } -} - -ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button) -{ - ImGuiContext& g = *GImGui; - ImVec2 label_size = CalcTextSize(label, NULL, true); - ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f); - if (has_close_button) - size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle. - else - size.x += g.Style.FramePadding.x + 1.0f; - return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); -} - -void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) -{ - // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it. - ImGuiContext& g = *GImGui; - const float width = bb.GetWidth(); - IM_UNUSED(flags); - IM_ASSERT(width > 0.0f); - const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f)); - const float y1 = bb.Min.y + 1.0f; - const float y2 = bb.Max.y - 1.0f; - draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); - draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9); - draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12); - draw_list->PathLineTo(ImVec2(bb.Max.x, y2)); - draw_list->PathFillConvex(col); - if (g.Style.TabBorderSize > 0.0f) - { - draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2)); - draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9); - draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12); - draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2)); - draw_list->PathStroke(GetColorU32(ImGuiCol_Border), 0, g.Style.TabBorderSize); - } -} - -// Render text label (with custom clipping) + Unsaved Document marker + Close Button logic -// We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter. -void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped) -{ - ImGuiContext& g = *GImGui; - ImVec2 label_size = CalcTextSize(label, NULL, true); - - if (out_just_closed) - *out_just_closed = false; - if (out_text_clipped) - *out_text_clipped = false; - - if (bb.GetWidth() <= 1.0f) - return; - - // In Style V2 we'll have full override of all colors per state (e.g. focused, selected) - // But right now if you want to alter text color of tabs this is what you need to do. -#if 0 - const float backup_alpha = g.Style.Alpha; - if (!is_contents_visible) - g.Style.Alpha *= 0.7f; -#endif - - // Render text label (with clipping + alpha gradient) + unsaved marker - ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); - ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; - - // Return clipped state ignoring the close button - if (out_text_clipped) - { - *out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_pixel_clip_bb.Max.x; - //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255)); - } - - const float button_sz = g.FontSize; - const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x * 2.0f - button_sz), bb.Min.y); - - // Close Button & Unsaved Marker - // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() - // 'hovered' will be true when hovering the Tab but NOT when hovering the close button - // 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button - // 'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false - bool close_button_pressed = false; - bool close_button_visible = false; - if (close_button_id != 0) - if (is_contents_visible || bb.GetWidth() >= ImMax(button_sz, g.Style.TabMinWidthForCloseButton)) - if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id) - close_button_visible = true; - bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x); - - if (close_button_visible) - { - ImGuiLastItemData last_item_backup = g.LastItemData; - PushStyleVar(ImGuiStyleVar_FramePadding, frame_padding); - if (CloseButton(close_button_id, button_pos)) - close_button_pressed = true; - PopStyleVar(); - g.LastItemData = last_item_backup; - - // Close with middle mouse button - if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) - close_button_pressed = true; - } - else if (unsaved_marker_visible) - { - const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz) + g.Style.FramePadding * 2.0f); - RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); - } - - // This is all rather complicated - // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position) - // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist.. - float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f; - if (close_button_visible || unsaved_marker_visible) - { - text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f); - text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f; - ellipsis_max_x = text_pixel_clip_bb.Max.x; - } - RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size); - -#if 0 - if (!is_contents_visible) - g.Style.Alpha = backup_alpha; -#endif - - if (out_just_closed) - *out_just_closed = close_button_pressed; -} - - -#endif // #ifndef IMGUI_DISABLE diff --git a/libs/zgui/libs/imgui/implot.cpp b/libs/zgui/libs/imgui/implot.cpp deleted file mode 100644 index e18e71d8e..000000000 --- a/libs/zgui/libs/imgui/implot.cpp +++ /dev/null @@ -1,5721 +0,0 @@ -// MIT License - -// Copyright (c) 2022 Evan Pezent - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// ImPlot v0.14 - -/* - -API BREAKING CHANGES -==================== -Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. -Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code. -When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. -You can read releases logs https://github.com/epezent/implot/releases for more details. - -- 2022/06/19 (0.14) - The signature of ColormapScale has changed to accommodate a new ImPlotColormapScaleFlags parameter -- 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters. - If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see - unexpected results or crashes without a compiler warning since these three are all default args. We apologize for the inconvenience, but this was a necessary evil. - - PlotBarsH has been removed; use PlotBars + ImPlotBarsFlags_Horizontal instead - - PlotErrorBarsH has been removed; use PlotErrorBars + ImPlotErrorBarsFlags_Horizontal - - PlotHistogram/PlotHistogram2D signatures changed; `cumulative`, `density`, and `outliers` options now specified via ImPlotHistogramFlags - - PlotPieChart signature changed; `normalize` option now specified via ImPlotPieChartFlags - - PlotText signature changes; `vertical` option now specified via `ImPlotTextFlags_Vertical` - - `PlotVLines` and `PlotHLines` replaced with `PlotInfLines` (+ ImPlotInfLinesFlags_Horizontal ) - - arguments of ImPlotGetter have been reversed to be consistent with other API callbacks - - SetupAxisScale + ImPlotScale have replaced ImPlotAxisFlags_LogScale and ImPlotAxisFlags_Time flags - - ImPlotFormatters should now return an int indicating the size written - - the signature of ImPlotGetter has been reversed so that void* user_data is the last argument and consistent with other callbacks -- 2021/10/19 (0.13) - MAJOR API OVERHAUL! See #168 and #272 - - TRIVIAL RENAME: - - ImPlotLimits -> ImPlotRect - - ImPlotYAxis_ -> ImAxis_ - - SetPlotYAxis -> SetAxis - - BeginDragDropTarget -> BeginDragDropTargetPlot - - BeginDragDropSource -> BeginDragDropSourcePlot - - ImPlotFlags_NoMousePos -> ImPlotFlags_NoMouseText - - SetNextPlotLimits -> SetNextAxesLimits - - SetMouseTextLocation -> SetupMouseText - - SIGNATURE MODIFIED: - - PixelsToPlot/PlotToPixels -> added optional X-Axis arg - - GetPlotMousePos -> added optional X-Axis arg - - GetPlotLimits -> added optional X-Axis arg - - GetPlotSelection -> added optional X-Axis arg - - DragLineX/Y/DragPoint -> now takes int id; removed labels (render with Annotation/Tag instead) - - REPLACED: - - IsPlotXAxisHovered/IsPlotXYAxisHovered -> IsAxisHovered(ImAxis) - - BeginDragDropTargetX/BeginDragDropTargetY -> BeginDragDropTargetAxis(ImAxis) - - BeginDragDropSourceX/BeginDragDropSourceY -> BeginDragDropSourceAxis(ImAxis) - - ImPlotCol_XAxis, ImPlotCol_YAxis1, etc. -> ImPlotCol_AxisText (push/pop this around SetupAxis to style individual axes) - - ImPlotCol_XAxisGrid, ImPlotCol_Y1AxisGrid -> ImPlotCol_AxisGrid (push/pop this around SetupAxis to style individual axes) - - SetNextPlotLimitsX/Y -> SetNextAxisLimits(ImAxis) - - LinkNextPlotLimits -> SetNextAxisLinks(ImAxis) - - FitNextPlotAxes -> SetNextAxisToFit(ImAxis)/SetNextAxesToFit - - SetLegendLocation -> SetupLegend - - ImPlotFlags_NoHighlight -> ImPlotLegendFlags_NoHighlight - - ImPlotOrientation -> ImPlotLegendFlags_Horizontal - - Annotate -> Annotation - - REMOVED: - - GetPlotQuery, SetPlotQuery, IsPlotQueried -> use DragRect - - SetNextPlotTicksX, SetNextPlotTicksY -> use SetupAxisTicks - - SetNextPlotFormatX, SetNextPlotFormatY -> use SetupAxisFormat - - AnnotateClamped -> use Annotation(bool clamp = true) - - OBSOLETED: - - BeginPlot (original signature) -> use simplified signature + Setup API -- 2021/07/30 (0.12) - The offset argument of `PlotXG` functions was been removed. Implement offsetting in your getter callback instead. -- 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap. - ShowColormapScale was changed to ColormapScale and requires additional arguments. -- 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size. -- 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. -- 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved - to implot_internal.h due to its immaturity. -- 2020/10/16 (0.8) - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding -- 2020/09/10 (0.8) - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0. -- 2020/09/07 (0.8) - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG) -- 2020/09/06 (0.7) - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset - is simply 0. This more closely matches ImGui's style and makes it easier to enable non-default but commonly used flags (e.g. ImPlotAxisFlags_Time). -- 2020/08/28 (0.5) - ImPlotMarker_ can no longer be combined with bitwise OR, |. This features caused unecessary slow-down, and almost no one used it. -- 2020/08/25 (0.5) - ImPlotAxisFlags_Scientific was removed. Logarithmic axes automatically uses scientific notation. -- 2020/08/17 (0.5) - PlotText was changed so that text is centered horizontally and vertically about the desired point. -- 2020/08/16 (0.5) - An ImPlotContext must be explicitly created and destroyed now with `CreateContext` and `DestroyContext`. Previously, the context was statically initialized in this source file. -- 2020/06/13 (0.4) - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default. -- 2020/06/03 (0.3) - The signature and behavior of PlotPieChart was changed so that data with sum less than 1 can optionally be normalized. The label format can now be specified as well. -- 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`. -- 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead. -- 2020/05/29 (0.3) - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2 -- 2020/05/16 (0.2) - All plotting functions were reverted to being prefixed with "Plot" to maintain a consistent VerbNoun style. `Plot` was split into `PlotLine` - and `PlotScatter` (however, `PlotLine` can still be used to plot scatter points as `Plot` did before.). `Bar` is not `PlotBars`, to indicate - that multiple bars will be plotted. -- 2020/05/13 (0.2) - `ImMarker` was change to `ImPlotMarker` and `ImAxisFlags` was changed to `ImPlotAxisFlags`. -- 2020/05/11 (0.2) - `ImPlotFlags_Selection` was changed to `ImPlotFlags_BoxSelect` -- 2020/05/11 (0.2) - The namespace ImGui:: was replaced with ImPlot::. As a result, the following additional changes were made: - - Functions that were prefixed or decorated with the word "Plot" have been truncated. E.g., `ImGui::PlotBars` is now just `ImPlot::Bar`. - It should be fairly obvious what was what. - - Some functions have been given names that would have otherwise collided with the ImGui namespace. This has been done to maintain a consistent - style with ImGui. E.g., 'ImGui::PushPlotStyleVar` is now 'ImPlot::PushStyleVar'. -- 2020/05/10 (0.2) - The following function/struct names were changes: - - ImPlotRange -> ImPlotLimits - - GetPlotRange() -> GetPlotLimits() - - SetNextPlotRange -> SetNextPlotLimits - - SetNextPlotRangeX -> SetNextPlotLimitsX - - SetNextPlotRangeY -> SetNextPlotLimitsY -- 2020/05/10 (0.2) - Plot queries are pixel based by default. Query rects that maintain relative plot position have been removed. This was done to support multi-y-axis. - -*/ - -#include "implot.h" -#include "implot_internal.h" - -#include - -// Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. -#if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll) -#define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All -#endif - -// Visual Studio warnings -#ifdef _MSC_VER -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#endif - -// Clang/GCC warnings with -Weverything -#if defined(__clang__) -#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal -#elif defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked -#endif - -// Global plot context -#ifndef GImPlot -ImPlotContext* GImPlot = NULL; -#endif - -//----------------------------------------------------------------------------- -// Struct Implementations -//----------------------------------------------------------------------------- - -ImPlotInputMap::ImPlotInputMap() { - ImPlot::MapInputDefault(this); -} - -ImPlotStyle::ImPlotStyle() { - - LineWeight = 1; - Marker = ImPlotMarker_None; - MarkerSize = 4; - MarkerWeight = 1; - FillAlpha = 1; - ErrorBarSize = 5; - ErrorBarWeight = 1.5f; - DigitalBitHeight = 8; - DigitalBitGap = 4; - - PlotBorderSize = 1; - MinorAlpha = 0.25f; - MajorTickLen = ImVec2(10,10); - MinorTickLen = ImVec2(5,5); - MajorTickSize = ImVec2(1,1); - MinorTickSize = ImVec2(1,1); - MajorGridSize = ImVec2(1,1); - MinorGridSize = ImVec2(1,1); - PlotPadding = ImVec2(10,10); - LabelPadding = ImVec2(5,5); - LegendPadding = ImVec2(10,10); - LegendInnerPadding = ImVec2(5,5); - LegendSpacing = ImVec2(5,0); - MousePosPadding = ImVec2(10,10); - AnnotationPadding = ImVec2(2,2); - FitPadding = ImVec2(0,0); - PlotDefaultSize = ImVec2(400,300); - PlotMinSize = ImVec2(200,150); - - ImPlot::StyleColorsAuto(this); - - Colormap = ImPlotColormap_Deep; - - UseLocalTime = false; - Use24HourClock = false; - UseISO8601 = false; -} - -//----------------------------------------------------------------------------- -// Style -//----------------------------------------------------------------------------- - -namespace ImPlot { - -const char* GetStyleColorName(ImPlotCol col) { - static const char* col_names[ImPlotCol_COUNT] = { - "Line", - "Fill", - "MarkerOutline", - "MarkerFill", - "ErrorBar", - "FrameBg", - "PlotBg", - "PlotBorder", - "LegendBg", - "LegendBorder", - "LegendText", - "TitleText", - "InlayText", - "AxisText", - "AxisGrid", - "AxisTick", - "AxisBg", - "AxisBgHovered", - "AxisBgActive", - "Selection", - "Crosshairs" - }; - return col_names[col]; -} - -const char* GetMarkerName(ImPlotMarker marker) { - switch (marker) { - case ImPlotMarker_None: return "None"; - case ImPlotMarker_Circle: return "Circle"; - case ImPlotMarker_Square: return "Square"; - case ImPlotMarker_Diamond: return "Diamond"; - case ImPlotMarker_Up: return "Up"; - case ImPlotMarker_Down: return "Down"; - case ImPlotMarker_Left: return "Left"; - case ImPlotMarker_Right: return "Right"; - case ImPlotMarker_Cross: return "Cross"; - case ImPlotMarker_Plus: return "Plus"; - case ImPlotMarker_Asterisk: return "Asterisk"; - default: return ""; - } -} - -ImVec4 GetAutoColor(ImPlotCol idx) { - ImVec4 col(0,0,0,1); - switch(idx) { - case ImPlotCol_Line: return col; // these are plot dependent! - case ImPlotCol_Fill: return col; // these are plot dependent! - case ImPlotCol_MarkerOutline: return col; // these are plot dependent! - case ImPlotCol_MarkerFill: return col; // these are plot dependent! - case ImPlotCol_ErrorBar: return ImGui::GetStyleColorVec4(ImGuiCol_Text); - case ImPlotCol_FrameBg: return ImGui::GetStyleColorVec4(ImGuiCol_FrameBg); - case ImPlotCol_PlotBg: return ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); - case ImPlotCol_PlotBorder: return ImGui::GetStyleColorVec4(ImGuiCol_Border); - case ImPlotCol_LegendBg: return ImGui::GetStyleColorVec4(ImGuiCol_PopupBg); - case ImPlotCol_LegendBorder: return GetStyleColorVec4(ImPlotCol_PlotBorder); - case ImPlotCol_LegendText: return GetStyleColorVec4(ImPlotCol_InlayText); - case ImPlotCol_TitleText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); - case ImPlotCol_InlayText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); - case ImPlotCol_AxisText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); - case ImPlotCol_AxisGrid: return GetStyleColorVec4(ImPlotCol_AxisText) * ImVec4(1,1,1,0.25f); - case ImPlotCol_AxisTick: return GetStyleColorVec4(ImPlotCol_AxisGrid); - case ImPlotCol_AxisBg: return ImVec4(0,0,0,0); - case ImPlotCol_AxisBgHovered: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); - case ImPlotCol_AxisBgActive: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive); - case ImPlotCol_Selection: return ImVec4(1,1,0,1); - case ImPlotCol_Crosshairs: return GetStyleColorVec4(ImPlotCol_PlotBorder); - default: return col; - } -} - -struct ImPlotStyleVarInfo { - ImGuiDataType Type; - ImU32 Count; - ImU32 Offset; - void* GetVarPtr(ImPlotStyle* style) const { return (void*)((unsigned char*)style + Offset); } -}; - -static const ImPlotStyleVarInfo GPlotStyleVarInfo[] = -{ - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight - { ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap - - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing - - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize -}; - -static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) { - IM_ASSERT(idx >= 0 && idx < ImPlotStyleVar_COUNT); - IM_ASSERT(IM_ARRAYSIZE(GPlotStyleVarInfo) == ImPlotStyleVar_COUNT); - return &GPlotStyleVarInfo[idx]; -} - -//----------------------------------------------------------------------------- -// Generic Helpers -//----------------------------------------------------------------------------- - -void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *text_begin, const char* text_end) { - // the code below is based loosely on ImFont::RenderText - if (!text_end) - text_end = text_begin + strlen(text_begin); - ImGuiContext& g = *GImGui; - ImFont* font = g.Font; - // Align to be pixel perfect - pos.x = IM_FLOOR(pos.x); - pos.y = IM_FLOOR(pos.y); - const float scale = g.FontSize / font->FontSize; - const char* s = text_begin; - int chars_exp = (int)(text_end - s); - int chars_rnd = 0; - const int vtx_count_max = chars_exp * 4; - const int idx_count_max = chars_exp * 6; - DrawList->PrimReserve(idx_count_max, vtx_count_max); - while (s < text_end) { - unsigned int c = (unsigned int)*s; - if (c < 0x80) { - s += 1; - } - else { - s += ImTextCharFromUtf8(&c, s, text_end); - if (c == 0) // Malformed UTF-8? - break; - } - const ImFontGlyph * glyph = font->FindGlyph((ImWchar)c); - if (glyph == NULL) { - continue; - } - DrawList->PrimQuadUV(pos + ImVec2(glyph->Y0, -glyph->X0) * scale, pos + ImVec2(glyph->Y0, -glyph->X1) * scale, - pos + ImVec2(glyph->Y1, -glyph->X1) * scale, pos + ImVec2(glyph->Y1, -glyph->X0) * scale, - ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V0), - ImVec2(glyph->U1, glyph->V1), ImVec2(glyph->U0, glyph->V1), - col); - pos.y -= glyph->AdvanceX * scale; - chars_rnd++; - } - // Give back unused vertices - int chars_skp = chars_exp-chars_rnd; - DrawList->PrimUnreserve(chars_skp*6, chars_skp*4); -} - -void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end) { - float txt_ht = ImGui::GetTextLineHeight(); - const char* title_end = ImGui::FindRenderedTextEnd(text_begin, text_end); - ImVec2 text_size; - float y = 0; - while (const char* tmp = (const char*)memchr(text_begin, '\n', title_end-text_begin)) { - text_size = ImGui::CalcTextSize(text_begin,tmp,true); - DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,tmp); - text_begin = tmp + 1; - y += txt_ht; - } - text_size = ImGui::CalcTextSize(text_begin,title_end,true); - DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,title_end); -} - -double NiceNum(double x, bool round) { - double f; - double nf; - int expv = (int)floor(ImLog10(x)); - f = x / ImPow(10.0, (double)expv); - if (round) - if (f < 1.5) - nf = 1; - else if (f < 3) - nf = 2; - else if (f < 7) - nf = 5; - else - nf = 10; - else if (f <= 1) - nf = 1; - else if (f <= 2) - nf = 2; - else if (f <= 5) - nf = 5; - else - nf = 10; - return nf * ImPow(10.0, expv); -} - -//----------------------------------------------------------------------------- -// Context Utils -//----------------------------------------------------------------------------- - -void SetImGuiContext(ImGuiContext* ctx) { - ImGui::SetCurrentContext(ctx); -} - -ImPlotContext* CreateContext() { - ImPlotContext* ctx = IM_NEW(ImPlotContext)(); - Initialize(ctx); - if (GImPlot == NULL) - SetCurrentContext(ctx); - return ctx; -} - -void DestroyContext(ImPlotContext* ctx) { - if (ctx == NULL) - ctx = GImPlot; - if (GImPlot == ctx) - SetCurrentContext(NULL); - IM_DELETE(ctx); -} - -ImPlotContext* GetCurrentContext() { - return GImPlot; -} - -void SetCurrentContext(ImPlotContext* ctx) { - GImPlot = ctx; -} - -#define IMPLOT_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32), qual) -#define IM_RGB(r,g,b) IM_COL32(r,g,b,255) - -void Initialize(ImPlotContext* ctx) { - ResetCtxForNextPlot(ctx); - ResetCtxForNextAlignedPlots(ctx); - ResetCtxForNextSubplot(ctx); - - const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396 }; - const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 }; - const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986 }; - const ImU32 Paired[] = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481}; - const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301 }; - const ImU32 Plasma[] = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752 }; - const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295 }; - const ImU32 Cool[] = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015 }; - const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295 }; - const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 }; - const ImU32 Twilight[] = {IM_RGB(226,217,226),IM_RGB(166,191,202),IM_RGB(109,144,192),IM_RGB(95,88,176),IM_RGB(83,30,124),IM_RGB(47,20,54),IM_RGB(100,25,75),IM_RGB(159,60,80),IM_RGB(192,117,94),IM_RGB(208,179,158),IM_RGB(226,217,226)}; - const ImU32 RdBu[] = {IM_RGB(103,0,31),IM_RGB(178,24,43),IM_RGB(214,96,77),IM_RGB(244,165,130),IM_RGB(253,219,199),IM_RGB(247,247,247),IM_RGB(209,229,240),IM_RGB(146,197,222),IM_RGB(67,147,195),IM_RGB(33,102,172),IM_RGB(5,48,97)}; - const ImU32 BrBG[] = {IM_RGB(84,48,5),IM_RGB(140,81,10),IM_RGB(191,129,45),IM_RGB(223,194,125),IM_RGB(246,232,195),IM_RGB(245,245,245),IM_RGB(199,234,229),IM_RGB(128,205,193),IM_RGB(53,151,143),IM_RGB(1,102,94),IM_RGB(0,60,48)}; - const ImU32 PiYG[] = {IM_RGB(142,1,82),IM_RGB(197,27,125),IM_RGB(222,119,174),IM_RGB(241,182,218),IM_RGB(253,224,239),IM_RGB(247,247,247),IM_RGB(230,245,208),IM_RGB(184,225,134),IM_RGB(127,188,65),IM_RGB(77,146,33),IM_RGB(39,100,25)}; - const ImU32 Spectral[] = {IM_RGB(158,1,66),IM_RGB(213,62,79),IM_RGB(244,109,67),IM_RGB(253,174,97),IM_RGB(254,224,139),IM_RGB(255,255,191),IM_RGB(230,245,152),IM_RGB(171,221,164),IM_RGB(102,194,165),IM_RGB(50,136,189),IM_RGB(94,79,162)}; - const ImU32 Greys[] = {IM_COL32_WHITE, IM_COL32_BLACK }; - - IMPLOT_APPEND_CMAP(Deep, true); - IMPLOT_APPEND_CMAP(Dark, true); - IMPLOT_APPEND_CMAP(Pastel, true); - IMPLOT_APPEND_CMAP(Paired, true); - IMPLOT_APPEND_CMAP(Viridis, false); - IMPLOT_APPEND_CMAP(Plasma, false); - IMPLOT_APPEND_CMAP(Hot, false); - IMPLOT_APPEND_CMAP(Cool, false); - IMPLOT_APPEND_CMAP(Pink, false); - IMPLOT_APPEND_CMAP(Jet, false); - IMPLOT_APPEND_CMAP(Twilight, false); - IMPLOT_APPEND_CMAP(RdBu, false); - IMPLOT_APPEND_CMAP(BrBG, false); - IMPLOT_APPEND_CMAP(PiYG, false); - IMPLOT_APPEND_CMAP(Spectral, false); - IMPLOT_APPEND_CMAP(Greys, false); -} - -void ResetCtxForNextPlot(ImPlotContext* ctx) { - // end child window if it was made - if (ctx->ChildWindowMade) - ImGui::EndChild(); - ctx->ChildWindowMade = false; - // reset the next plot/item data - ctx->NextPlotData.Reset(); - ctx->NextItemData.Reset(); - // reset labels - ctx->Annotations.Reset(); - ctx->Tags.Reset(); - // reset extents/fit - ctx->OpenContextThisFrame = false; - // reset digital plot items count - ctx->DigitalPlotItemCnt = 0; - ctx->DigitalPlotOffset = 0; - // nullify plot - ctx->CurrentPlot = NULL; - ctx->CurrentItem = NULL; - ctx->PreviousItem = NULL; -} - -void ResetCtxForNextAlignedPlots(ImPlotContext* ctx) { - ctx->CurrentAlignmentH = NULL; - ctx->CurrentAlignmentV = NULL; -} - -void ResetCtxForNextSubplot(ImPlotContext* ctx) { - ctx->CurrentSubplot = NULL; - ctx->CurrentAlignmentH = NULL; - ctx->CurrentAlignmentV = NULL; -} - -//----------------------------------------------------------------------------- -// Plot Utils -//----------------------------------------------------------------------------- - -ImPlotPlot* GetPlot(const char* title) { - ImGuiWindow* Window = GImGui->CurrentWindow; - const ImGuiID ID = Window->GetID(title); - return GImPlot->Plots.GetByKey(ID); -} - -ImPlotPlot* GetCurrentPlot() { - return GImPlot->CurrentPlot; -} - -void BustPlotCache() { - GImPlot->Plots.Clear(); - GImPlot->Subplots.Clear(); -} - -//----------------------------------------------------------------------------- -// Legend Utils -//----------------------------------------------------------------------------- - -ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation loc, const ImVec2& pad) { - ImVec2 pos; - if (ImHasFlag(loc, ImPlotLocation_West) && !ImHasFlag(loc, ImPlotLocation_East)) - pos.x = outer_rect.Min.x + pad.x; - else if (!ImHasFlag(loc, ImPlotLocation_West) && ImHasFlag(loc, ImPlotLocation_East)) - pos.x = outer_rect.Max.x - pad.x - inner_size.x; - else - pos.x = outer_rect.GetCenter().x - inner_size.x * 0.5f; - // legend reference point y - if (ImHasFlag(loc, ImPlotLocation_North) && !ImHasFlag(loc, ImPlotLocation_South)) - pos.y = outer_rect.Min.y + pad.y; - else if (!ImHasFlag(loc, ImPlotLocation_North) && ImHasFlag(loc, ImPlotLocation_South)) - pos.y = outer_rect.Max.y - pad.y - inner_size.y; - else - pos.y = outer_rect.GetCenter().y - inner_size.y * 0.5f; - pos.x = IM_ROUND(pos.x); - pos.y = IM_ROUND(pos.y); - return pos; -} - -ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical) { - // vars - const int nItems = items.GetLegendCount(); - const float txt_ht = ImGui::GetTextLineHeight(); - const float icon_size = txt_ht; - // get label max width - float max_label_width = 0; - float sum_label_width = 0; - for (int i = 0; i < nItems; ++i) { - const char* label = items.GetLegendLabel(i); - const float label_width = ImGui::CalcTextSize(label, NULL, true).x; - max_label_width = label_width > max_label_width ? label_width : max_label_width; - sum_label_width += label_width; - } - // calc legend size - const ImVec2 legend_size = vertical ? - ImVec2(pad.x * 2 + icon_size + max_label_width, pad.y * 2 + nItems * txt_ht + (nItems - 1) * spacing.y) : - ImVec2(pad.x * 2 + icon_size * nItems + sum_label_width + (nItems - 1) * spacing.x, pad.y * 2 + txt_ht); - return legend_size; -} - -int LegendSortingComp(void* _items, const void* _a, const void* _b) { - ImPlotItemGroup* items = (ImPlotItemGroup*)_items; - const int a = *(const int*)_a; - const int b = *(const int*)_b; - const char* label_a = items->GetLegendLabel(a); - const char* label_b = items->GetLegendLabel(b); - return strcmp(label_a,label_b); -} - -bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool hovered, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList) { - // vars - const float txt_ht = ImGui::GetTextLineHeight(); - const float icon_size = txt_ht; - const float icon_shrink = 2; - ImU32 col_txt = GetStyleColorU32(ImPlotCol_LegendText); - ImU32 col_txt_dis = ImAlphaU32(col_txt, 0.25f); - // render each legend item - float sum_label_width = 0; - bool any_item_hovered = false; - - const int num_items = items.GetLegendCount(); - if (num_items < 1) - return hovered; - // ImVector& indices = GImPlot->TempInt1; - // indices.resize(num_items); - // // bool sort = true; - // // if (sort && num_items > 1) { - // // qsort_s(indices.Data, num_items, sizeof(int), LegendSortingComp, &items); - // // } - // // else { - // // for (int i = 0; i < num_items; ++i) - // // indices[i] = i; - // // } - for (int i = 0; i < num_items; ++i) { - const int idx = i; //indices[i]; - ImPlotItem* item = items.GetLegendItem(idx); - const char* label = items.GetLegendLabel(idx); - const float label_width = ImGui::CalcTextSize(label, NULL, true).x; - const ImVec2 top_left = vertical ? - legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y)) : - legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0); - sum_label_width += label_width; - ImRect icon_bb; - icon_bb.Min = top_left + ImVec2(icon_shrink,icon_shrink); - icon_bb.Max = top_left + ImVec2(icon_size - icon_shrink, icon_size - icon_shrink); - ImRect label_bb; - label_bb.Min = top_left; - label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size); - ImU32 col_txt_hl; - ImU32 col_item = ImAlphaU32(item->Color,1); - - ImRect button_bb(icon_bb.Min, label_bb.Max); - - ImGui::KeepAliveID(item->ID); - - bool item_hov = false; - bool item_hld = false; - bool item_clk = ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoButtons) - ? false - : ImGui::ButtonBehavior(button_bb, item->ID, &item_hov, &item_hld); - - if (item_clk) - item->Show = !item->Show; - - - const bool can_hover = (item_hov) - && (!ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightItem) - || !ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)); - - if (can_hover) { - item->LegendHoverRect.Min = icon_bb.Min; - item->LegendHoverRect.Max = label_bb.Max; - item->LegendHovered = true; - col_txt_hl = ImMixU32(col_txt, col_item, 64); - any_item_hovered = true; - } - else { - col_txt_hl = ImGui::GetColorU32(col_txt); - } - ImU32 col_icon; - if (item_hld) - col_icon = item->Show ? ImAlphaU32(col_item,0.5f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f); - else if (item_hov) - col_icon = item->Show ? ImAlphaU32(col_item,0.75f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.75f); - else - col_icon = item->Show ? col_item : col_txt_dis; - - DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, col_icon); - const char* text_display_end = ImGui::FindRenderedTextEnd(label, NULL); - if (label != text_display_end) - DrawList.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_txt_hl : col_txt_dis, label, text_display_end); - } - return hovered && !any_item_hovered; -} - -//----------------------------------------------------------------------------- -// Locators -//----------------------------------------------------------------------------- - -static const float TICK_FILL_X = 0.8f; -static const float TICK_FILL_Y = 1.0f; - -void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { - if (range.Min == range.Max) - return; - const int nMinor = 10; - const int nMajor = ImMax(2, (int)IM_ROUND(pixels / (vertical ? 300.0f : 400.0f))); - const double nice_range = NiceNum(range.Size() * 0.99, false); - const double interval = NiceNum(nice_range / (nMajor - 1), true); - const double graphmin = floor(range.Min / interval) * interval; - const double graphmax = ceil(range.Max / interval) * interval; - bool first_major_set = false; - int first_major_idx = 0; - const int idx0 = ticker.TickCount(); // ticker may have user custom ticks - ImVec2 total_size(0,0); - for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) { - // is this zero? combat zero formatting issues - if (major-interval < 0 && major+interval > 0) - major = 0; - if (range.Contains(major)) { - if (!first_major_set) { - first_major_idx = ticker.TickCount(); - first_major_set = true; - } - total_size += ticker.AddTick(major, true, 0, true, formatter, formatter_data).LabelSize; - } - for (int i = 1; i < nMinor; ++i) { - double minor = major + i * interval / nMinor; - if (range.Contains(minor)) { - total_size += ticker.AddTick(minor, false, 0, true, formatter, formatter_data).LabelSize; - } - } - } - // prune if necessary - if ((!vertical && total_size.x > pixels*TICK_FILL_X) || (vertical && total_size.y > pixels*TICK_FILL_Y)) { - for (int i = first_major_idx-1; i >= idx0; i -= 2) - ticker.Ticks[i].ShowLabel = false; - for (int i = first_major_idx+1; i < ticker.TickCount(); i += 2) - ticker.Ticks[i].ShowLabel = false; - } -} - -bool CalcLogarithmicExponents(const ImPlotRange& range, float pix, bool vertical, int& exp_min, int& exp_max, int& exp_step) { - if (range.Min * range.Max > 0) { - const int nMajor = vertical ? ImMax(2, (int)IM_ROUND(pix * 0.02f)) : ImMax(2, (int)IM_ROUND(pix * 0.01f)); // TODO: magic numbers - double log_min = ImLog10(ImAbs(range.Min)); - double log_max = ImLog10(ImAbs(range.Max)); - double log_a = ImMin(log_min,log_max); - double log_b = ImMax(log_min,log_max); - exp_step = ImMax(1,(int)(log_b - log_a) / nMajor); - exp_min = (int)log_a; - exp_max = (int)log_b; - if (exp_step != 1) { - while(exp_step % 3 != 0) exp_step++; // make step size multiple of three - while(exp_min % exp_step != 0) exp_min--; // decrease exp_min until exp_min + N * exp_step will be 0 - } - return true; - } - return false; -} - -void AddTicksLogarithmic(const ImPlotRange& range, int exp_min, int exp_max, int exp_step, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) { - const double sign = ImSign(range.Max); - for (int e = exp_min - exp_step; e < (exp_max + exp_step); e += exp_step) { - double major1 = sign*ImPow(10, (double)(e)); - double major2 = sign*ImPow(10, (double)(e + 1)); - double interval = (major2 - major1) / 9; - if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON)) - ticker.AddTick(major1, true, 0, true, formatter, data); - for (int j = 0; j < exp_step; ++j) { - major1 = sign*ImPow(10, (double)(e+j)); - major2 = sign*ImPow(10, (double)(e+j+1)); - interval = (major2 - major1) / 9; - for (int i = 1; i < (9 + (int)(j < (exp_step - 1))); ++i) { - double minor = major1 + i * interval; - if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON)) - ticker.AddTick(minor, false, 0, false, formatter, data); - } - } - } -} - -void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { - int exp_min, exp_max, exp_step; - if (CalcLogarithmicExponents(range, pixels, vertical, exp_min, exp_max, exp_step)) - AddTicksLogarithmic(range, exp_min, exp_max, exp_step, ticker, formatter, formatter_data); -} - -float CalcSymLogPixel(double plt, const ImPlotRange& range, float pixels) { - double scaleToPixels = pixels / range.Size(); - double scaleMin = TransformForward_SymLog(range.Min,NULL); - double scaleMax = TransformForward_SymLog(range.Max,NULL); - double s = TransformForward_SymLog(plt, NULL); - double t = (s - scaleMin) / (scaleMax - scaleMin); - plt = range.Min + range.Size() * t; - - return (float)(0 + scaleToPixels * (plt - range.Min)); -} - -void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { - if (range.Min >= -1 && range.Max <= 1) { - Locator_Default(ticker, range, pixels, vertical, formatter, formatter_data); - } - else if (range.Min * range.Max < 0) { // cross zero - const float pix_min = 0; - const float pix_max = pixels; - const float pix_p1 = CalcSymLogPixel(1, range, pixels); - const float pix_n1 = CalcSymLogPixel(-1, range, pixels); - int exp_min_p, exp_max_p, exp_step_p; - int exp_min_n, exp_max_n, exp_step_n; - CalcLogarithmicExponents(ImPlotRange(1,range.Max), ImAbs(pix_max-pix_p1),vertical,exp_min_p,exp_max_p,exp_step_p); - CalcLogarithmicExponents(ImPlotRange(range.Min,-1),ImAbs(pix_n1-pix_min),vertical,exp_min_n,exp_max_n,exp_step_n); - int exp_step = ImMax(exp_step_n, exp_step_p); - ticker.AddTick(0,true,0,true,formatter,formatter_data); - AddTicksLogarithmic(ImPlotRange(1,range.Max), exp_min_p,exp_max_p,exp_step,ticker,formatter,formatter_data); - AddTicksLogarithmic(ImPlotRange(range.Min,-1),exp_min_n,exp_max_n,exp_step,ticker,formatter,formatter_data); - } - else { - Locator_Log10(ticker, range, pixels, vertical, formatter, formatter_data); - } -} - -void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) { - for (int i = 0; i < n; ++i) { - if (labels != NULL) - ticker.AddTick(values[i], false, 0, true, labels[i]); - else - ticker.AddTick(values[i], false, 0, true, formatter, data); - } -} - -//----------------------------------------------------------------------------- -// Time Ticks and Utils -//----------------------------------------------------------------------------- - -// this may not be thread safe? -static const double TimeUnitSpans[ImPlotTimeUnit_COUNT] = { - 0.000001, - 0.001, - 1, - 60, - 3600, - 86400, - 2629800, - 31557600 -}; - -inline ImPlotTimeUnit GetUnitForRange(double range) { - static double cutoffs[ImPlotTimeUnit_COUNT] = {0.001, 1, 60, 3600, 86400, 2629800, 31557600, IMPLOT_MAX_TIME}; - for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) { - if (range <= cutoffs[i]) - return (ImPlotTimeUnit)i; - } - return ImPlotTimeUnit_Yr; -} - -inline int LowerBoundStep(int max_divs, const int* divs, const int* step, int size) { - if (max_divs < divs[0]) - return 0; - for (int i = 1; i < size; ++i) { - if (max_divs < divs[i]) - return step[i-1]; - } - return step[size-1]; -} - -inline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) { - if (unit == ImPlotTimeUnit_Ms || unit == ImPlotTimeUnit_Us) { - static const int step[] = {500,250,200,100,50,25,20,10,5,2,1}; - static const int divs[] = {2,4,5,10,20,40,50,100,200,500,1000}; - return LowerBoundStep(max_divs, divs, step, 11); - } - if (unit == ImPlotTimeUnit_S || unit == ImPlotTimeUnit_Min) { - static const int step[] = {30,15,10,5,1}; - static const int divs[] = {2,4,6,12,60}; - return LowerBoundStep(max_divs, divs, step, 5); - } - else if (unit == ImPlotTimeUnit_Hr) { - static const int step[] = {12,6,3,2,1}; - static const int divs[] = {2,4,8,12,24}; - return LowerBoundStep(max_divs, divs, step, 5); - } - else if (unit == ImPlotTimeUnit_Day) { - static const int step[] = {14,7,2,1}; - static const int divs[] = {2,4,14,28}; - return LowerBoundStep(max_divs, divs, step, 4); - } - else if (unit == ImPlotTimeUnit_Mo) { - static const int step[] = {6,3,2,1}; - static const int divs[] = {2,4,6,12}; - return LowerBoundStep(max_divs, divs, step, 4); - } - return 0; -} - -ImPlotTime MkGmtTime(struct tm *ptm) { - ImPlotTime t; -#ifdef _WIN32 - t.S = _mkgmtime(ptm); -#else - t.S = timegm(ptm); -#endif - if (t.S < 0) - t.S = 0; - return t; -} - -tm* GetGmtTime(const ImPlotTime& t, tm* ptm) -{ -#ifdef _WIN32 - if (gmtime_s(ptm, &t.S) == 0) - return ptm; - else - return NULL; -#else - return gmtime_r(&t.S, ptm); -#endif -} - -ImPlotTime MkLocTime(struct tm *ptm) { - ImPlotTime t; - t.S = mktime(ptm); - if (t.S < 0) - t.S = 0; - return t; -} - -tm* GetLocTime(const ImPlotTime& t, tm* ptm) { -#ifdef _WIN32 - if (localtime_s(ptm, &t.S) == 0) - return ptm; - else - return NULL; -#else - return localtime_r(&t.S, ptm); -#endif -} - -inline ImPlotTime MkTime(struct tm *ptm) { - if (GetStyle().UseLocalTime) - return MkLocTime(ptm); - else - return MkGmtTime(ptm); -} - -inline tm* GetTime(const ImPlotTime& t, tm* ptm) { - if (GetStyle().UseLocalTime) - return GetLocTime(t,ptm); - else - return GetGmtTime(t,ptm); -} - -ImPlotTime MakeTime(int year, int month, int day, int hour, int min, int sec, int us) { - tm& Tm = GImPlot->Tm; - - int yr = year - 1900; - if (yr < 0) - yr = 0; - - sec = sec + us / 1000000; - us = us % 1000000; - - Tm.tm_sec = sec; - Tm.tm_min = min; - Tm.tm_hour = hour; - Tm.tm_mday = day; - Tm.tm_mon = month; - Tm.tm_year = yr; - - ImPlotTime t = MkTime(&Tm); - - t.Us = us; - return t; -} - -int GetYear(const ImPlotTime& t) { - tm& Tm = GImPlot->Tm; - GetTime(t, &Tm); - return Tm.tm_year + 1900; -} - -ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) { - tm& Tm = GImPlot->Tm; - ImPlotTime t_out = t; - switch(unit) { - case ImPlotTimeUnit_Us: t_out.Us += count; break; - case ImPlotTimeUnit_Ms: t_out.Us += count * 1000; break; - case ImPlotTimeUnit_S: t_out.S += count; break; - case ImPlotTimeUnit_Min: t_out.S += count * 60; break; - case ImPlotTimeUnit_Hr: t_out.S += count * 3600; break; - case ImPlotTimeUnit_Day: t_out.S += count * 86400; break; - case ImPlotTimeUnit_Mo: for (int i = 0; i < abs(count); ++i) { - GetTime(t_out, &Tm); - if (count > 0) - t_out.S += 86400 * GetDaysInMonth(Tm.tm_year + 1900, Tm.tm_mon); - else if (count < 0) - t_out.S -= 86400 * GetDaysInMonth(Tm.tm_year + 1900 - (Tm.tm_mon == 0 ? 1 : 0), Tm.tm_mon == 0 ? 11 : Tm.tm_mon - 1); // NOT WORKING - } - break; - case ImPlotTimeUnit_Yr: for (int i = 0; i < abs(count); ++i) { - if (count > 0) - t_out.S += 86400 * (365 + (int)IsLeapYear(GetYear(t_out))); - else if (count < 0) - t_out.S -= 86400 * (365 + (int)IsLeapYear(GetYear(t_out) - 1)); - // this is incorrect if leap year and we are past Feb 28 - } - break; - default: break; - } - t_out.RollOver(); - return t_out; -} - -ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit) { - GetTime(t, &GImPlot->Tm); - switch (unit) { - case ImPlotTimeUnit_S: return ImPlotTime(t.S, 0); - case ImPlotTimeUnit_Ms: return ImPlotTime(t.S, (t.Us / 1000) * 1000); - case ImPlotTimeUnit_Us: return t; - case ImPlotTimeUnit_Yr: GImPlot->Tm.tm_mon = 0; // fall-through - case ImPlotTimeUnit_Mo: GImPlot->Tm.tm_mday = 1; // fall-through - case ImPlotTimeUnit_Day: GImPlot->Tm.tm_hour = 0; // fall-through - case ImPlotTimeUnit_Hr: GImPlot->Tm.tm_min = 0; // fall-through - case ImPlotTimeUnit_Min: GImPlot->Tm.tm_sec = 0; break; - default: return t; - } - return MkTime(&GImPlot->Tm); -} - -ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit) { - return AddTime(FloorTime(t, unit), unit, 1); -} - -ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit) { - ImPlotTime t1 = FloorTime(t, unit); - ImPlotTime t2 = AddTime(t1,unit,1); - if (t1.S == t2.S) - return t.Us - t1.Us < t2.Us - t.Us ? t1 : t2; - return t.S - t1.S < t2.S - t.S ? t1 : t2; -} - -ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& tod_part) { - tm& Tm = GImPlot->Tm; - GetTime(date_part, &GImPlot->Tm); - int y = Tm.tm_year; - int m = Tm.tm_mon; - int d = Tm.tm_mday; - GetTime(tod_part, &GImPlot->Tm); - Tm.tm_year = y; - Tm.tm_mon = m; - Tm.tm_mday = d; - ImPlotTime t = MkTime(&Tm); - t.Us = tod_part.Us; - return t; -} - -// TODO: allow users to define these -static const char* MONTH_NAMES[] = {"January","February","March","April","May","June","July","August","September","October","November","December"}; -static const char* WD_ABRVS[] = {"Su","Mo","Tu","We","Th","Fr","Sa"}; -static const char* MONTH_ABRVS[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; - -int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, bool use_24_hr_clk) { - tm& Tm = GImPlot->Tm; - GetTime(t, &Tm); - const int us = t.Us % 1000; - const int ms = t.Us / 1000; - const int sec = Tm.tm_sec; - const int min = Tm.tm_min; - if (use_24_hr_clk) { - const int hr = Tm.tm_hour; - switch(fmt) { - case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us); - case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us); - case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms); - case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec); - case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%02d:%02d:%02d.%03d", hr, min, sec, ms); - case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%02d:%02d:%02d", hr, min, sec); - case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%02d:%02d", hr, min); - case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%02d:00", hr); - default: return 0; - } - } - else { - const char* ap = Tm.tm_hour < 12 ? "am" : "pm"; - const int hr = (Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12; - switch(fmt) { - case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us); - case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us); - case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms); - case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec); - case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%d:%02d:%02d.%03d%s", hr, min, sec, ms, ap); - case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%d:%02d:%02d%s", hr, min, sec, ap); - case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%d:%02d%s", hr, min, ap); - case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%d%s", hr, ap); - default: return 0; - } - } -} - -int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601) { - tm& Tm = GImPlot->Tm; - GetTime(t, &Tm); - const int day = Tm.tm_mday; - const int mon = Tm.tm_mon + 1; - const int year = Tm.tm_year + 1900; - const int yr = year % 100; - if (use_iso_8601) { - switch (fmt) { - case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "--%02d-%02d", mon, day); - case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d-%02d-%02d", year, mon, day); - case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%d-%02d", year, mon); - case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "--%02d", mon); - case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year); - default: return 0; - } - } - else { - switch (fmt) { - case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "%d/%d", mon, day); - case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d/%d/%02d", mon, day, yr); - case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%s %d", MONTH_ABRVS[Tm.tm_mon], year); - case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "%s", MONTH_ABRVS[Tm.tm_mon]); - case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year); - default: return 0; - } - } - } - -int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt) { - int written = 0; - if (fmt.Date != ImPlotDateFmt_None) - written += FormatDate(t, buffer, size, fmt.Date, fmt.UseISO8601); - if (fmt.Time != ImPlotTimeFmt_None) { - if (fmt.Date != ImPlotDateFmt_None) - buffer[written++] = ' '; - written += FormatTime(t, &buffer[written], size - written, fmt.Time, fmt.Use24HourClock); - } - return written; -} - -inline float GetDateTimeWidth(ImPlotDateTimeSpec fmt) { - static const ImPlotTime t_max_width = MakeTime(2888, 12, 22, 12, 58, 58, 888888); // best guess at time that maximizes pixel width - char buffer[32]; - FormatDateTime(t_max_width, buffer, 32, fmt); - return ImGui::CalcTextSize(buffer).x; -} - -inline bool TimeLabelSame(const char* l1, const char* l2) { - size_t len1 = strlen(l1); - size_t len2 = strlen(l2); - size_t n = len1 < len2 ? len1 : len2; - return strcmp(l1 + len1 - n, l2 + len2 - n) == 0; -} - -static const ImPlotDateTimeSpec TimeFormatLevel0[ImPlotTimeUnit_COUNT] = { - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_S), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Hr), - ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_None), - ImPlotDateTimeSpec(ImPlotDateFmt_Mo, ImPlotTimeFmt_None), - ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) -}; - -static const ImPlotDateTimeSpec TimeFormatLevel1[ImPlotTimeUnit_COUNT] = { - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), - ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), - ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), - ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), - ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) -}; - -static const ImPlotDateTimeSpec TimeFormatLevel1First[ImPlotTimeUnit_COUNT] = { - ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), - ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), - ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), - ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), - ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), - ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), - ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), - ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) -}; - -static const ImPlotDateTimeSpec TimeFormatMouseCursor[ImPlotTimeUnit_COUNT] = { - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SUs), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), - ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), - ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_Hr), - ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), - ImPlotDateTimeSpec(ImPlotDateFmt_MoYr, ImPlotTimeFmt_None) -}; - -inline ImPlotDateTimeSpec GetDateTimeFmt(const ImPlotDateTimeSpec* ctx, ImPlotTimeUnit idx) { - ImPlotStyle& style = GetStyle(); - ImPlotDateTimeSpec fmt = ctx[idx]; - fmt.UseISO8601 = style.UseISO8601; - fmt.Use24HourClock = style.Use24HourClock; - return fmt; -} - -void Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { - IM_ASSERT_USER_ERROR(vertical == false, "Cannot locate Time ticks on vertical axis!"); - (void)vertical; - // get units for level 0 and level 1 labels - const ImPlotTimeUnit unit0 = GetUnitForRange(range.Size() / (pixels / 100)); // level = 0 (top) - const ImPlotTimeUnit unit1 = unit0 + 1; // level = 1 (bottom) - // get time format specs - const ImPlotDateTimeSpec fmt0 = GetDateTimeFmt(TimeFormatLevel0, unit0); - const ImPlotDateTimeSpec fmt1 = GetDateTimeFmt(TimeFormatLevel1, unit1); - const ImPlotDateTimeSpec fmtf = GetDateTimeFmt(TimeFormatLevel1First, unit1); - // min max times - const ImPlotTime t_min = ImPlotTime::FromDouble(range.Min); - const ImPlotTime t_max = ImPlotTime::FromDouble(range.Max); - // maximum allowable density of labels - const float max_density = 0.5f; - // book keeping - int last_major_offset = -1; - // formatter data - Formatter_Time_Data ftd; - ftd.UserFormatter = formatter; - ftd.UserFormatterData = formatter_data; - if (unit0 != ImPlotTimeUnit_Yr) { - // pixels per major (level 1) division - const float pix_per_major_div = pixels / (float)(range.Size() / TimeUnitSpans[unit1]); - // nominal pixels taken up by labels - const float fmt0_width = GetDateTimeWidth(fmt0); - const float fmt1_width = GetDateTimeWidth(fmt1); - const float fmtf_width = GetDateTimeWidth(fmtf); - // the maximum number of minor (level 0) labels that can fit between major (level 1) divisions - const int minor_per_major = (int)(max_density * pix_per_major_div / fmt0_width); - // the minor step size (level 0) - const int step = GetTimeStep(minor_per_major, unit0); - // generate ticks - ImPlotTime t1 = FloorTime(ImPlotTime::FromDouble(range.Min), unit1); - while (t1 < t_max) { - // get next major - const ImPlotTime t2 = AddTime(t1, unit1, 1); - // add major tick - if (t1 >= t_min && t1 <= t_max) { - // minor level 0 tick - ftd.Time = t1; ftd.Spec = fmt0; - ticker.AddTick(t1.ToDouble(), true, 0, true, Formatter_Time, &ftd); - // major level 1 tick - ftd.Time = t1; ftd.Spec = last_major_offset < 0 ? fmtf : fmt1; - ImPlotTick& tick_maj = ticker.AddTick(t1.ToDouble(), true, 1, true, Formatter_Time, &ftd); - const char* this_major = ticker.GetText(tick_maj); - if (last_major_offset >= 0 && TimeLabelSame(ticker.TextBuffer.Buf.Data + last_major_offset, this_major)) - tick_maj.ShowLabel = false; - last_major_offset = tick_maj.TextOffset; - } - // add minor ticks up until next major - if (minor_per_major > 1 && (t_min <= t2 && t1 <= t_max)) { - ImPlotTime t12 = AddTime(t1, unit0, step); - while (t12 < t2) { - float px_to_t2 = (float)((t2 - t12).ToDouble()/range.Size()) * pixels; - if (t12 >= t_min && t12 <= t_max) { - ftd.Time = t12; ftd.Spec = fmt0; - ticker.AddTick(t12.ToDouble(), false, 0, px_to_t2 >= fmt0_width, Formatter_Time, &ftd); - if (last_major_offset < 0 && px_to_t2 >= fmt0_width && px_to_t2 >= (fmt1_width + fmtf_width) / 2) { - ftd.Time = t12; ftd.Spec = fmtf; - ImPlotTick& tick_maj = ticker.AddTick(t12.ToDouble(), true, 1, true, Formatter_Time, &ftd); - last_major_offset = tick_maj.TextOffset; - } - } - t12 = AddTime(t12, unit0, step); - } - } - t1 = t2; - } - } - else { - const ImPlotDateTimeSpec fmty = GetDateTimeFmt(TimeFormatLevel0, ImPlotTimeUnit_Yr); - const float label_width = GetDateTimeWidth(fmty); - const int max_labels = (int)(max_density * pixels / label_width); - const int year_min = GetYear(t_min); - const int year_max = GetYear(CeilTime(t_max, ImPlotTimeUnit_Yr)); - const double nice_range = NiceNum((year_max - year_min)*0.99,false); - const double interval = NiceNum(nice_range / (max_labels - 1), true); - const int graphmin = (int)(floor(year_min / interval) * interval); - const int graphmax = (int)(ceil(year_max / interval) * interval); - const int step = (int)interval <= 0 ? 1 : (int)interval; - - for (int y = graphmin; y < graphmax; y += step) { - ImPlotTime t = MakeTime(y); - if (t >= t_min && t <= t_max) { - ftd.Time = t; ftd.Spec = fmty; - ticker.AddTick(t.ToDouble(), true, 0, true, Formatter_Time, &ftd); - } - } - } -} - -//----------------------------------------------------------------------------- -// Context Menu -//----------------------------------------------------------------------------- - -template -bool DragFloat(const char*, F*, float, F, F) { - return false; -} - -template <> -bool DragFloat(const char* label, double* v, float v_speed, double v_min, double v_max) { - return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3f", 1); -} - -template <> -bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max) { - return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3f", 1); -} - -inline void BeginDisabledControls(bool cond) { - if (cond) { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); - } -} - -inline void EndDisabledControls(bool cond) { - if (cond) { - ImGui::PopItemFlag(); - ImGui::PopStyleVar(); - } -} - -void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool /*time_allowed*/) { - - ImGui::PushItemWidth(75); - bool always_locked = axis.IsRangeLocked() || axis.IsAutoFitting(); - bool label = axis.HasLabel(); - bool grid = axis.HasGridLines(); - bool ticks = axis.HasTickMarks(); - bool labels = axis.HasTickLabels(); - double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits. - - if (axis.Scale == ImPlotScale_Time) { - ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min); - ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max); - - BeginDisabledControls(always_locked); - ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); - EndDisabledControls(always_locked); - ImGui::SameLine(); - BeginDisabledControls(axis.IsLockedMin() || always_locked); - if (ImGui::BeginMenu("Min Time")) { - if (ShowTimePicker("mintime", &tmin)) { - if (tmin >= tmax) - tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); - axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); - } - ImGui::Separator(); - if (ShowDatePicker("mindate",&axis.PickerLevel,&axis.PickerTimeMin,&tmin,&tmax)) { - tmin = CombineDateTime(axis.PickerTimeMin, tmin); - if (tmin >= tmax) - tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); - axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); - } - ImGui::EndMenu(); - } - EndDisabledControls(axis.IsLockedMin() || always_locked); - - BeginDisabledControls(always_locked); - ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); - EndDisabledControls(always_locked); - ImGui::SameLine(); - BeginDisabledControls(axis.IsLockedMax() || always_locked); - if (ImGui::BeginMenu("Max Time")) { - if (ShowTimePicker("maxtime", &tmax)) { - if (tmax <= tmin) - tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); - axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); - } - ImGui::Separator(); - if (ShowDatePicker("maxdate",&axis.PickerLevel,&axis.PickerTimeMax,&tmin,&tmax)) { - tmax = CombineDateTime(axis.PickerTimeMax, tmax); - if (tmax <= tmin) - tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); - axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); - } - ImGui::EndMenu(); - } - EndDisabledControls(axis.IsLockedMax() || always_locked); - } - else { - BeginDisabledControls(always_locked); - ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); - EndDisabledControls(always_locked); - ImGui::SameLine(); - BeginDisabledControls(axis.IsLockedMin() || always_locked); - double temp_min = axis.Range.Min; - if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON)) { - axis.SetMin(temp_min,true); - if (equal_axis != NULL) - equal_axis->SetAspect(axis.GetAspect()); - } - EndDisabledControls(axis.IsLockedMin() || always_locked); - - BeginDisabledControls(always_locked); - ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); - EndDisabledControls(always_locked); - ImGui::SameLine(); - BeginDisabledControls(axis.IsLockedMax() || always_locked); - double temp_max = axis.Range.Max; - if (DragFloat("Max", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL)) { - axis.SetMax(temp_max,true); - if (equal_axis != NULL) - equal_axis->SetAspect(axis.GetAspect()); - } - EndDisabledControls(axis.IsLockedMax() || always_locked); - } - - ImGui::Separator(); - - ImGui::CheckboxFlags("Auto-Fit",(unsigned int*)&axis.Flags, ImPlotAxisFlags_AutoFit); - // TODO - // BeginDisabledControls(axis.IsTime() && time_allowed); - // ImGui::CheckboxFlags("Log Scale",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale); - // EndDisabledControls(axis.IsTime() && time_allowed); - // if (time_allowed) { - // BeginDisabledControls(axis.IsLog() || axis.IsSymLog()); - // ImGui::CheckboxFlags("Time",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time); - // EndDisabledControls(axis.IsLog() || axis.IsSymLog()); - // } - ImGui::Separator(); - ImGui::CheckboxFlags("Invert",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Invert); - ImGui::CheckboxFlags("Opposite",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Opposite); - ImGui::Separator(); - BeginDisabledControls(axis.LabelOffset == -1); - if (ImGui::Checkbox("Label", &label)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoLabel); - EndDisabledControls(axis.LabelOffset == -1); - if (ImGui::Checkbox("Grid Lines", &grid)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); - if (ImGui::Checkbox("Tick Marks", &ticks)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); - if (ImGui::Checkbox("Tick Labels", &labels)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); - -} - -bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible) { - const float s = ImGui::GetFrameHeight(); - bool ret = false; - if (ImGui::Checkbox("Show",&visible)) - ret = true; - if (legend.CanGoInside) - ImGui::CheckboxFlags("Outside",(unsigned int*)&legend.Flags, ImPlotLegendFlags_Outside); - if (ImGui::RadioButton("H", ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal))) - legend.Flags |= ImPlotLegendFlags_Horizontal; - ImGui::SameLine(); - if (ImGui::RadioButton("V", !ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal))) - legend.Flags &= ~ImPlotLegendFlags_Horizontal; - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2,2)); - if (ImGui::Button("NW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthWest; } ImGui::SameLine(); - if (ImGui::Button("N", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_North; } ImGui::SameLine(); - if (ImGui::Button("NE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthEast; } - if (ImGui::Button("W", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_West; } ImGui::SameLine(); - if (ImGui::InvisibleButton("C", ImVec2(1.5f*s,s))) { } ImGui::SameLine(); - if (ImGui::Button("E", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_East; } - if (ImGui::Button("SW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthWest; } ImGui::SameLine(); - if (ImGui::Button("S", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_South; } ImGui::SameLine(); - if (ImGui::Button("SE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthEast; } - ImGui::PopStyleVar(); - return ret; -} - -void ShowSubplotsContextMenu(ImPlotSubplot& subplot) { - if ((ImGui::BeginMenu("Linking"))) { - if (ImGui::MenuItem("Link Rows",NULL,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows))) - ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows); - if (ImGui::MenuItem("Link Cols",NULL,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols))) - ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols); - if (ImGui::MenuItem("Link All X",NULL,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX))) - ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX); - if (ImGui::MenuItem("Link All Y",NULL,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY))) - ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY); - ImGui::EndMenu(); - } - if ((ImGui::BeginMenu("Settings"))) { - BeginDisabledControls(!subplot.HasTitle); - if (ImGui::MenuItem("Title",NULL,subplot.HasTitle && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle))) - ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle); - EndDisabledControls(!subplot.HasTitle); - if (ImGui::MenuItem("Resizable",NULL,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize))) - ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoResize); - if (ImGui::MenuItem("Align",NULL,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign))) - ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign); - if (ImGui::MenuItem("Share Items",NULL,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems))) - ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); - ImGui::EndMenu(); - } -} - -void ShowPlotContextMenu(ImPlotPlot& plot) { - const bool owns_legend = GImPlot->CurrentItems == &plot.Items; - const bool equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); - - char buf[16] = {}; - - for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { - ImPlotAxis& x_axis = plot.XAxis(i); - if (!x_axis.Enabled || !x_axis.HasMenus()) - continue; - ImGui::PushID(i); - ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "X-Axis" : "X-Axis %d", i + 1); - if (ImGui::BeginMenu(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : buf)) { - ShowAxisContextMenu(x_axis, equal ? x_axis.OrthoAxis : NULL, false); - ImGui::EndMenu(); - } - ImGui::PopID(); - } - - for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { - ImPlotAxis& y_axis = plot.YAxis(i); - if (!y_axis.Enabled || !y_axis.HasMenus()) - continue; - ImGui::PushID(i); - ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1); - if (ImGui::BeginMenu(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : buf)) { - ShowAxisContextMenu(y_axis, equal ? y_axis.OrthoAxis : NULL, false); - ImGui::EndMenu(); - } - ImGui::PopID(); - } - - ImGui::Separator(); - if (!ImHasFlag(GImPlot->CurrentItems->Legend.Flags, ImPlotLegendFlags_NoMenus)) { - if ((ImGui::BeginMenu("Legend"))) { - if (owns_legend) { - if (ShowLegendContextMenu(plot.Items.Legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) - ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); - } - else if (GImPlot->CurrentSubplot != NULL) { - if (ShowLegendContextMenu(GImPlot->CurrentSubplot->Items.Legend, !ImHasFlag(GImPlot->CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend))) - ImFlipFlag(GImPlot->CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend); - } - ImGui::EndMenu(); - } - } - if ((ImGui::BeginMenu("Settings"))) { - if (ImGui::MenuItem("Equal", NULL, ImHasFlag(plot.Flags, ImPlotFlags_Equal))) - ImFlipFlag(plot.Flags, ImPlotFlags_Equal); - if (ImGui::MenuItem("Box Select",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect))) - ImFlipFlag(plot.Flags, ImPlotFlags_NoBoxSelect); - BeginDisabledControls(plot.TitleOffset == -1); - if (ImGui::MenuItem("Title",NULL,plot.HasTitle())) - ImFlipFlag(plot.Flags, ImPlotFlags_NoTitle); - EndDisabledControls(plot.TitleOffset == -1); - if (ImGui::MenuItem("Mouse Position",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText))) - ImFlipFlag(plot.Flags, ImPlotFlags_NoMouseText); - if (ImGui::MenuItem("Crosshairs",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs))) - ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs); - ImGui::EndMenu(); - } - if (GImPlot->CurrentSubplot != NULL && !ImHasFlag(GImPlot->CurrentPlot->Flags, ImPlotSubplotFlags_NoMenus)) { - ImGui::Separator(); - if ((ImGui::BeginMenu("Subplots"))) { - ShowSubplotsContextMenu(*GImPlot->CurrentSubplot); - ImGui::EndMenu(); - } - } -} - -//----------------------------------------------------------------------------- -// Axis Utils -//----------------------------------------------------------------------------- - -static inline int AxisPrecision(const ImPlotAxis& axis) { - const double range = axis.Ticker.TickCount() > 1 ? (axis.Ticker.Ticks[1].PlotPos - axis.Ticker.Ticks[0].PlotPos) : axis.Range.Size(); - return Precision(range); -} - -static inline double RoundAxisValue(const ImPlotAxis& axis, double value) { - return RoundTo(value, AxisPrecision(axis)); -} - -void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round) { - ImPlotContext& gp = *GImPlot; - // TODO: We shouldn't explicitly check that the axis is Time here. Ideally, - // Formatter_Time would handle the formatting for us, but the code below - // needs additional arguments which are not currently available in ImPlotFormatter - if (axis.Locator == Locator_Time) { - ImPlotTimeUnit unit = axis.Vertical - ? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100)) // TODO: magic value! - : GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100)); // TODO: magic value! - FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit)); - } - else { - if (round) - value = RoundAxisValue(axis, value); - axis.Formatter(value, buff, size, axis.FormatterData); - } -} - -void UpdateAxisColors(ImPlotAxis& axis) { - const ImVec4 col_grid = GetStyleColorVec4(ImPlotCol_AxisGrid); - axis.ColorMaj = ImGui::GetColorU32(col_grid); - axis.ColorMin = ImGui::GetColorU32(col_grid*ImVec4(1,1,1,GImPlot->Style.MinorAlpha)); - axis.ColorTick = GetStyleColorU32(ImPlotCol_AxisTick); - axis.ColorTxt = GetStyleColorU32(ImPlotCol_AxisText); - axis.ColorBg = GetStyleColorU32(ImPlotCol_AxisBg); - axis.ColorHov = GetStyleColorU32(ImPlotCol_AxisBgHovered); - axis.ColorAct = GetStyleColorU32(ImPlotCol_AxisBgActive); - // axis.ColorHiLi = IM_COL32_BLACK_TRANS; -} - -void PadAndDatumAxesX(ImPlotPlot& plot, float& pad_T, float& pad_B, ImPlotAlignmentData* align) { - - ImPlotContext& gp = *GImPlot; - - const float T = ImGui::GetTextLineHeight(); - const float P = gp.Style.LabelPadding.y; - const float K = gp.Style.MinorTickLen.x; - - int count_T = 0; - int count_B = 0; - float last_T = plot.AxesRect.Min.y; - float last_B = plot.AxesRect.Max.y; - - for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { // FYI: can iterate forward - ImPlotAxis& axis = plot.XAxis(i); - if (!axis.Enabled) - continue; - const bool label = axis.HasLabel(); - const bool ticks = axis.HasTickLabels(); - const bool opp = axis.IsOpposite(); - const bool time = axis.Scale == ImPlotScale_Time; - if (opp) { - if (count_T++ > 0) - pad_T += K + P; - if (label) - pad_T += T + P; - if (ticks) - pad_T += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0); - axis.Datum1 = plot.CanvasRect.Min.y + pad_T; - axis.Datum2 = last_T; - last_T = axis.Datum1; - } - else { - if (count_B++ > 0) - pad_B += K + P; - if (label) - pad_B += T + P; - if (ticks) - pad_B += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0); - axis.Datum1 = plot.CanvasRect.Max.y - pad_B; - axis.Datum2 = last_B; - last_B = axis.Datum1; - } - } - - if (align) { - count_T = count_B = 0; - float delta_T, delta_B; - align->Update(pad_T,pad_B,delta_T,delta_B); - for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { - ImPlotAxis& axis = plot.XAxis(i); - if (!axis.Enabled) - continue; - if (axis.IsOpposite()) { - axis.Datum1 += delta_T; - axis.Datum2 += count_T++ > 1 ? delta_T : 0; - } - else { - axis.Datum1 -= delta_B; - axis.Datum2 -= count_B++ > 1 ? delta_B : 0; - } - } - } -} - -void PadAndDatumAxesY(ImPlotPlot& plot, float& pad_L, float& pad_R, ImPlotAlignmentData* align) { - - // [ pad_L ] [ pad_R ] - // .................CanvasRect................ - // :TPWPK.PTPWP _____PlotRect____ PWPTP.KPWPT: - // :A # |- A # |- -| # A -| # A: - // :X | X | | X | x: - // :I # |- I # |- -| # I -| # I: - // :S | S | | S | S: - // :3 # |- 0 # |-_______________-| # 1 -| # 2: - // :.........................................: - // - // T = text height - // P = label padding - // K = minor tick length - // W = label width - - ImPlotContext& gp = *GImPlot; - - const float T = ImGui::GetTextLineHeight(); - const float P = gp.Style.LabelPadding.x; - const float K = gp.Style.MinorTickLen.y; - - int count_L = 0; - int count_R = 0; - float last_L = plot.AxesRect.Min.x; - float last_R = plot.AxesRect.Max.x; - - for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { // FYI: can iterate forward - ImPlotAxis& axis = plot.YAxis(i); - if (!axis.Enabled) - continue; - const bool label = axis.HasLabel(); - const bool ticks = axis.HasTickLabels(); - const bool opp = axis.IsOpposite(); - if (opp) { - if (count_R++ > 0) - pad_R += K + P; - if (label) - pad_R += T + P; - if (ticks) - pad_R += axis.Ticker.MaxSize.x + P; - axis.Datum1 = plot.CanvasRect.Max.x - pad_R; - axis.Datum2 = last_R; - last_R = axis.Datum1; - } - else { - if (count_L++ > 0) - pad_L += K + P; - if (label) - pad_L += T + P; - if (ticks) - pad_L += axis.Ticker.MaxSize.x + P; - axis.Datum1 = plot.CanvasRect.Min.x + pad_L; - axis.Datum2 = last_L; - last_L = axis.Datum1; - } - } - - plot.PlotRect.Min.x = plot.CanvasRect.Min.x + pad_L; - plot.PlotRect.Max.x = plot.CanvasRect.Max.x - pad_R; - - if (align) { - count_L = count_R = 0; - float delta_L, delta_R; - align->Update(pad_L,pad_R,delta_L,delta_R); - for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { - ImPlotAxis& axis = plot.YAxis(i); - if (!axis.Enabled) - continue; - if (axis.IsOpposite()) { - axis.Datum1 -= delta_R; - axis.Datum2 -= count_R++ > 1 ? delta_R : 0; - } - else { - axis.Datum1 += delta_L; - axis.Datum2 += count_L++ > 1 ? delta_L : 0; - } - } - } -} - -//----------------------------------------------------------------------------- -// RENDERING -//----------------------------------------------------------------------------- - -static inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { - const float density = ticker.TickCount() / rect.GetWidth(); - ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min); - col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); - col_min = ImGui::ColorConvertFloat4ToU32(col_min4); - for (int t = 0; t < ticker.TickCount(); t++) { - const ImPlotTick& xt = ticker.Ticks[t]; - if (xt.PixelPos < rect.Min.x || xt.PixelPos > rect.Max.x) - continue; - if (xt.Level == 0) { - if (xt.Major) - DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_maj, size_maj); - else if (density < 0.2f) - DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_min, size_min); - } - } -} - -static inline void RenderGridLinesY(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { - const float density = ticker.TickCount() / rect.GetHeight(); - ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min); - col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); - col_min = ImGui::ColorConvertFloat4ToU32(col_min4); - for (int t = 0; t < ticker.TickCount(); t++) { - const ImPlotTick& yt = ticker.Ticks[t]; - if (yt.PixelPos < rect.Min.y || yt.PixelPos > rect.Max.y) - continue; - if (yt.Major) - DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_maj, size_maj); - else if (density < 0.2f) - DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_min, size_min); - } -} - -static inline void RenderSelectionRect(ImDrawList& DrawList, const ImVec2& p_min, const ImVec2& p_max, const ImVec4& col) { - const ImU32 col_bg = ImGui::GetColorU32(col * ImVec4(1,1,1,0.25f)); - const ImU32 col_bd = ImGui::GetColorU32(col); - DrawList.AddRectFilled(p_min, p_max, col_bg); - DrawList.AddRect(p_min, p_max, col_bd); -} - -//----------------------------------------------------------------------------- -// Input Handling -//----------------------------------------------------------------------------- - -static const float MOUSE_CURSOR_DRAG_THRESHOLD = 5.0f; -static const float BOX_SELECT_DRAG_THRESHOLD = 4.0f; - -bool UpdateInput(ImPlotPlot& plot) { - - bool changed = false; - - ImPlotContext& gp = *GImPlot; - ImGuiIO& IO = ImGui::GetIO(); - - // BUTTON STATE ----------------------------------------------------------- - - const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowItemOverlap - | ImGuiButtonFlags_PressedOnClick - | ImGuiButtonFlags_PressedOnDoubleClick - | ImGuiButtonFlags_MouseButtonLeft - | ImGuiButtonFlags_MouseButtonRight - | ImGuiButtonFlags_MouseButtonMiddle; - const ImGuiButtonFlags axis_button_flags = ImGuiButtonFlags_FlattenChildren - | plot_button_flags; - - const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags); - ImGui::SetItemAllowOverlap(); - - if (plot_clicked) { - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) { - plot.Selecting = true; - plot.SelectStart = IO.MousePos; - plot.SelectRect = ImRect(0,0,0,0); - } - if (IO.MouseDoubleClicked[gp.InputMap.Fit]) { - plot.FitThisFrame = true; - for (int i = 0; i < ImAxis_COUNT; ++i) - plot.Axes[i].FitThisFrame = true; - } - } - - const bool can_pan = IO.MouseDown[gp.InputMap.Pan] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod); - - plot.Held = plot.Held && can_pan; - - bool x_click[IMPLOT_NUM_X_AXES] = {false}; - bool x_held[IMPLOT_NUM_X_AXES] = {false}; - bool x_hov[IMPLOT_NUM_X_AXES] = {false}; - - bool y_click[IMPLOT_NUM_Y_AXES] = {false}; - bool y_held[IMPLOT_NUM_Y_AXES] = {false}; - bool y_hov[IMPLOT_NUM_Y_AXES] = {false}; - - for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { - ImPlotAxis& xax = plot.XAxis(i); - if (xax.Enabled) { - ImGui::KeepAliveID(xax.ID); - x_click[i] = ImGui::ButtonBehavior(xax.HoverRect,xax.ID,&xax.Hovered,&xax.Held,axis_button_flags); - if (x_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit]) - plot.FitThisFrame = xax.FitThisFrame = true; - xax.Held = xax.Held && can_pan; - x_hov[i] = xax.Hovered || plot.Hovered; - x_held[i] = xax.Held || plot.Held; - } - } - - for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { - ImPlotAxis& yax = plot.YAxis(i); - if (yax.Enabled) { - ImGui::KeepAliveID(yax.ID); - y_click[i] = ImGui::ButtonBehavior(yax.HoverRect,yax.ID,&yax.Hovered,&yax.Held,axis_button_flags); - if (y_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit]) - plot.FitThisFrame = yax.FitThisFrame = true; - yax.Held = yax.Held && can_pan; - y_hov[i] = yax.Hovered || plot.Hovered; - y_held[i] = yax.Held || plot.Held; - } - } - - // cancel due to DND activity - if (GImGui->DragDropActive || (IO.KeyMods == gp.InputMap.OverrideMod && gp.InputMap.OverrideMod != 0)) - return false; - - // STATE ------------------------------------------------------------------- - - const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); - - const bool any_x_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); - const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); - const bool any_y_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); - const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); - const bool any_hov = any_x_hov || any_y_hov; - const bool any_held = any_x_held || any_y_held; - - const ImVec2 select_drag = ImGui::GetMouseDragDelta(gp.InputMap.Select); - const ImVec2 pan_drag = ImGui::GetMouseDragDelta(gp.InputMap.Pan); - const float select_drag_sq = ImLengthSqr(select_drag); - const float pan_drag_sq = ImLengthSqr(pan_drag); - const bool selecting = plot.Selecting && select_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD; - const bool panning = any_held && pan_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD; - - // CONTEXT MENU ----------------------------------------------------------- - - if (IO.MouseReleased[gp.InputMap.Menu] && !plot.ContextLocked) - gp.OpenContextThisFrame = true; - - if (selecting || panning) - plot.ContextLocked = true; - else if (!(IO.MouseDown[gp.InputMap.Menu] || IO.MouseReleased[gp.InputMap.Menu])) - plot.ContextLocked = false; - - // DRAG INPUT ------------------------------------------------------------- - - if (any_held && !plot.Selecting) { - int drag_direction = 0; - for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { - ImPlotAxis& x_axis = plot.XAxis(i); - if (x_held[i] && !x_axis.IsInputLocked()) { - drag_direction |= (1 << 1); - bool increasing = x_axis.IsInverted() ? IO.MouseDelta.x > 0 : IO.MouseDelta.x < 0; - if (IO.MouseDelta.x != 0 && !x_axis.IsPanLocked(increasing)) { - const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x); - const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x); - x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); - x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); - if (axis_equal && x_axis.OrthoAxis != NULL) - x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); - changed = true; - } - } - } - for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { - ImPlotAxis& y_axis = plot.YAxis(i); - if (y_held[i] && !y_axis.IsInputLocked()) { - drag_direction |= (1 << 2); - bool increasing = y_axis.IsInverted() ? IO.MouseDelta.y < 0 : IO.MouseDelta.y > 0; - if (IO.MouseDelta.y != 0 && !y_axis.IsPanLocked(increasing)) { - const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y); - const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y); - y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); - y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); - if (axis_equal && y_axis.OrthoAxis != NULL) - y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); - changed = true; - } - } - } - if (IO.MouseDragMaxDistanceSqr[gp.InputMap.Pan] > MOUSE_CURSOR_DRAG_THRESHOLD) { - switch (drag_direction) { - case 0 : ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); break; - case (1 << 1) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); break; - case (1 << 2) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); break; - default : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); break; - } - } - } - - // SCROLL INPUT ----------------------------------------------------------- - - if (any_hov && IO.MouseWheel != 0 && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) { - - float zoom_rate = gp.InputMap.ZoomRate; - if (IO.MouseWheel > 0) - zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate)); - ImVec2 rect_size = plot.PlotRect.GetSize(); - float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f); - float ty = ImRemap(IO.MousePos.y, plot.PlotRect.Min.y, plot.PlotRect.Max.y, 0.0f, 1.0f); - - for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { - ImPlotAxis& x_axis = plot.XAxis(i); - const bool equal_zoom = axis_equal && x_axis.OrthoAxis != NULL; - const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked(); - if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) { - float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; - const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction); - const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction); - x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); - x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); - if (axis_equal && x_axis.OrthoAxis != NULL) - x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); - changed = true; - } - } - for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { - ImPlotAxis& y_axis = plot.YAxis(i); - const bool equal_zoom = axis_equal && y_axis.OrthoAxis != NULL; - const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked(); - if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) { - float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; - const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction); - const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction); - y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); - y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); - if (axis_equal && y_axis.OrthoAxis != NULL) - y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); - changed = true; - } - } - } - - // BOX-SELECTION ---------------------------------------------------------- - - if (plot.Selecting) { - const ImVec2 d = plot.SelectStart - IO.MousePos; - const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImFabs(d.x) > 2; - const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod) && ImFabs(d.y) > 2; - // confirm - if (IO.MouseReleased[gp.InputMap.Select]) { - for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { - ImPlotAxis& x_axis = plot.XAxis(i); - if (!x_axis.IsInputLocked() && x_can_change) { - const double p1 = x_axis.PixelsToPlot(plot.SelectStart.x); - const double p2 = x_axis.PixelsToPlot(IO.MousePos.x); - x_axis.SetMin(ImMin(p1, p2)); - x_axis.SetMax(ImMax(p1, p2)); - changed = true; - } - } - for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { - ImPlotAxis& y_axis = plot.YAxis(i); - if (!y_axis.IsInputLocked() && y_can_change) { - const double p1 = y_axis.PixelsToPlot(plot.SelectStart.y); - const double p2 = y_axis.PixelsToPlot(IO.MousePos.y); - y_axis.SetMin(ImMin(p1, p2)); - y_axis.SetMax(ImMax(p1, p2)); - changed = true; - } - } - if (x_can_change || y_can_change || (ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod))) - gp.OpenContextThisFrame = false; - plot.Selected = plot.Selecting = false; - } - // cancel - else if (IO.MouseReleased[gp.InputMap.SelectCancel]) { - plot.Selected = plot.Selecting = false; - gp.OpenContextThisFrame = false; - } - else if (ImLengthSqr(d) > BOX_SELECT_DRAG_THRESHOLD) { - // bad selection - if (plot.IsInputLocked()) { - ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); - gp.OpenContextThisFrame = false; - plot.Selected = false; - } - else { - // TODO: Handle only min or max locked cases - const bool full_width = ImHasFlag(IO.KeyMods, gp.InputMap.SelectHorzMod) || AllAxesInputLocked(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); - const bool full_height = ImHasFlag(IO.KeyMods, gp.InputMap.SelectVertMod) || AllAxesInputLocked(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); - plot.SelectRect.Min.x = full_width ? plot.PlotRect.Min.x : ImMin(plot.SelectStart.x, IO.MousePos.x); - plot.SelectRect.Max.x = full_width ? plot.PlotRect.Max.x : ImMax(plot.SelectStart.x, IO.MousePos.x); - plot.SelectRect.Min.y = full_height ? plot.PlotRect.Min.y : ImMin(plot.SelectStart.y, IO.MousePos.y); - plot.SelectRect.Max.y = full_height ? plot.PlotRect.Max.y : ImMax(plot.SelectStart.y, IO.MousePos.y); - plot.SelectRect.Min -= plot.PlotRect.Min; - plot.SelectRect.Max -= plot.PlotRect.Min; - plot.Selected = true; - } - } - else { - plot.Selected = false; - } - } - return changed; -} - -//----------------------------------------------------------------------------- -// Next Plot Data (Legacy) -//----------------------------------------------------------------------------- - -void ApplyNextPlotData(ImAxis idx) { - ImPlotContext& gp = *GImPlot; - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - if (!axis.Enabled) - return; - double* npd_lmin = gp.NextPlotData.LinkedMin[idx]; - double* npd_lmax = gp.NextPlotData.LinkedMax[idx]; - bool npd_rngh = gp.NextPlotData.HasRange[idx]; - ImPlotCond npd_rngc = gp.NextPlotData.RangeCond[idx]; - ImPlotRange npd_rngv = gp.NextPlotData.Range[idx]; - axis.LinkedMin = npd_lmin; - axis.LinkedMax = npd_lmax; - axis.PullLinks(); - if (npd_rngh) { - if (!plot.Initialized || npd_rngc == ImPlotCond_Always) - axis.SetRange(npd_rngv); - } - axis.HasRange = npd_rngh; - axis.RangeCond = npd_rngc; -} - -//----------------------------------------------------------------------------- -// Setup -//----------------------------------------------------------------------------- - -void SetupAxis(ImAxis idx, const char* label, ImPlotAxisFlags flags) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - // get plot and axis - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - // set ID - axis.ID = plot.ID + idx + 1; - // check and set flags - if (plot.JustCreated || flags != axis.PreviousFlags) - axis.Flags = flags; - axis.PreviousFlags = flags; - // enable axis - axis.Enabled = true; - // set label - plot.SetAxisLabel(axis,label); - // cache colors - UpdateAxisColors(axis); -} - -void SetupAxisLimits(ImAxis idx, double min_lim, double max_lim, ImPlotCond cond) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); // get plot and axis - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - if (!plot.Initialized || cond == ImPlotCond_Always) - axis.SetRange(min_lim, max_lim); - axis.HasRange = true; - axis.RangeCond = cond; -} - -void SetupAxisFormat(ImAxis idx, const char* fmt) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - axis.HasFormatSpec = fmt != NULL; - if (fmt != NULL) - ImStrncpy(axis.FormatSpec,fmt,sizeof(axis.FormatSpec)); -} - -void SetupAxisLinks(ImAxis idx, double* min_lnk, double* max_lnk) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - axis.LinkedMin = min_lnk; - axis.LinkedMax = max_lnk; - axis.PullLinks(); -} - -void SetupAxisFormat(ImAxis idx, ImPlotFormatter formatter, void* data) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - axis.Formatter = formatter; - axis.FormatterData = data; -} - -void SetupAxisTicks(ImAxis idx, const double* values, int n_ticks, const char* const labels[], bool show_default) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - axis.ShowDefaultTicks = show_default; - AddTicksCustom(values, - labels, - n_ticks, - axis.Ticker, - axis.Formatter ? axis.Formatter : Formatter_Default, - (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); -} - -void SetupAxisTicks(ImAxis idx, double v_min, double v_max, int n_ticks, const char* const labels[], bool show_default) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - IM_ASSERT_USER_ERROR(n_ticks > 1, "The number of ticks must be greater than 1"); - FillRange(GImPlot->TempDouble1, n_ticks, v_min, v_max); - SetupAxisTicks(idx, GImPlot->TempDouble1.Data, n_ticks, labels, show_default); -} - -void SetupAxisScale(ImAxis idx, ImPlotScale scale) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - axis.Scale = scale; - switch (scale) - { - case ImPlotScale_Time: - axis.TransformForward = NULL; - axis.TransformInverse = NULL; - axis.TransformData = NULL; - axis.Locator = Locator_Time; - axis.ConstraintRange = ImPlotRange(IMPLOT_MIN_TIME, IMPLOT_MAX_TIME); - axis.Ticker.Levels = 2; - break; - case ImPlotScale_Log10: - axis.TransformForward = TransformForward_Log10; - axis.TransformInverse = TransformInverse_Log10; - axis.TransformData = NULL; - axis.Locator = Locator_Log10; - axis.ConstraintRange = ImPlotRange(DBL_MIN, INFINITY); - break; - case ImPlotScale_SymLog: - axis.TransformForward = TransformForward_SymLog; - axis.TransformInverse = TransformInverse_SymLog; - axis.TransformData = NULL; - axis.Locator = Locator_SymLog; - axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY); - break; - default: - axis.TransformForward = NULL; - axis.TransformInverse = NULL; - axis.TransformData = NULL; - axis.Locator = NULL; - axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY); - break; - } -} - -void SetupAxisScale(ImAxis idx, ImPlotTransform fwd, ImPlotTransform inv, void* data) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - axis.Scale = IMPLOT_AUTO; - axis.TransformForward = fwd; - axis.TransformInverse = inv; - axis.TransformData = data; -} - -void SetupAxisLimitsConstraints(ImAxis idx, double v_min, double v_max) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - axis.ConstraintRange.Min = v_min; - axis.ConstraintRange.Max = v_max; -} - -void SetupAxisZoomConstraints(ImAxis idx, double z_min, double z_max) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& axis = plot.Axes[idx]; - IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - axis.ConstraintZoom.Min = z_min; - axis.ConstraintZoom.Max = z_max; -} - -void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags) { - SetupAxis(ImAxis_X1, x_label, x_flags); - SetupAxis(ImAxis_Y1, y_label, y_flags); -} - -void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) { - SetupAxisLimits(ImAxis_X1, x_min, x_max, cond); - SetupAxisLimits(ImAxis_Y1, y_min, y_max, cond); -} - -void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - IM_ASSERT_USER_ERROR(GImPlot->CurrentItems != NULL, - "SetupLegend() needs to be called within an itemized context!"); - ImPlotLegend& legend = GImPlot->CurrentItems->Legend; - // check and set location - if (location != legend.PreviousLocation) - legend.Location = location; - legend.PreviousLocation = location; - // check and set flags - if (flags != legend.PreviousFlags) - legend.Flags = flags; - legend.PreviousFlags = flags; -} - -void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, - "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - GImPlot->CurrentPlot->MouseTextLocation = location; - GImPlot->CurrentPlot->MouseTextFlags = flags; -} - -//----------------------------------------------------------------------------- -// SetNext -//----------------------------------------------------------------------------- - -void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextAxisLimits() needs to be called before BeginPlot()!"); - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - gp.NextPlotData.HasRange[axis] = true; - gp.NextPlotData.RangeCond[axis] = cond; - gp.NextPlotData.Range[axis].Min = v_min; - gp.NextPlotData.Range[axis].Max = v_max; -} - -void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextAxisLinks() needs to be called before BeginPlot()!"); - gp.NextPlotData.LinkedMin[axis] = link_min; - gp.NextPlotData.LinkedMax[axis] = link_max; -} - -void SetNextAxisToFit(ImAxis axis) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextAxisToFit() needs to be called before BeginPlot()!"); - gp.NextPlotData.Fit[axis] = true; -} - -void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) { - SetNextAxisLimits(ImAxis_X1, x_min, x_max, cond); - SetNextAxisLimits(ImAxis_Y1, y_min, y_max, cond); -} - -void SetNextAxesToFit() { - for (int i = 0; i < ImAxis_COUNT; ++i) - SetNextAxisToFit(i); -} - -//----------------------------------------------------------------------------- -// BeginPlot -//----------------------------------------------------------------------------- - -bool BeginPlot(const char* title_id, const ImVec2& size, ImPlotFlags flags) { - IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot == NULL, "Mismatched BeginPlot()/EndPlot()!"); - - // FRONT MATTER ----------------------------------------------------------- - - if (GImPlot->CurrentSubplot != NULL) - ImGui::PushID(GImPlot->CurrentSubplot->CurrentIdx); - - // get globals - ImPlotContext& gp = *GImPlot; - ImGuiContext &G = *GImGui; - ImGuiWindow* Window = G.CurrentWindow; - - // skip if needed - if (Window->SkipItems && !gp.CurrentSubplot) { - ResetCtxForNextPlot(GImPlot); - return false; - } - - // ID and age (TODO: keep track of plot age in frames) - const ImGuiID ID = Window->GetID(title_id); - const bool just_created = gp.Plots.GetByKey(ID) == NULL; - gp.CurrentPlot = gp.Plots.GetOrAddByKey(ID); - - ImPlotPlot &plot = *gp.CurrentPlot; - plot.ID = ID; - plot.Items.ID = ID - 1; - plot.JustCreated = just_created; - plot.SetupLocked = false; - - // check flags - if (plot.JustCreated) - plot.Flags = flags; - else if (flags != plot.PreviousFlags) - plot.Flags = flags; - plot.PreviousFlags = flags; - - // setup default axes - if (plot.JustCreated) { - SetupAxis(ImAxis_X1); - SetupAxis(ImAxis_Y1); - } - - // reset axes - for (int i = 0; i < ImAxis_COUNT; ++i) { - plot.Axes[i].Reset(); - UpdateAxisColors(plot.Axes[i]); - } - // ensure first axes enabled - plot.Axes[ImAxis_X1].Enabled = true; - plot.Axes[ImAxis_Y1].Enabled = true; - // set initial axes - plot.CurrentX = ImAxis_X1; - plot.CurrentY = ImAxis_Y1; - - // process next plot data (legacy) - for (int i = 0; i < ImAxis_COUNT; ++i) - ApplyNextPlotData(i); - - // capture scroll with a child region - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoChild)) { - ImVec2 child_size; - if (gp.CurrentSubplot != NULL) - child_size = gp.CurrentSubplot->CellSize; - else - child_size = ImVec2(size.x == 0 ? gp.Style.PlotDefaultSize.x : size.x, size.y == 0 ? gp.Style.PlotDefaultSize.y : size.y); - ImGui::BeginChild(title_id, child_size, false, ImGuiWindowFlags_NoScrollbar); - Window = ImGui::GetCurrentWindow(); - Window->ScrollMax.y = 1.0f; - gp.ChildWindowMade = true; - } - else { - gp.ChildWindowMade = false; - } - - // clear text buffers - plot.ClearTextBuffer(); - plot.SetTitle(title_id); - - // set frame size - ImVec2 frame_size; - if (gp.CurrentSubplot != NULL) - frame_size = gp.CurrentSubplot->CellSize; - else - frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); - - if (frame_size.x < gp.Style.PlotMinSize.x && (size.x < 0.0f || gp.CurrentSubplot != NULL)) - frame_size.x = gp.Style.PlotMinSize.x; - if (frame_size.y < gp.Style.PlotMinSize.y && (size.y < 0.0f || gp.CurrentSubplot != NULL)) - frame_size.y = gp.Style.PlotMinSize.y; - - plot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); - ImGui::ItemSize(plot.FrameRect); - if (!ImGui::ItemAdd(plot.FrameRect, plot.ID, &plot.FrameRect) && !gp.CurrentSubplot) { - ResetCtxForNextPlot(GImPlot); - return false; - } - - // setup items (or dont) - if (gp.CurrentItems == NULL) - gp.CurrentItems = &plot.Items; - - return true; -} - -//----------------------------------------------------------------------------- -// SetupFinish -//----------------------------------------------------------------------------- - -void SetupFinish() { - IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "SetupFinish needs to be called after BeginPlot!"); - - ImPlotContext& gp = *GImPlot; - ImGuiContext& G = *GImGui; - ImDrawList& DrawList = *G.CurrentWindow->DrawList; - const ImGuiStyle& Style = G.Style; - - ImPlotPlot &plot = *gp.CurrentPlot; - - // lock setup - plot.SetupLocked = true; - - // finalize axes and set default formatter/locator - for (int i = 0; i < ImAxis_COUNT; ++i) { - ImPlotAxis& axis = plot.Axes[i]; - if (axis.Enabled) { - axis.Constrain(); - if (!plot.Initialized && axis.CanInitFit()) - plot.FitThisFrame = axis.FitThisFrame = true; - } - if (axis.Formatter == NULL) { - axis.Formatter = Formatter_Default; - if (axis.HasFormatSpec) - axis.FormatterData = axis.FormatSpec; - else - axis.FormatterData = (void*)IMPLOT_LABEL_FORMAT; - } - if (axis.Locator == NULL) { - axis.Locator = Locator_Default; - } - } - - // setup NULL orthogonal axes - const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); - for (int ix = ImAxis_X1, iy = ImAxis_Y1; ix < ImAxis_Y1 || iy < ImAxis_COUNT; ++ix, ++iy) { - ImPlotAxis& x_axis = plot.Axes[ix]; - ImPlotAxis& y_axis = plot.Axes[iy]; - if (x_axis.Enabled && y_axis.Enabled) { - if (x_axis.OrthoAxis == NULL) - x_axis.OrthoAxis = &y_axis; - if (y_axis.OrthoAxis == NULL) - y_axis.OrthoAxis = &x_axis; - } - else if (x_axis.Enabled) - { - if (x_axis.OrthoAxis == NULL && !axis_equal) - x_axis.OrthoAxis = &plot.Axes[ImAxis_Y1]; - } - else if (y_axis.Enabled) { - if (y_axis.OrthoAxis == NULL && !axis_equal) - y_axis.OrthoAxis = &plot.Axes[ImAxis_X1]; - } - } - - // canvas/axes bb - plot.CanvasRect = ImRect(plot.FrameRect.Min + gp.Style.PlotPadding, plot.FrameRect.Max - gp.Style.PlotPadding); - plot.AxesRect = plot.FrameRect; - - // outside legend adjustments - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0 && ImHasFlag(plot.Items.Legend.Flags, ImPlotLegendFlags_Outside)) { - ImPlotLegend& legend = plot.Items.Legend; - const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); - const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz); - const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East); - const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West); - const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South); - const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North); - if ((west && !horz) || (west && horz && !north && !south)) { - plot.CanvasRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); - plot.AxesRect.Min.x += (legend_size.x + gp.Style.PlotPadding.x); - } - if ((east && !horz) || (east && horz && !north && !south)) { - plot.CanvasRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); - plot.AxesRect.Max.x -= (legend_size.x + gp.Style.PlotPadding.x); - } - if ((north && horz) || (north && !horz && !west && !east)) { - plot.CanvasRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); - plot.AxesRect.Min.y += (legend_size.y + gp.Style.PlotPadding.y); - } - if ((south && horz) || (south && !horz && !west && !east)) { - plot.CanvasRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); - plot.AxesRect.Max.y -= (legend_size.y + gp.Style.PlotPadding.y); - } - } - - // plot bb - float pad_top = 0, pad_bot = 0, pad_left = 0, pad_right = 0; - - // (0) calc top padding form title - ImVec2 title_size(0.0f, 0.0f); - if (plot.HasTitle()) - title_size = ImGui::CalcTextSize(plot.GetTitle(), NULL, true); - if (title_size.x > 0) { - pad_top += title_size.y + gp.Style.LabelPadding.y; - plot.AxesRect.Min.y += gp.Style.PlotPadding.y + pad_top; - } - - // (1) calc addition top padding and bot padding - PadAndDatumAxesX(plot,pad_top,pad_bot,gp.CurrentAlignmentH); - - const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot; - - // (2) get y tick labels (needed for left/right pad) - for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { - ImPlotAxis& axis = plot.YAxis(i); - if (axis.WillRender() && axis.ShowDefaultTicks) { - axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData); - } - } - - // (3) calc left/right pad - PadAndDatumAxesY(plot,pad_left,pad_right,gp.CurrentAlignmentV); - - const float plot_width = plot.CanvasRect.GetWidth() - pad_left - pad_right; - - // (4) get x ticks - for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { - ImPlotAxis& axis = plot.XAxis(i); - if (axis.WillRender() && axis.ShowDefaultTicks) { - axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData); - } - } - - // (5) calc plot bb - plot.PlotRect = ImRect(plot.CanvasRect.Min + ImVec2(pad_left, pad_top), plot.CanvasRect.Max - ImVec2(pad_right, pad_bot)); - - // HOVER------------------------------------------------------------ - - // axes hover rect, pixel ranges - for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { - ImPlotAxis& xax = plot.XAxis(i); - xax.HoverRect = ImRect(ImVec2(plot.PlotRect.Min.x, ImMin(xax.Datum1,xax.Datum2)), - ImVec2(plot.PlotRect.Max.x, ImMax(xax.Datum1,xax.Datum2))); - xax.PixelMin = xax.IsInverted() ? plot.PlotRect.Max.x : plot.PlotRect.Min.x; - xax.PixelMax = xax.IsInverted() ? plot.PlotRect.Min.x : plot.PlotRect.Max.x; - xax.UpdateTransformCache(); - } - - for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { - ImPlotAxis& yax = plot.YAxis(i); - yax.HoverRect = ImRect(ImVec2(ImMin(yax.Datum1,yax.Datum2),plot.PlotRect.Min.y), - ImVec2(ImMax(yax.Datum1,yax.Datum2),plot.PlotRect.Max.y)); - yax.PixelMin = yax.IsInverted() ? plot.PlotRect.Min.y : plot.PlotRect.Max.y; - yax.PixelMax = yax.IsInverted() ? plot.PlotRect.Max.y : plot.PlotRect.Min.y; - yax.UpdateTransformCache(); - } - // Equal axis constraint. Must happen after we set Pixels - // constrain equal axes for primary x and y if not approximately equal - // constrains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case - if (axis_equal) { - for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { - ImPlotAxis& x_axis = plot.XAxis(i); - if (x_axis.OrthoAxis == NULL) - continue; - double xar = x_axis.GetAspect(); - double yar = x_axis.OrthoAxis->GetAspect(); - // edge case: user has set x range this frame, so fit y to x so that we honor their request for x range - // NB: because of feedback across several frames, the user's x request may not be perfectly honored - if (x_axis.HasRange) - x_axis.OrthoAxis->SetAspect(xar); - else if (!ImAlmostEqual(xar,yar) && !x_axis.OrthoAxis->IsInputLocked()) - x_axis.SetAspect(yar); - } - } - - // INPUT ------------------------------------------------------------------ - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoInputs)) - UpdateInput(plot); - - // fit from FitNextPlotAxes or auto fit - for (int i = 0; i < ImAxis_COUNT; ++i) { - if (gp.NextPlotData.Fit[i] || plot.Axes[i].IsAutoFitting()) { - plot.FitThisFrame = true; - plot.Axes[i].FitThisFrame = true; - } - } - - // RENDER ----------------------------------------------------------------- - - const float txt_height = ImGui::GetTextLineHeight(); - - // render frame - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoFrame)) - ImGui::RenderFrame(plot.FrameRect.Min, plot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding); - - // grid bg - DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBg)); - - // transform ticks - for (int i = 0; i < ImAxis_COUNT; i++) { - ImPlotAxis& axis = plot.Axes[i]; - if (axis.WillRender()) { - for (int t = 0; t < axis.Ticker.TickCount(); t++) { - ImPlotTick& tk = axis.Ticker.Ticks[t]; - tk.PixelPos = IM_ROUND(axis.PlotToPixels(tk.PlotPos)); - } - } - } - - // render grid (background) - for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { - ImPlotAxis& x_axis = plot.XAxis(i); - if (x_axis.Enabled && x_axis.HasGridLines() && !x_axis.IsForeground()) - RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); - } - for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { - ImPlotAxis& y_axis = plot.YAxis(i); - if (y_axis.Enabled && y_axis.HasGridLines() && !y_axis.IsForeground()) - RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); - } - - // render x axis button, label, tick labels - for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { - ImPlotAxis& ax = plot.XAxis(i); - if (!ax.Enabled) - continue; - if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight)) - DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov); - else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) { - DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi); - ax.ColorHiLi = IM_COL32_BLACK_TRANS; - } - else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { - DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); - } - const ImPlotTicker& tkr = ax.Ticker; - const bool opp = ax.IsOpposite(); - if (ax.HasLabel()) { - const char* label = plot.GetAxisLabel(ax); - const ImVec2 label_size = ImGui::CalcTextSize(label); - const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.y + gp.Style.LabelPadding.y : 0.0f) - + (tkr.Levels - 1) * (txt_height + gp.Style.LabelPadding.y) - + gp.Style.LabelPadding.y; - const ImVec2 label_pos(plot.PlotRect.GetCenter().x - label_size.x * 0.5f, - opp ? ax.Datum1 - label_offset - label_size.y : ax.Datum1 + label_offset); - DrawList.AddText(label_pos, ax.ColorTxt, label); - } - if (ax.HasTickLabels()) { - for (int j = 0; j < tkr.TickCount(); ++j) { - const ImPlotTick& tk = tkr.Ticks[j]; - const float datum = ax.Datum1 + (opp ? (-gp.Style.LabelPadding.y -txt_height -tk.Level * (txt_height + gp.Style.LabelPadding.y)) - : gp.Style.LabelPadding.y + tk.Level * (txt_height + gp.Style.LabelPadding.y)); - if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.x - 1 && tk.PixelPos <= plot.PlotRect.Max.x + 1) { - ImVec2 start(tk.PixelPos - 0.5f * tk.LabelSize.x, datum); - DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j)); - } - } - } - } - - // render y axis button, label, tick labels - for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { - ImPlotAxis& ax = plot.YAxis(i); - if (!ax.Enabled) - continue; - if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight)) - DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov); - else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) { - DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi); - ax.ColorHiLi = IM_COL32_BLACK_TRANS; - } - else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { - DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); - } - const ImPlotTicker& tkr = ax.Ticker; - const bool opp = ax.IsOpposite(); - if (ax.HasLabel()) { - const char* label = plot.GetAxisLabel(ax); - const ImVec2 label_size = CalcTextSizeVertical(label); - const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.x + gp.Style.LabelPadding.x : 0.0f) - + gp.Style.LabelPadding.x; - const ImVec2 label_pos(opp ? ax.Datum1 + label_offset : ax.Datum1 - label_offset - label_size.x, - plot.PlotRect.GetCenter().y + label_size.y * 0.5f); - AddTextVertical(&DrawList, label_pos, ax.ColorTxt, label); - } - if (ax.HasTickLabels()) { - for (int j = 0; j < tkr.TickCount(); ++j) { - const ImPlotTick& tk = tkr.Ticks[j]; - const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x)); - if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1) { - ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y); - DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j)); - } - } - } - } - - - // clear legend (TODO: put elsewhere) - plot.Items.Legend.Reset(); - // push ID to set item hashes (NB: !!!THIS PROBABLY NEEDS TO BE IN BEGIN PLOT!!!!) - ImGui::PushOverrideID(gp.CurrentItems->ID); -} - -//----------------------------------------------------------------------------- -// EndPlot() -//----------------------------------------------------------------------------- - -void EndPlot() { - IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "Mismatched BeginPlot()/EndPlot()!"); - - SetupLock(); - - ImPlotContext& gp = *GImPlot; - ImGuiContext &G = *GImGui; - ImPlotPlot &plot = *gp.CurrentPlot; - ImGuiWindow * Window = G.CurrentWindow; - ImDrawList & DrawList = *Window->DrawList; - const ImGuiIO & IO = ImGui::GetIO(); - - // FINAL RENDER ----------------------------------------------------------- - - const bool render_border = gp.Style.PlotBorderSize > 0 && gp.Style.Colors[ImPlotCol_PlotBorder].w > 0; - const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); - const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); - - ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); - - // render grid (foreground) - for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { - ImPlotAxis& x_axis = plot.XAxis(i); - if (x_axis.Enabled && x_axis.HasGridLines() && x_axis.IsForeground()) - RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); - } - for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { - ImPlotAxis& y_axis = plot.YAxis(i); - if (y_axis.Enabled && y_axis.HasGridLines() && y_axis.IsForeground()) - RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); - } - - - // render title - if (plot.HasTitle()) { - ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); - AddTextCentered(&DrawList,ImVec2(plot.PlotRect.GetCenter().x, plot.CanvasRect.Min.y),col,plot.GetTitle()); - } - - // render x ticks - int count_B = 0, count_T = 0; - for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { - const ImPlotAxis& ax = plot.XAxis(i); - if (!ax.Enabled) - continue; - const ImPlotTicker& tkr = ax.Ticker; - const bool opp = ax.IsOpposite(); - const bool aux = ((opp && count_T > 0)||(!opp && count_B > 0)); - if (ax.HasTickMarks()) { - const float direction = opp ? 1.0f : -1.0f; - for (int j = 0; j < tkr.TickCount(); ++j) { - const ImPlotTick& tk = tkr.Ticks[j]; - if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.x || tk.PixelPos > plot.PlotRect.Max.x) - continue; - const ImVec2 start(tk.PixelPos, ax.Datum1); - const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x; - const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x; - DrawList.AddLine(start, start + ImVec2(0,direction*len), ax.ColorTick, thk); - } - if (aux || !render_border) - DrawList.AddLine(ImVec2(plot.PlotRect.Min.x,ax.Datum1), ImVec2(plot.PlotRect.Max.x,ax.Datum1), ax.ColorTick, gp.Style.MinorTickSize.x); - } - count_B += !opp; - count_T += opp; - } - - // render y ticks - int count_L = 0, count_R = 0; - for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { - const ImPlotAxis& ax = plot.YAxis(i); - if (!ax.Enabled) - continue; - const ImPlotTicker& tkr = ax.Ticker; - const bool opp = ax.IsOpposite(); - const bool aux = ((opp && count_R > 0)||(!opp && count_L > 0)); - if (ax.HasTickMarks()) { - const float direction = opp ? -1.0f : 1.0f; - for (int j = 0; j < tkr.TickCount(); ++j) { - const ImPlotTick& tk = tkr.Ticks[j]; - if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.y || tk.PixelPos > plot.PlotRect.Max.y) - continue; - const ImVec2 start(ax.Datum1, tk.PixelPos); - const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; - const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; - DrawList.AddLine(start, start + ImVec2(direction*len,0), ax.ColorTick, thk); - } - if (aux || !render_border) - DrawList.AddLine(ImVec2(ax.Datum1, plot.PlotRect.Min.y), ImVec2(ax.Datum1, plot.PlotRect.Max.y), ax.ColorTick, gp.Style.MinorTickSize.y); - } - count_L += !opp; - count_R += opp; - } - ImGui::PopClipRect(); - - // render annotations - PushPlotClipRect(); - for (int i = 0; i < gp.Annotations.Size; ++i) { - const char* txt = gp.Annotations.GetText(i); - ImPlotAnnotation& an = gp.Annotations.Annotations[i]; - const ImVec2 txt_size = ImGui::CalcTextSize(txt); - const ImVec2 size = txt_size + gp.Style.AnnotationPadding * 2; - ImVec2 pos = an.Pos; - if (an.Offset.x == 0) - pos.x -= size.x / 2; - else if (an.Offset.x > 0) - pos.x += an.Offset.x; - else - pos.x -= size.x - an.Offset.x; - if (an.Offset.y == 0) - pos.y -= size.y / 2; - else if (an.Offset.y > 0) - pos.y += an.Offset.y; - else - pos.y -= size.y - an.Offset.y; - if (an.Clamp) - pos = ClampLabelPos(pos, size, plot.PlotRect.Min, plot.PlotRect.Max); - ImRect rect(pos,pos+size); - if (an.Offset.x != 0 || an.Offset.y != 0) { - ImVec2 corners[4] = {rect.GetTL(), rect.GetTR(), rect.GetBR(), rect.GetBL()}; - int min_corner = 0; - float min_len = FLT_MAX; - for (int c = 0; c < 4; ++c) { - float len = ImLengthSqr(an.Pos - corners[c]); - if (len < min_len) { - min_corner = c; - min_len = len; - } - } - DrawList.AddLine(an.Pos, corners[min_corner], an.ColorBg); - } - DrawList.AddRectFilled(rect.Min, rect.Max, an.ColorBg); - DrawList.AddText(pos + gp.Style.AnnotationPadding, an.ColorFg, txt); - } - - // render selection - if (plot.Selected) - RenderSelectionRect(DrawList, plot.SelectRect.Min + plot.PlotRect.Min, plot.SelectRect.Max + plot.PlotRect.Min, GetStyleColorVec4(ImPlotCol_Selection)); - - // render crosshairs - if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && plot.Hovered && !(any_x_held || any_y_held) && !plot.Selecting && !plot.Items.Legend.Hovered) { - ImGui::SetMouseCursor(ImGuiMouseCursor_None); - ImVec2 xy = IO.MousePos; - ImVec2 h1(plot.PlotRect.Min.x, xy.y); - ImVec2 h2(xy.x - 5, xy.y); - ImVec2 h3(xy.x + 5, xy.y); - ImVec2 h4(plot.PlotRect.Max.x, xy.y); - ImVec2 v1(xy.x, plot.PlotRect.Min.y); - ImVec2 v2(xy.x, xy.y - 5); - ImVec2 v3(xy.x, xy.y + 5); - ImVec2 v4(xy.x, plot.PlotRect.Max.y); - ImU32 col = GetStyleColorU32(ImPlotCol_Crosshairs); - DrawList.AddLine(h1, h2, col); - DrawList.AddLine(h3, h4, col); - DrawList.AddLine(v1, v2, col); - DrawList.AddLine(v3, v4, col); - } - - // render mouse pos - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText) && (plot.Hovered || ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_ShowAlways))) { - - const bool no_aux = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoAuxAxes); - const bool no_fmt = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoFormat); - - ImGuiTextBuffer& builder = gp.MousePosStringBuilder; - builder.Buf.shrink(0); - char buff[IMPLOT_LABEL_MAX_SIZE]; - - const int num_x = no_aux ? 1 : IMPLOT_NUM_X_AXES; - for (int i = 0; i < num_x; ++i) { - ImPlotAxis& x_axis = plot.XAxis(i); - if (!x_axis.Enabled) - continue; - if (i > 0) - builder.append(", ("); - double v = x_axis.PixelsToPlot(IO.MousePos.x); - if (no_fmt) - Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT); - else - LabelAxisValue(x_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); - builder.append(buff); - if (i > 0) - builder.append(")"); - } - builder.append(", "); - const int num_y = no_aux ? 1 : IMPLOT_NUM_Y_AXES; - for (int i = 0; i < num_y; ++i) { - ImPlotAxis& y_axis = plot.YAxis(i); - if (!y_axis.Enabled) - continue; - if (i > 0) - builder.append(", ("); - double v = y_axis.PixelsToPlot(IO.MousePos.y); - if (no_fmt) - Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT); - else - LabelAxisValue(y_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); - builder.append(buff); - if (i > 0) - builder.append(")"); - } - - if (!builder.empty()) { - const ImVec2 size = ImGui::CalcTextSize(builder.c_str()); - const ImVec2 pos = GetLocationPos(plot.PlotRect, size, plot.MouseTextLocation, gp.Style.MousePosPadding); - DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), builder.c_str()); - } - } - PopPlotClipRect(); - - // axis side switch - if (!plot.Held) { - ImVec2 mouse_pos = ImGui::GetIO().MousePos; - ImRect trigger_rect = plot.PlotRect; - trigger_rect.Expand(-10); - for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { - ImPlotAxis& x_axis = plot.XAxis(i); - if (ImHasFlag(x_axis.Flags, ImPlotAxisFlags_NoSideSwitch)) - continue; - if (x_axis.Held && plot.PlotRect.Contains(mouse_pos)) { - const bool opp = ImHasFlag(x_axis.Flags, ImPlotAxisFlags_Opposite); - if (!opp) { - ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5, - plot.PlotRect.Max.x + 5, plot.PlotRect.Min.y + 5); - if (mouse_pos.y < plot.PlotRect.Max.y - 10) - DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov); - if (rect.Contains(mouse_pos)) - x_axis.Flags |= ImPlotAxisFlags_Opposite; - } - else { - ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Max.y - 5, - plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5); - if (mouse_pos.y > plot.PlotRect.Min.y + 10) - DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov); - if (rect.Contains(mouse_pos)) - x_axis.Flags &= ~ImPlotAxisFlags_Opposite; - } - } - } - for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { - ImPlotAxis& y_axis = plot.YAxis(i); - if (ImHasFlag(y_axis.Flags, ImPlotAxisFlags_NoSideSwitch)) - continue; - if (y_axis.Held && plot.PlotRect.Contains(mouse_pos)) { - const bool opp = ImHasFlag(y_axis.Flags, ImPlotAxisFlags_Opposite); - if (!opp) { - ImRect rect(plot.PlotRect.Max.x - 5, plot.PlotRect.Min.y - 5, - plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5); - if (mouse_pos.x > plot.PlotRect.Min.x + 10) - DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov); - if (rect.Contains(mouse_pos)) - y_axis.Flags |= ImPlotAxisFlags_Opposite; - } - else { - ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5, - plot.PlotRect.Min.x + 5, plot.PlotRect.Max.y + 5); - if (mouse_pos.x < plot.PlotRect.Max.x - 10) - DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov); - if (rect.Contains(mouse_pos)) - y_axis.Flags &= ~ImPlotAxisFlags_Opposite; - } - } - } - } - - // reset legend hovers - plot.Items.Legend.Hovered = false; - for (int i = 0; i < plot.Items.GetItemCount(); ++i) - plot.Items.GetItemByIndex(i)->LegendHovered = false; - // render legend - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0) { - ImPlotLegend& legend = plot.Items.Legend; - const bool legend_out = ImHasFlag(legend.Flags, ImPlotLegendFlags_Outside); - const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); - const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); - const ImVec2 legend_pos = GetLocationPos(legend_out ? plot.FrameRect : plot.PlotRect, - legend_size, - legend.Location, - legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding); - legend.Rect = ImRect(legend_pos, legend_pos + legend_size); - // test hover - legend.Hovered = ImGui::IsWindowHovered() && legend.Rect.Contains(IO.MousePos); - - if (legend_out) - ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); - else - PushPlotClipRect(); - ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); - ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); - DrawList.AddRectFilled(legend.Rect.Min, legend.Rect.Max, col_bg); - DrawList.AddRect(legend.Rect.Min, legend.Rect.Max, col_bd); - bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) - && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus); - - // main ctx menu - if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus)) - ImGui::OpenPopup("##LegendContext"); - ImGui::PopClipRect(); - if (ImGui::BeginPopup("##LegendContext")) { - ImGui::Text("Legend"); ImGui::Separator(); - if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) - ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); - ImGui::EndPopup(); - } - } - else { - plot.Items.Legend.Rect = ImRect(); - } - - // render border - if (render_border) - DrawList.AddRect(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawFlags_RoundCornersAll, gp.Style.PlotBorderSize); - - // render tags - for (int i = 0; i < gp.Tags.Size; ++i) { - ImPlotTag& tag = gp.Tags.Tags[i]; - ImPlotAxis& axis = plot.Axes[tag.Axis]; - if (!axis.Enabled || !axis.Range.Contains(tag.Value)) - continue; - const char* txt = gp.Tags.GetText(i); - ImVec2 text_size = ImGui::CalcTextSize(txt); - ImVec2 size = text_size + gp.Style.AnnotationPadding * 2; - ImVec2 pos; - axis.Ticker.OverrideSizeLate(size); - float pix = IM_ROUND(axis.PlotToPixels(tag.Value)); - if (axis.Vertical) { - if (axis.IsOpposite()) { - pos = ImVec2(axis.Datum1 + gp.Style.LabelPadding.x, pix - size.y * 0.5f); - DrawList.AddTriangleFilled(ImVec2(axis.Datum1,pix), pos, pos + ImVec2(0,size.y), tag.ColorBg); - } - else { - pos = ImVec2(axis.Datum1 - size.x - gp.Style.LabelPadding.x, pix - size.y * 0.5f); - DrawList.AddTriangleFilled(pos + ImVec2(size.x,0), ImVec2(axis.Datum1,pix), pos+size, tag.ColorBg); - } - } - else { - if (axis.IsOpposite()) { - pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 - size.y - gp.Style.LabelPadding.y ); - DrawList.AddTriangleFilled(pos + ImVec2(0,size.y), pos + size, ImVec2(pix,axis.Datum1), tag.ColorBg); - } - else { - pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 + gp.Style.LabelPadding.y); - DrawList.AddTriangleFilled(pos, ImVec2(pix,axis.Datum1), pos + ImVec2(size.x, 0), tag.ColorBg); - } - } - DrawList.AddRectFilled(pos,pos+size,tag.ColorBg); - DrawList.AddText(pos+gp.Style.AnnotationPadding,tag.ColorFg,txt); - } - - // FIT DATA -------------------------------------------------------------- - const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); - if (plot.FitThisFrame) { - for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { - ImPlotAxis& x_axis = plot.XAxis(i); - if (x_axis.FitThisFrame) { - x_axis.ApplyFit(gp.Style.FitPadding.x); - if (axis_equal && x_axis.OrthoAxis != NULL) { - double aspect = x_axis.GetAspect(); - ImPlotAxis& y_axis = *x_axis.OrthoAxis; - if (y_axis.FitThisFrame) { - y_axis.ApplyFit(gp.Style.FitPadding.y); - y_axis.FitThisFrame = false; - aspect = ImMax(aspect, y_axis.GetAspect()); - } - x_axis.SetAspect(aspect); - y_axis.SetAspect(aspect); - } - } - } - for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { - ImPlotAxis& y_axis = plot.YAxis(i); - if (y_axis.FitThisFrame) { - y_axis.ApplyFit(gp.Style.FitPadding.y); - if (axis_equal && y_axis.OrthoAxis != NULL) { - double aspect = y_axis.GetAspect(); - ImPlotAxis& x_axis = *y_axis.OrthoAxis; - if (x_axis.FitThisFrame) { - x_axis.ApplyFit(gp.Style.FitPadding.x); - x_axis.FitThisFrame = false; - aspect = ImMax(x_axis.GetAspect(), aspect); - } - x_axis.SetAspect(aspect); - y_axis.SetAspect(aspect); - } - } - } - plot.FitThisFrame = false; - } - - // CONTEXT MENUS ----------------------------------------------------------- - - ImGui::PushOverrideID(plot.ID); - - const bool can_ctx = gp.OpenContextThisFrame && - !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && - !plot.Items.Legend.Hovered; - - - - // main ctx menu - if (can_ctx && plot.Hovered) - ImGui::OpenPopup("##PlotContext"); - if (ImGui::BeginPopup("##PlotContext")) { - ShowPlotContextMenu(plot); - ImGui::EndPopup(); - } - - // axes ctx menus - for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { - ImGui::PushID(i); - ImPlotAxis& x_axis = plot.XAxis(i); - if (can_ctx && x_axis.Hovered && x_axis.HasMenus()) - ImGui::OpenPopup("##XContext"); - if (ImGui::BeginPopup("##XContext")) { - ImGui::Text(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : i == 0 ? "X-Axis" : "X-Axis %d", i + 1); - ImGui::Separator(); - ShowAxisContextMenu(x_axis, axis_equal ? x_axis.OrthoAxis : NULL, true); - ImGui::EndPopup(); - } - ImGui::PopID(); - } - for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { - ImGui::PushID(i); - ImPlotAxis& y_axis = plot.YAxis(i); - if (can_ctx && y_axis.Hovered && y_axis.HasMenus()) - ImGui::OpenPopup("##YContext"); - if (ImGui::BeginPopup("##YContext")) { - ImGui::Text(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1); - ImGui::Separator(); - ShowAxisContextMenu(y_axis, axis_equal ? y_axis.OrthoAxis : NULL, false); - ImGui::EndPopup(); - } - ImGui::PopID(); - } - ImGui::PopID(); - - // LINKED AXES ------------------------------------------------------------ - - for (int i = 0; i < ImAxis_COUNT; ++i) - plot.Axes[i].PushLinks(); - - - // CLEANUP ---------------------------------------------------------------- - - // remove items - if (gp.CurrentItems == &plot.Items) - gp.CurrentItems = NULL; - // reset the plot items for the next frame - for (int i = 0; i < plot.Items.GetItemCount(); ++i) { - plot.Items.GetItemByIndex(i)->SeenThisFrame = false; - } - - // mark the plot as initialized, i.e. having made it through one frame completely - plot.Initialized = true; - // Pop ImGui::PushID at the end of BeginPlot - ImGui::PopID(); - // Reset context for next plot - ResetCtxForNextPlot(GImPlot); - - // setup next subplot - if (gp.CurrentSubplot != NULL) { - ImGui::PopID(); - SubplotNextCell(); - } -} - -//----------------------------------------------------------------------------- -// BEGIN/END SUBPLOT -//----------------------------------------------------------------------------- - -static const float SUBPLOT_BORDER_SIZE = 1.0f; -static const float SUBPLOT_SPLITTER_HALF_THICKNESS = 4.0f; -static const float SUBPLOT_SPLITTER_FEEDBACK_TIMER = 0.06f; - -void SubplotSetCell(int row, int col) { - ImPlotContext& gp = *GImPlot; - ImPlotSubplot& subplot = *gp.CurrentSubplot; - if (row >= subplot.Rows || col >= subplot.Cols) - return; - float xoff = 0; - float yoff = 0; - for (int c = 0; c < col; ++c) - xoff += subplot.ColRatios[c]; - for (int r = 0; r < row; ++r) - yoff += subplot.RowRatios[r]; - const ImVec2 grid_size = subplot.GridRect.GetSize(); - ImVec2 cpos = subplot.GridRect.Min + ImVec2(xoff*grid_size.x,yoff*grid_size.y); - cpos.x = IM_ROUND(cpos.x); - cpos.y = IM_ROUND(cpos.y); - ImGui::GetCurrentWindow()->DC.CursorPos = cpos; - // set cell size - subplot.CellSize.x = IM_ROUND(subplot.GridRect.GetWidth() * subplot.ColRatios[col]); - subplot.CellSize.y = IM_ROUND(subplot.GridRect.GetHeight() * subplot.RowRatios[row]); - // setup links - const bool lx = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX); - const bool ly = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY); - const bool lr = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows); - const bool lc = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols); - - SetNextAxisLinks(ImAxis_X1, lx ? &subplot.ColLinkData[0].Min : lc ? &subplot.ColLinkData[col].Min : NULL, - lx ? &subplot.ColLinkData[0].Max : lc ? &subplot.ColLinkData[col].Max : NULL); - SetNextAxisLinks(ImAxis_Y1, ly ? &subplot.RowLinkData[0].Min : lr ? &subplot.RowLinkData[row].Min : NULL, - ly ? &subplot.RowLinkData[0].Max : lr ? &subplot.RowLinkData[row].Max : NULL); - // setup alignment - if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign)) { - gp.CurrentAlignmentH = &subplot.RowAlignmentData[row]; - gp.CurrentAlignmentV = &subplot.ColAlignmentData[col]; - } - // set idx - if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) - subplot.CurrentIdx = col * subplot.Rows + row; - else - subplot.CurrentIdx = row * subplot.Cols + col; -} - -void SubplotSetCell(int idx) { - ImPlotContext& gp = *GImPlot; - ImPlotSubplot& subplot = *gp.CurrentSubplot; - if (idx >= subplot.Rows * subplot.Cols) - return; - int row = 0, col = 0; - if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) { - row = idx % subplot.Rows; - col = idx / subplot.Rows; - } - else { - row = idx / subplot.Cols; - col = idx % subplot.Cols; - } - return SubplotSetCell(row, col); -} - -void SubplotNextCell() { - ImPlotContext& gp = *GImPlot; - ImPlotSubplot& subplot = *gp.CurrentSubplot; - SubplotSetCell(++subplot.CurrentIdx); -} - -bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, ImPlotSubplotFlags flags, float* row_sizes, float* col_sizes) { - IM_ASSERT_USER_ERROR(rows > 0 && cols > 0, "Invalid sizing arguments!"); - IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); - IM_ASSERT_USER_ERROR(GImPlot->CurrentSubplot == NULL, "Mismatched BeginSubplots()/EndSubplots()!"); - ImPlotContext& gp = *GImPlot; - ImGuiContext &G = *GImGui; - ImGuiWindow * Window = G.CurrentWindow; - if (Window->SkipItems) - return false; - const ImGuiID ID = Window->GetID(title); - bool just_created = gp.Subplots.GetByKey(ID) == NULL; - gp.CurrentSubplot = gp.Subplots.GetOrAddByKey(ID); - ImPlotSubplot& subplot = *gp.CurrentSubplot; - subplot.ID = ID; - subplot.Items.ID = ID - 1; - subplot.HasTitle = ImGui::FindRenderedTextEnd(title, NULL) != title; - // push ID - ImGui::PushID(ID); - - if (just_created) - subplot.Flags = flags; - else if (flags != subplot.PreviousFlags) - subplot.Flags = flags; - subplot.PreviousFlags = flags; - - // check for change in rows and cols - if (subplot.Rows != rows || subplot.Cols != cols) { - subplot.RowAlignmentData.resize(rows); - subplot.RowLinkData.resize(rows); - subplot.RowRatios.resize(rows); - for (int r = 0; r < rows; ++r) { - subplot.RowAlignmentData[r].Reset(); - subplot.RowLinkData[r] = ImPlotRange(0,1); - subplot.RowRatios[r] = 1.0f / rows; - } - subplot.ColAlignmentData.resize(cols); - subplot.ColLinkData.resize(cols); - subplot.ColRatios.resize(cols); - for (int c = 0; c < cols; ++c) { - subplot.ColAlignmentData[c].Reset(); - subplot.ColLinkData[c] = ImPlotRange(0,1); - subplot.ColRatios[c] = 1.0f / cols; - } - } - // check incoming size requests - float row_sum = 0, col_sum = 0; - if (row_sizes != NULL) { - row_sum = ImSum(row_sizes, rows); - for (int r = 0; r < rows; ++r) - subplot.RowRatios[r] = row_sizes[r] / row_sum; - } - if (col_sizes != NULL) { - col_sum = ImSum(col_sizes, cols); - for (int c = 0; c < cols; ++c) - subplot.ColRatios[c] = col_sizes[c] / col_sum; - } - subplot.Rows = rows; - subplot.Cols = cols; - - // calc plot frame sizes - ImVec2 title_size(0.0f, 0.0f); - if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle)) - title_size = ImGui::CalcTextSize(title, NULL, true); - const float pad_top = title_size.x > 0.0f ? title_size.y + gp.Style.LabelPadding.y : 0; - const ImVec2 half_pad = gp.Style.PlotPadding/2; - const ImVec2 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); - subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); - subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top); - subplot.GridRect.Max = subplot.FrameRect.Max - half_pad; - subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows); - - // outside legend adjustments (TODO: make function) - const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); - if (share_items) - gp.CurrentItems = &subplot.Items; - if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { - ImPlotLegend& legend = subplot.Items.Legend; - const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); - const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz); - const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East); - const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West); - const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South); - const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North); - if ((west && !horz) || (west && horz && !north && !south)) - subplot.GridRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); - if ((east && !horz) || (east && horz && !north && !south)) - subplot.GridRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); - if ((north && horz) || (north && !horz && !west && !east)) - subplot.GridRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); - if ((south && horz) || (south && !horz && !west && !east)) - subplot.GridRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); - } - - // render single background frame - ImGui::RenderFrame(subplot.FrameRect.Min, subplot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, ImGui::GetStyle().FrameRounding); - // render title - if (title_size.x > 0.0f && !ImHasFlag(subplot.Flags, ImPlotFlags_NoTitle)) { - const ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); - AddTextCentered(ImGui::GetWindowDrawList(),ImVec2(subplot.GridRect.GetCenter().x, subplot.GridRect.Min.y - pad_top + half_pad.y),col,title); - } - - // render splitters - if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize)) { - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - const ImU32 hov_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorHovered]); - const ImU32 act_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]); - float xpos = subplot.GridRect.Min.x; - float ypos = subplot.GridRect.Min.y; - int separator = 1; - // bool pass = false; - for (int r = 0; r < subplot.Rows-1; ++r) { - ypos += subplot.RowRatios[r] * subplot.GridRect.GetHeight(); - const ImGuiID sep_id = subplot.ID + separator; - ImGui::KeepAliveID(sep_id); - const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS); - bool sep_hov = false, sep_hld = false; - const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); - if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { - if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { - float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2; - subplot.RowRatios[r] = subplot.RowRatios[r+1] = p; - } - if (sep_clk) { - subplot.TempSizes[0] = subplot.RowRatios[r]; - subplot.TempSizes[1] = subplot.RowRatios[r+1]; - } - if (sep_hld) { - float dp = ImGui::GetMouseDragDelta(0).y / subplot.GridRect.GetHeight(); - if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) { - subplot.RowRatios[r] = subplot.TempSizes[0] + dp; - subplot.RowRatios[r+1] = subplot.TempSizes[1] - dp; - } - } - DrawList.AddLine(ImVec2(IM_ROUND(subplot.GridRect.Min.x),IM_ROUND(ypos)), - ImVec2(IM_ROUND(subplot.GridRect.Max.x),IM_ROUND(ypos)), - sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE); - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); - } - separator++; - } - for (int c = 0; c < subplot.Cols-1; ++c) { - xpos += subplot.ColRatios[c] * subplot.GridRect.GetWidth(); - const ImGuiID sep_id = subplot.ID + separator; - ImGui::KeepAliveID(sep_id); - const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y); - bool sep_hov = false, sep_hld = false; - const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); - if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { - if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { - float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2; - subplot.ColRatios[c] = subplot.ColRatios[c+1] = p; - } - if (sep_clk) { - subplot.TempSizes[0] = subplot.ColRatios[c]; - subplot.TempSizes[1] = subplot.ColRatios[c+1]; - } - if (sep_hld) { - float dp = ImGui::GetMouseDragDelta(0).x / subplot.GridRect.GetWidth(); - if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) { - subplot.ColRatios[c] = subplot.TempSizes[0] + dp; - subplot.ColRatios[c+1] = subplot.TempSizes[1] - dp; - } - } - DrawList.AddLine(ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Min.y)), - ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Max.y)), - sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE); - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); - } - separator++; - } - } - - // set outgoing sizes - if (row_sizes != NULL) { - for (int r = 0; r < rows; ++r) - row_sizes[r] = subplot.RowRatios[r] * row_sum; - } - if (col_sizes != NULL) { - for (int c = 0; c < cols; ++c) - col_sizes[c] = subplot.ColRatios[c] * col_sum; - } - - // push styling - PushStyleColor(ImPlotCol_FrameBg, IM_COL32_BLACK_TRANS); - PushStyleVar(ImPlotStyleVar_PlotPadding, half_pad); - PushStyleVar(ImPlotStyleVar_PlotMinSize, ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize,0); - - // set initial cursor pos - Window->DC.CursorPos = subplot.GridRect.Min; - // begin alignments - for (int r = 0; r < subplot.Rows; ++r) - subplot.RowAlignmentData[r].Begin(); - for (int c = 0; c < subplot.Cols; ++c) - subplot.ColAlignmentData[c].Begin(); - // clear legend data - subplot.Items.Legend.Reset(); - // Setup first subplot - SubplotSetCell(0,0); - return true; -} - -void EndSubplots() { - IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); - IM_ASSERT_USER_ERROR(GImPlot->CurrentSubplot != NULL, "Mismatched BeginSubplots()/EndSubplots()!"); - ImPlotContext& gp = *GImPlot; - ImPlotSubplot& subplot = *GImPlot->CurrentSubplot; - // set alignments - for (int r = 0; r < subplot.Rows; ++r) - subplot.RowAlignmentData[r].End(); - for (int c = 0; c < subplot.Cols; ++c) - subplot.ColAlignmentData[c].End(); - // pop styling - PopStyleColor(); - PopStyleVar(); - PopStyleVar(); - ImGui::PopStyleVar(); - // legend - subplot.Items.Legend.Hovered = false; - for (int i = 0; i < subplot.Items.GetItemCount(); ++i) - subplot.Items.GetItemByIndex(i)->LegendHovered = false; - // render legend - const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { - const bool legend_horz = ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_Horizontal); - const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); - const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, subplot.Items.Legend.Location, gp.Style.PlotPadding); - subplot.Items.Legend.Rect = ImRect(legend_pos, legend_pos + legend_size); - subplot.Items.Legend.Hovered = subplot.FrameHovered && subplot.Items.Legend.Rect.Contains(ImGui::GetIO().MousePos); - ImGui::PushClipRect(subplot.FrameRect.Min, subplot.FrameRect.Max, true); - ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); - ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); - DrawList.AddRectFilled(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bg); - DrawList.AddRect(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bd); - bool legend_contextable = ShowLegendEntries(subplot.Items, subplot.Items.Legend.Rect, subplot.Items.Legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) - && !ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_NoMenus); - if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu]) - ImGui::OpenPopup("##LegendContext"); - ImGui::PopClipRect(); - if (ImGui::BeginPopup("##LegendContext")) { - ImGui::Text("Legend"); ImGui::Separator(); - if (ShowLegendContextMenu(subplot.Items.Legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend))) - ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend); - ImGui::EndPopup(); - } - } - else { - subplot.Items.Legend.Rect = ImRect(); - } - // remove items - if (gp.CurrentItems == &subplot.Items) - gp.CurrentItems = NULL; - // reset the plot items for the next frame (TODO: put this elswhere) - for (int i = 0; i < subplot.Items.GetItemCount(); ++i) { - subplot.Items.GetItemByIndex(i)->SeenThisFrame = false; - } - // pop id - ImGui::PopID(); - // set DC back correctly - GImGui->CurrentWindow->DC.CursorPos = subplot.FrameRect.Min; - ImGui::Dummy(subplot.FrameRect.GetSize()); - ResetCtxForNextSubplot(GImPlot); - -} - -//----------------------------------------------------------------------------- -// [SECTION] Plot Utils -//----------------------------------------------------------------------------- - -void SetAxis(ImAxis axis) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetAxis() needs to be called between BeginPlot() and EndPlot()!"); - IM_ASSERT_USER_ERROR(axis >= ImAxis_X1 && axis < ImAxis_COUNT, "Axis index out of bounds!"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[axis].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - SetupLock(); - if (axis < ImAxis_Y1) - gp.CurrentPlot->CurrentX = axis; - else - gp.CurrentPlot->CurrentY = axis; -} - -void SetAxes(ImAxis x_idx, ImAxis y_idx) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetAxes() needs to be called between BeginPlot() and EndPlot()!"); - IM_ASSERT_USER_ERROR(x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1, "X-Axis index out of bounds!"); - IM_ASSERT_USER_ERROR(y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT, "Y-Axis index out of bounds!"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[x_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[y_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); - SetupLock(); - gp.CurrentPlot->CurrentX = x_idx; - gp.CurrentPlot->CurrentY = y_idx; -} - -ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_idx, ImAxis y_idx) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!"); - IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); - IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); - SetupLock(); - ImPlotPlot& plot = *gp.CurrentPlot; - ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; - ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; - return ImPlotPoint( x_axis.PixelsToPlot(x), y_axis.PixelsToPlot(y) ); -} - -ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_idx, ImAxis y_idx) { - return PixelsToPlot(pix.x, pix.y, x_idx, y_idx); -} - -ImVec2 PlotToPixels(double x, double y, ImAxis x_idx, ImAxis y_idx) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!"); - IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); - IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); - SetupLock(); - ImPlotPlot& plot = *gp.CurrentPlot; - ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; - ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; - return ImVec2( x_axis.PlotToPixels(x), y_axis.PlotToPixels(y) ); -} - -ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_idx, ImAxis y_idx) { - return PlotToPixels(plt.x, plt.y, x_idx, y_idx); -} - -ImVec2 GetPlotPos() { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotPos() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - return gp.CurrentPlot->PlotRect.Min; -} - -ImVec2 GetPlotSize() { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotSize() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - return gp.CurrentPlot->PlotRect.GetSize(); -} - -ImPlotPoint GetPlotMousePos(ImAxis x_idx, ImAxis y_idx) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - return PixelsToPlot(ImGui::GetMousePos(), x_idx, y_idx); -} - -ImPlotRect GetPlotLimits(ImAxis x_idx, ImAxis y_idx) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!"); - IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); - IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); - SetupLock(); - ImPlotPlot& plot = *gp.CurrentPlot; - ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; - ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; - ImPlotRect limits; - limits.X = x_axis.Range; - limits.Y = y_axis.Range; - return limits; -} - -bool IsPlotHovered() { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - return gp.CurrentPlot->Hovered; -} - -bool IsAxisHovered(ImAxis axis) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotXAxisHovered() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - return gp.CurrentPlot->Axes[axis].Hovered; -} - -bool IsSubplotsHovered() { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentSubplot != NULL, "IsSubplotsHovered() needs to be called between BeginSubplots() and EndSubplots()!"); - return gp.CurrentSubplot->FrameHovered; -} - -bool IsPlotSelected() { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotSelected() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - return gp.CurrentPlot->Selected; -} - -ImPlotRect GetPlotSelection(ImAxis x_idx, ImAxis y_idx) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotSelection() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - ImPlotPlot& plot = *gp.CurrentPlot; - if (!plot.Selected) - return ImPlotRect(0,0,0,0); - ImPlotPoint p1 = PixelsToPlot(plot.SelectRect.Min + plot.PlotRect.Min, x_idx, y_idx); - ImPlotPoint p2 = PixelsToPlot(plot.SelectRect.Max + plot.PlotRect.Min, x_idx, y_idx); - ImPlotRect result; - result.X.Min = ImMin(p1.x, p2.x); - result.X.Max = ImMax(p1.x, p2.x); - result.Y.Min = ImMin(p1.y, p2.y); - result.Y.Max = ImMax(p1.y, p2.y); - return result; -} - -void CancelPlotSelection() { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "CancelPlotSelection() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - ImPlotPlot& plot = *gp.CurrentPlot; - if (plot.Selected) - plot.Selected = plot.Selecting = false; -} - -void HideNextItem(bool hidden, ImPlotCond cond) { - ImPlotContext& gp = *GImPlot; - gp.NextItemData.HasHidden = true; - gp.NextItemData.Hidden = hidden; - gp.NextItemData.HiddenCond = cond; -} - -//----------------------------------------------------------------------------- -// [SECTION] Plot Tools -//----------------------------------------------------------------------------- - -void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, bool round) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Annotation() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - char x_buff[IMPLOT_LABEL_MAX_SIZE]; - char y_buff[IMPLOT_LABEL_MAX_SIZE]; - ImPlotAxis& x_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX]; - ImPlotAxis& y_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX]; - LabelAxisValue(x_axis, x, x_buff, sizeof(x_buff), round); - LabelAxisValue(y_axis, y, y_buff, sizeof(y_buff), round); - Annotation(x,y,col,offset,clamp,"%s, %s",x_buff,y_buff); -} - -void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, va_list args) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Annotation() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - ImVec2 pos = PlotToPixels(x,y,IMPLOT_AUTO,IMPLOT_AUTO); - ImU32 bg = ImGui::GetColorU32(col); - ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_InlayText) : CalcTextColor(col); - gp.Annotations.AppendV(pos, offset, bg, fg, clamp, fmt, args); -} - -void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - AnnotationV(x,y,col,offset,clamp,fmt,args); - va_end(args); -} - -void TagV(ImAxis axis, double v, const ImVec4& col, const char* fmt, va_list args) { - ImPlotContext& gp = *GImPlot; - SetupLock(); - ImU32 bg = ImGui::GetColorU32(col); - ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_AxisText) : CalcTextColor(col); - gp.Tags.AppendV(axis,v,bg,fg,fmt,args); -} - -void Tag(ImAxis axis, double v, const ImVec4& col, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - TagV(axis,v,col,fmt,args); - va_end(args); -} - -void Tag(ImAxis axis, double v, const ImVec4& color, bool round) { - ImPlotContext& gp = *GImPlot; - SetupLock(); - char buff[IMPLOT_LABEL_MAX_SIZE]; - ImPlotAxis& ax = gp.CurrentPlot->Axes[axis]; - LabelAxisValue(ax, v, buff, sizeof(buff), round); - Tag(axis,v,color,"%s",buff); -} - -IMPLOT_API void TagX(double x, const ImVec4& color, bool round) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagX() needs to be called between BeginPlot() and EndPlot()!"); - Tag(GImPlot->CurrentPlot->CurrentX, x, color, round); -} - -IMPLOT_API void TagX(double x, const ImVec4& color, const char* fmt, ...) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagX() needs to be called between BeginPlot() and EndPlot()!"); - va_list args; - va_start(args, fmt); - TagV(GImPlot->CurrentPlot->CurrentX,x,color,fmt,args); - va_end(args); -} - -IMPLOT_API void TagXV(double x, const ImVec4& color, const char* fmt, va_list args) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagX() needs to be called between BeginPlot() and EndPlot()!"); - TagV(GImPlot->CurrentPlot->CurrentX, x, color, fmt, args); -} - -IMPLOT_API void TagY(double y, const ImVec4& color, bool round) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagY() needs to be called between BeginPlot() and EndPlot()!"); - Tag(GImPlot->CurrentPlot->CurrentY, y, color, round); -} - -IMPLOT_API void TagY(double y, const ImVec4& color, const char* fmt, ...) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagY() needs to be called between BeginPlot() and EndPlot()!"); - va_list args; - va_start(args, fmt); - TagV(GImPlot->CurrentPlot->CurrentY,y,color,fmt,args); - va_end(args); -} - -IMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list args) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagY() needs to be called between BeginPlot() and EndPlot()!"); - TagV(GImPlot->CurrentPlot->CurrentY, y, color, fmt, args); -} - -static const float DRAG_GRAB_HALF_SIZE = 4.0f; - -bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags) { - ImGui::PushID("#IMPLOT_DRAG_POINT"); - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "DragPoint() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - - if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { - FitPoint(ImPlotPoint(*x,*y)); - } - - const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); - const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); - const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); - const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, radius); - const ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; - const ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); - - ImVec2 pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); - const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); - ImRect rect(pos.x-grab_half_size,pos.y-grab_half_size,pos.x+grab_half_size,pos.y+grab_half_size); - bool hovered = false, held = false; - - ImGui::KeepAliveID(id); - if (input) - ImGui::ButtonBehavior(rect,id,&hovered,&held); - - bool dragging = false; - if (held && ImGui::IsMouseDragging(0)) { - *x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; - *y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; - dragging = true; - } - - PushPlotClipRect(); - ImDrawList& DrawList = *GetPlotDrawList(); - if ((hovered || held) && show_curs) - ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); - if (dragging && no_delay) - pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); - DrawList.AddCircleFilled(pos, radius, col32); - PopPlotClipRect(); - - ImGui::PopID(); - return dragging; -} - -bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) { - // ImGui::PushID("#IMPLOT_DRAG_LINE_X"); - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragLineX() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - - if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { - FitPointX(*value); - } - - const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); - const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); - const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); - const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2); - float yt = gp.CurrentPlot->PlotRect.Min.y; - float yb = gp.CurrentPlot->PlotRect.Max.y; - float x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); - const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); - ImRect rect(x-grab_half_size,yt,x+grab_half_size,yb); - bool hovered = false, held = false; - - ImGui::KeepAliveID(id); - if (input) - ImGui::ButtonBehavior(rect,id,&hovered,&held); - - if ((hovered || held) && show_curs) - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); - - float len = gp.Style.MajorTickLen.x; - ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; - ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); - - bool dragging = false; - if (held && ImGui::IsMouseDragging(0)) { - *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; - dragging = true; - } - - PushPlotClipRect(); - ImDrawList& DrawList = *GetPlotDrawList(); - if (dragging && no_delay) - x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); - DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness); - DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness); - DrawList.AddLine(ImVec2(x,yb), ImVec2(x,yb-len), col32, 3*thickness); - PopPlotClipRect(); - - // ImGui::PopID(); - return dragging; -} - -bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) { - ImGui::PushID("#IMPLOT_DRAG_LINE_Y"); - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragLineY() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - - if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { - FitPointY(*value); - } - - const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); - const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); - const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); - const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2); - float xl = gp.CurrentPlot->PlotRect.Min.x; - float xr = gp.CurrentPlot->PlotRect.Max.x; - float y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); - - const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); - ImRect rect(xl,y-grab_half_size,xr,y+grab_half_size); - bool hovered = false, held = false; - - ImGui::KeepAliveID(id); - if (input) - ImGui::ButtonBehavior(rect,id,&hovered,&held); - - if ((hovered || held) && show_curs) - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); - - float len = gp.Style.MajorTickLen.y; - ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; - ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); - - bool dragging = false; - if (held && ImGui::IsMouseDragging(0)) { - *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; - dragging = true; - } - - PushPlotClipRect(); - ImDrawList& DrawList = *GetPlotDrawList(); - if (dragging && no_delay) - y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); - DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness); - DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness); - DrawList.AddLine(ImVec2(xr,y), ImVec2(xr-len,y), col32, 3*thickness); - PopPlotClipRect(); - - ImGui::PopID(); - return dragging; -} - -bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags) { - ImGui::PushID("#IMPLOT_DRAG_RECT"); - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "DragRect() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - - if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { - FitPoint(ImPlotPoint(*x_min,*y_min)); - FitPoint(ImPlotPoint(*x_max,*y_max)); - } - - const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); - const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); - const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); - bool h[] = {true,false,true,false}; - double* x[] = {x_min,x_max,x_max,x_min}; - double* y[] = {y_min,y_min,y_max,y_max}; - ImVec2 p[4]; - for (int i = 0; i < 4; ++i) - p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); - ImVec2 pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); - ImRect rect(ImMin(p[0],p[2]),ImMax(p[0],p[2])); - ImRect rect_grab = rect; rect_grab.Expand(DRAG_GRAB_HALF_SIZE); - - ImGuiMouseCursor cur[4]; - if (show_curs) { - cur[0] = (rect.Min.x == p[0].x && rect.Min.y == p[0].y) || (rect.Max.x == p[0].x && rect.Max.y == p[0].y) ? ImGuiMouseCursor_ResizeNWSE : ImGuiMouseCursor_ResizeNESW; - cur[1] = cur[0] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - cur[2] = cur[1] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - cur[3] = cur[2] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - } - - ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; - ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); - color.w *= 0.25f; - ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color); - const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); - - bool dragging = false; - bool hovered = false, held = false; - ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE); - - ImGui::KeepAliveID(id); - if (input) - ImGui::ButtonBehavior(b_rect,id,&hovered,&held); - - if ((hovered || held) && show_curs) - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); - if (held && ImGui::IsMouseDragging(0)) { - for (int i = 0; i < 4; ++i) { - ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO); - *y[i] = pp.y; - *x[i] = pp.x; - } - dragging = true; - } - - for (int i = 0; i < 4; ++i) { - // points - b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE); - ImGuiID p_id = id + i + 1; - ImGui::KeepAliveID(p_id); - if (input) - ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held); - if ((hovered || held) && show_curs) - ImGui::SetMouseCursor(cur[i]); - - if (held && ImGui::IsMouseDragging(0)) { - *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; - *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; - dragging = true; - } - - // edges - ImVec2 e_min = ImMin(p[i],p[(i+1)%4]); - ImVec2 e_max = ImMax(p[i],p[(i+1)%4]); - b_rect = h[i] ? ImRect(e_min.x + DRAG_GRAB_HALF_SIZE, e_min.y - DRAG_GRAB_HALF_SIZE, e_max.x - DRAG_GRAB_HALF_SIZE, e_max.y + DRAG_GRAB_HALF_SIZE) - : ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE); - ImGuiID e_id = id + i + 5; - ImGui::KeepAliveID(e_id); - if (input) - ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held); - if ((hovered || held) && show_curs) - h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); - if (held && ImGui::IsMouseDragging(0)) { - if (h[i]) - *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; - else - *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; - dragging = true; - } - if (hovered && ImGui::IsMouseDoubleClicked(0)) - { - ImPlotRect b = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO); - if (h[i]) - *y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max; - else - *x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max; - dragging = true; - } - } - - - PushPlotClipRect(); - ImDrawList& DrawList = *GetPlotDrawList(); - if (dragging && no_delay) { - for (int i = 0; i < 4; ++i) - p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); - pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); - rect = ImRect(ImMin(p[0],p[2]),ImMax(p[0],p[2])); - } - DrawList.AddRectFilled(rect.Min, rect.Max, col32_a); - DrawList.AddRect(rect.Min, rect.Max, col32); - if (input && (dragging || rect_grab.Contains(ImGui::GetMousePos()))) { - DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32); - for (int i = 0; i < 4; ++i) - DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32); - } - PopPlotClipRect(); - ImGui::PopID(); - return dragging; -} - -bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags) { - return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags); -} - -//----------------------------------------------------------------------------- -// [SECTION] Legend Utils and Tools -//----------------------------------------------------------------------------- - -bool IsLegendEntryHovered(const char* label_id) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentItems != NULL, "IsPlotItemHighlight() needs to be called within an itemized context!"); - SetupLock(); - ImGuiID id = ImGui::GetIDWithSeed(label_id, NULL, gp.CurrentItems->ID); - ImPlotItem* item = gp.CurrentItems->GetItem(id); - return item && item->LegendHovered; -} - -bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentItems != NULL, "BeginLegendPopup() needs to be called within an itemized context!"); - SetupLock(); - ImGuiWindow* window = GImGui->CurrentWindow; - if (window->SkipItems) - return false; - ImGuiID id = ImGui::GetIDWithSeed(label_id, NULL, gp.CurrentItems->ID); - if (ImGui::IsMouseReleased(mouse_button)) { - ImPlotItem* item = gp.CurrentItems->GetItem(id); - if (item && item->LegendHovered) - ImGui::OpenPopupEx(id); - } - return ImGui::BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); -} - -void EndLegendPopup() { - SetupLock(); - ImGui::EndPopup(); -} - -void ShowAltLegend(const char* title_id, bool vertical, const ImVec2 size, bool interactable) { - ImPlotContext& gp = *GImPlot; - ImGuiContext &G = *GImGui; - ImGuiWindow * Window = G.CurrentWindow; - if (Window->SkipItems) - return; - ImDrawList &DrawList = *Window->DrawList; - ImPlotPlot* plot = GetPlot(title_id); - ImVec2 legend_size; - ImVec2 default_size = gp.Style.LegendPadding * 2; - if (plot != NULL) { - legend_size = CalcLegendSize(plot->Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical); - default_size = legend_size + gp.Style.LegendPadding * 2; - } - ImVec2 frame_size = ImGui::CalcItemSize(size, default_size.x, default_size.y); - ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); - ImGui::ItemSize(bb_frame); - if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame)) - return; - ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding); - DrawList.PushClipRect(bb_frame.Min, bb_frame.Max, true); - if (plot != NULL) { - const ImVec2 legend_pos = GetLocationPos(bb_frame, legend_size, 0, gp.Style.LegendPadding); - const ImRect legend_bb(legend_pos, legend_pos + legend_size); - interactable = interactable && bb_frame.Contains(ImGui::GetIO().MousePos); - // render legend box - ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); - ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); - DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg); - DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd); - // render entries - ShowLegendEntries(plot->Items, legend_bb, interactable, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical, DrawList); - } - DrawList.PopClipRect(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Drag and Drop Utils -//----------------------------------------------------------------------------- - -bool BeginDragDropTargetPlot() { - SetupLock(); - ImRect rect = GImPlot->CurrentPlot->PlotRect; - return ImGui::BeginDragDropTargetCustom(rect, GImPlot->CurrentPlot->ID); -} - -bool BeginDragDropTargetAxis(ImAxis axis) { - SetupLock(); - ImPlotPlot& plot = *GImPlot->CurrentPlot; - ImPlotAxis& ax = plot.Axes[axis]; - ImRect rect = ax.HoverRect; - rect.Expand(-3.5f); - return ImGui::BeginDragDropTargetCustom(rect, ax.ID); -} - -bool BeginDragDropTargetLegend() { - SetupLock(); - ImPlotItemGroup& items = *GImPlot->CurrentItems; - ImRect rect = items.Legend.Rect; - return ImGui::BeginDragDropTargetCustom(rect, items.ID); -} - -void EndDragDropTarget() { - SetupLock(); - ImGui::EndDragDropTarget(); -} - -bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags) { - SetupLock(); - ImPlotPlot* plot = GImPlot->CurrentPlot; - if (GImGui->IO.KeyMods == GImPlot->InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == plot->ID) - return ImGui::ItemAdd(plot->PlotRect, plot->ID) && ImGui::BeginDragDropSource(flags); - return false; -} - -bool BeginDragDropSourceAxis(ImAxis idx, ImGuiDragDropFlags flags) { - SetupLock(); - ImPlotAxis& axis = GImPlot->CurrentPlot->Axes[idx]; - if (GImGui->IO.KeyMods == GImPlot->InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == axis.ID) - return ImGui::ItemAdd(axis.HoverRect, axis.ID) && ImGui::BeginDragDropSource(flags); - return false; -} - -bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags) { - SetupLock(); - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentItems != NULL, "BeginDragDropSourceItem() needs to be called within an itemized context!"); - ImGuiID item_id = ImGui::GetIDWithSeed(label_id, NULL, gp.CurrentItems->ID); - ImPlotItem* item = gp.CurrentItems->GetItem(item_id); - if (item != NULL) { - return ImGui::ItemAdd(item->LegendHoverRect, item->ID) && ImGui::BeginDragDropSource(flags); - } - return false; -} - -void EndDragDropSource() { - SetupLock(); - ImGui::EndDragDropSource(); -} - -//----------------------------------------------------------------------------- -// [SECTION] Aligned Plots -//----------------------------------------------------------------------------- - -bool BeginAlignedPlots(const char* group_id, bool vertical) { - IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); - IM_ASSERT_USER_ERROR(GImPlot->CurrentAlignmentH == NULL && GImPlot->CurrentAlignmentV == NULL, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!"); - ImPlotContext& gp = *GImPlot; - ImGuiContext &G = *GImGui; - ImGuiWindow * Window = G.CurrentWindow; - if (Window->SkipItems) - return false; - const ImGuiID ID = Window->GetID(group_id); - ImPlotAlignmentData* alignment = gp.AlignmentData.GetOrAddByKey(ID); - if (vertical) - gp.CurrentAlignmentV = alignment; - else - gp.CurrentAlignmentH = alignment; - if (alignment->Vertical != vertical) - alignment->Reset(); - alignment->Vertical = vertical; - alignment->Begin(); - return true; -} - -void EndAlignedPlots() { - IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); - IM_ASSERT_USER_ERROR(GImPlot->CurrentAlignmentH != NULL || GImPlot->CurrentAlignmentV != NULL, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!"); - ImPlotContext& gp = *GImPlot; - ImPlotAlignmentData* alignment = gp.CurrentAlignmentH != NULL ? gp.CurrentAlignmentH : (gp.CurrentAlignmentV != NULL ? gp.CurrentAlignmentV : NULL); - if (alignment) - alignment->End(); - ResetCtxForNextAlignedPlots(GImPlot); -} - -//----------------------------------------------------------------------------- -// [SECTION] Plot and Item Styling -//----------------------------------------------------------------------------- - -ImPlotStyle& GetStyle() { - IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); - ImPlotContext& gp = *GImPlot; - return gp.Style; -} - -void PushStyleColor(ImPlotCol idx, ImU32 col) { - ImPlotContext& gp = *GImPlot; - ImGuiColorMod backup; - backup.Col = idx; - backup.BackupValue = gp.Style.Colors[idx]; - gp.ColorModifiers.push_back(backup); - gp.Style.Colors[idx] = ImGui::ColorConvertU32ToFloat4(col); -} - -void PushStyleColor(ImPlotCol idx, const ImVec4& col) { - ImPlotContext& gp = *GImPlot; - ImGuiColorMod backup; - backup.Col = idx; - backup.BackupValue = gp.Style.Colors[idx]; - gp.ColorModifiers.push_back(backup); - gp.Style.Colors[idx] = col; -} - -void PopStyleColor(int count) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(count <= gp.ColorModifiers.Size, "You can't pop more modifiers than have been pushed!"); - while (count > 0) - { - ImGuiColorMod& backup = gp.ColorModifiers.back(); - gp.Style.Colors[backup.Col] = backup.BackupValue; - gp.ColorModifiers.pop_back(); - count--; - } -} - -void PushStyleVar(ImPlotStyleVar idx, float val) { - ImPlotContext& gp = *GImPlot; - const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { - float* pvar = (float*)var_info->GetVarPtr(&gp.Style); - gp.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); - *pvar = val; - return; - } - IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); -} - -void PushStyleVar(ImPlotStyleVar idx, int val) { - ImPlotContext& gp = *GImPlot; - const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_S32 && var_info->Count == 1) { - int* pvar = (int*)var_info->GetVarPtr(&gp.Style); - gp.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); - *pvar = val; - return; - } - else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { - float* pvar = (float*)var_info->GetVarPtr(&gp.Style); - gp.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); - *pvar = (float)val; - return; - } - IM_ASSERT(0 && "Called PushStyleVar() int variant but variable is not a int!"); -} - -void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) -{ - ImPlotContext& gp = *GImPlot; - const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) - { - ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&gp.Style); - gp.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); - *pvar = val; - return; - } - IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); -} - -void PopStyleVar(int count) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(count <= gp.StyleModifiers.Size, "You can't pop more modifiers than have been pushed!"); - while (count > 0) { - ImGuiStyleMod& backup = gp.StyleModifiers.back(); - const ImPlotStyleVarInfo* info = GetPlotStyleVarInfo(backup.VarIdx); - void* data = info->GetVarPtr(&gp.Style); - if (info->Type == ImGuiDataType_Float && info->Count == 1) { - ((float*)data)[0] = backup.BackupFloat[0]; - } - else if (info->Type == ImGuiDataType_Float && info->Count == 2) { - ((float*)data)[0] = backup.BackupFloat[0]; - ((float*)data)[1] = backup.BackupFloat[1]; - } - else if (info->Type == ImGuiDataType_S32 && info->Count == 1) { - ((int*)data)[0] = backup.BackupInt[0]; - } - gp.StyleModifiers.pop_back(); - count--; - } -} - -//------------------------------------------------------------------------------ -// [Section] Colormaps -//------------------------------------------------------------------------------ - -ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size, bool qual) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); - IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already been used!"); - ImVector buffer; - buffer.resize(size); - for (int i = 0; i < size; ++i) - buffer[i] = ImGui::ColorConvertFloat4ToU32(colormap[i]); - return gp.ColormapData.Append(name, buffer.Data, size, qual); -} - -ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size, bool qual) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); - IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already be used!"); - return gp.ColormapData.Append(name, colormap, size, qual); -} - -int GetColormapCount() { - ImPlotContext& gp = *GImPlot; - return gp.ColormapData.Count; -} - -const char* GetColormapName(ImPlotColormap colormap) { - ImPlotContext& gp = *GImPlot; - return gp.ColormapData.GetName(colormap); -} - -ImPlotColormap GetColormapIndex(const char* name) { - ImPlotContext& gp = *GImPlot; - return gp.ColormapData.GetIndex(name); -} - -void PushColormap(ImPlotColormap colormap) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(colormap >= 0 && colormap < gp.ColormapData.Count, "The colormap index is invalid!"); - gp.ColormapModifiers.push_back(gp.Style.Colormap); - gp.Style.Colormap = colormap; -} - -void PushColormap(const char* name) { - ImPlotContext& gp = *GImPlot; - ImPlotColormap idx = gp.ColormapData.GetIndex(name); - IM_ASSERT_USER_ERROR(idx != -1, "The colormap name is invalid!"); - PushColormap(idx); -} - -void PopColormap(int count) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(count <= gp.ColormapModifiers.Size, "You can't pop more modifiers than have been pushed!"); - while (count > 0) { - const ImPlotColormap& backup = gp.ColormapModifiers.back(); - gp.Style.Colormap = backup; - gp.ColormapModifiers.pop_back(); - count--; - } -} - -ImU32 NextColormapColorU32() { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentItems != NULL, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); - int idx = gp.CurrentItems->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); - ImU32 col = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx); - gp.CurrentItems->ColormapIdx++; - return col; -} - -ImVec4 NextColormapColor() { - return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32()); -} - -int GetColormapSize(ImPlotColormap cmap) { - ImPlotContext& gp = *GImPlot; - cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; - IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); - return gp.ColormapData.GetKeyCount(cmap); -} - -ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap) { - ImPlotContext& gp = *GImPlot; - cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; - IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); - idx = idx % gp.ColormapData.GetKeyCount(cmap); - return gp.ColormapData.GetKeyColor(cmap, idx); -} - -ImVec4 GetColormapColor(int idx, ImPlotColormap cmap) { - return ImGui::ColorConvertU32ToFloat4(GetColormapColorU32(idx,cmap)); -} - -ImU32 SampleColormapU32(float t, ImPlotColormap cmap) { - ImPlotContext& gp = *GImPlot; - cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; - IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); - return gp.ColormapData.LerpTable(cmap, t); -} - -ImVec4 SampleColormap(float t, ImPlotColormap cmap) { - return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t,cmap)); -} - -void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) { - const int n = continuous ? size - 1 : size; - ImU32 col1, col2; - if (vert) { - const float step = bounds.GetHeight() / n; - ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Max.x, bounds.Min.y + step); - for (int i = 0; i < n; ++i) { - if (reversed) { - col1 = colors[size-i-1]; - col2 = continuous ? colors[size-i-2] : col1; - } - else { - col1 = colors[i]; - col2 = continuous ? colors[i+1] : col1; - } - DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2); - rect.TranslateY(step); - } - } - else { - const float step = bounds.GetWidth() / n; - ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Min.x + step, bounds.Max.y); - for (int i = 0; i < n; ++i) { - if (reversed) { - col1 = colors[size-i-1]; - col2 = continuous ? colors[size-i-2] : col1; - } - else { - col1 = colors[i]; - col2 = continuous ? colors[i+1] : col1; - } - DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1); - rect.TranslateX(step); - } - } -} - -void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size, const char* format, ImPlotColormapScaleFlags flags, ImPlotColormap cmap) { - ImGuiContext &G = *GImGui; - ImGuiWindow * Window = G.CurrentWindow; - if (Window->SkipItems) - return; - - const ImGuiID ID = Window->GetID(label); - ImVec2 label_size(0,0); - if (!ImHasFlag(flags, ImPlotColormapScaleFlags_NoLabel)) { - label_size = ImGui::CalcTextSize(label,NULL,true); - } - - ImPlotContext& gp = *GImPlot; - cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; - IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); - - ImVec2 frame_size = ImGui::CalcItemSize(size, 0, gp.Style.PlotDefaultSize.y); - if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f) - frame_size.y = gp.Style.PlotMinSize.y; - - ImPlotRange range(ImMin(scale_min,scale_max), ImMax(scale_min,scale_max)); - gp.CTicker.Reset(); - Locator_Default(gp.CTicker, range, frame_size.y, true, Formatter_Default, (void*)format); - - const bool rend_label = label_size.x > 0; - const float txt_off = gp.Style.LabelPadding.x; - const float pad = txt_off + gp.CTicker.MaxSize.x + (rend_label ? txt_off + label_size.y : 0); - float bar_w = 20; - if (frame_size.x == 0) - frame_size.x = bar_w + pad + 2 * gp.Style.PlotPadding.x; - else { - bar_w = frame_size.x - (pad + 2 * gp.Style.PlotPadding.x); - if (bar_w < gp.Style.MajorTickLen.y) - bar_w = gp.Style.MajorTickLen.y; - } - - ImDrawList &DrawList = *Window->DrawList; - ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); - ImGui::ItemSize(bb_frame); - if (!ImGui::ItemAdd(bb_frame, ID, &bb_frame)) - return; - - ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding); - - const bool opposite = ImHasFlag(flags, ImPlotColormapScaleFlags_Opposite); - const bool inverted = ImHasFlag(flags, ImPlotColormapScaleFlags_Invert); - const bool reversed = scale_min > scale_max; - - float bb_grad_shift = opposite ? pad : 0; - ImRect bb_grad(bb_frame.Min + gp.Style.PlotPadding + ImVec2(bb_grad_shift, 0), - bb_frame.Min + ImVec2(bar_w + gp.Style.PlotPadding.x + bb_grad_shift, - frame_size.y - gp.Style.PlotPadding.y)); - - ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true); - const ImU32 col_text = ImGui::GetColorU32(ImGuiCol_Text); - - const bool invert_scale = inverted ? (reversed ? false : true) : (reversed ? true : false); - const float y_min = invert_scale ? bb_grad.Max.y : bb_grad.Min.y; - const float y_max = invert_scale ? bb_grad.Min.y : bb_grad.Max.y; - - RenderColorBar(gp.ColormapData.GetKeys(cmap), gp.ColormapData.GetKeyCount(cmap), DrawList, bb_grad, true, !inverted, !gp.ColormapData.IsQual(cmap)); - for (int i = 0; i < gp.CTicker.TickCount(); ++i) { - const double y_pos_plt = gp.CTicker.Ticks[i].PlotPos; - const float y_pos = ImRemap((float)y_pos_plt, (float)range.Max, (float)range.Min, y_min, y_max); - const float tick_width = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; - const float tick_thick = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; - const float tick_t = (float)((y_pos_plt - scale_min) / (scale_max - scale_min)); - const ImU32 tick_col = CalcTextColor(GImPlot->ColormapData.LerpTable(cmap,tick_t)); - if (y_pos < bb_grad.Max.y - 2 && y_pos > bb_grad.Min.y + 2) { - DrawList.AddLine(opposite ? ImVec2(bb_grad.Min.x+1, y_pos) : ImVec2(bb_grad.Max.x-1, y_pos), - opposite ? ImVec2(bb_grad.Min.x + tick_width, y_pos) : ImVec2(bb_grad.Max.x - tick_width, y_pos), - tick_col, - tick_thick); - } - const float txt_x = opposite ? bb_grad.Min.x - txt_off - gp.CTicker.Ticks[i].LabelSize.x : bb_grad.Max.x + txt_off; - const float txt_y = y_pos - gp.CTicker.Ticks[i].LabelSize.y * 0.5f; - DrawList.AddText(ImVec2(txt_x, txt_y), col_text, gp.CTicker.GetText(i)); - } - - if (rend_label) { - const float pos_x = opposite ? bb_frame.Min.x + gp.Style.PlotPadding.x : bb_grad.Max.x + 2 * txt_off + gp.CTicker.MaxSize.x; - const float pos_y = bb_grad.GetCenter().y + label_size.x * 0.5f; - const char* label_end = ImGui::FindRenderedTextEnd(label); - AddTextVertical(&DrawList,ImVec2(pos_x,pos_y),col_text,label,label_end); - } - DrawList.AddRect(bb_grad.Min, bb_grad.Max, GetStyleColorU32(ImPlotCol_PlotBorder)); - ImGui::PopClipRect(); -} - -bool ColormapSlider(const char* label, float* t, ImVec4* out, const char* format, ImPlotColormap cmap) { - *t = ImClamp(*t,0.0f,1.0f); - ImGuiContext &G = *GImGui; - ImGuiWindow * Window = G.CurrentWindow; - if (Window->SkipItems) - return false; - ImPlotContext& gp = *GImPlot; - cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; - IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); - const ImU32* keys = GImPlot->ColormapData.GetKeys(cmap); - const int count = GImPlot->ColormapData.GetKeyCount(cmap); - const bool qual = GImPlot->ColormapData.IsQual(cmap); - const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; - const float w = ImGui::CalcItemWidth(); - const float h = ImGui::GetFrameHeight(); - const ImRect rect = ImRect(pos.x,pos.y,pos.x+w,pos.y+h); - RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual); - const ImU32 grab = CalcTextColor(GImPlot->ColormapData.LerpTable(cmap,*t)); - // const ImU32 text = CalcTextColor(GImPlot->ColormapData.LerpTable(cmap,0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBg,IM_COL32_BLACK_TRANS); - ImGui::PushStyleColor(ImGuiCol_FrameBgActive,IM_COL32_BLACK_TRANS); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered,ImVec4(1,1,1,0.1f)); - ImGui::PushStyleColor(ImGuiCol_SliderGrab,grab); - ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, grab); - ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize,2); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0); - const bool changed = ImGui::SliderFloat(label,t,0,1,format); - ImGui::PopStyleColor(5); - ImGui::PopStyleVar(2); - if (out != NULL) - *out = ImGui::ColorConvertU32ToFloat4(GImPlot->ColormapData.LerpTable(cmap,*t)); - return changed; -} - -bool ColormapButton(const char* label, const ImVec2& size_arg, ImPlotColormap cmap) { - ImGuiContext &G = *GImGui; - const ImGuiStyle& style = G.Style; - ImGuiWindow * Window = G.CurrentWindow; - if (Window->SkipItems) - return false; - ImPlotContext& gp = *GImPlot; - cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; - IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); - const ImU32* keys = GImPlot->ColormapData.GetKeys(cmap); - const int count = GImPlot->ColormapData.GetKeyCount(cmap); - const bool qual = GImPlot->ColormapData.IsQual(cmap); - const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; - const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); - ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); - const ImRect rect = ImRect(pos.x,pos.y,pos.x+size.x,pos.y+size.y); - RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual); - const ImU32 text = CalcTextColor(GImPlot->ColormapData.LerpTable(cmap,G.Style.ButtonTextAlign.x)); - ImGui::PushStyleColor(ImGuiCol_Button,IM_COL32_BLACK_TRANS); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ImVec4(1,1,1,0.1f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive,ImVec4(1,1,1,0.2f)); - ImGui::PushStyleColor(ImGuiCol_Text,text); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0); - const bool pressed = ImGui::Button(label,size); - ImGui::PopStyleColor(4); - ImGui::PopStyleVar(1); - return pressed; -} - -//----------------------------------------------------------------------------- -// [Section] Miscellaneous -//----------------------------------------------------------------------------- - -ImPlotInputMap& GetInputMap() { - IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); - ImPlotContext& gp = *GImPlot; - return gp.InputMap; -} - -void MapInputDefault(ImPlotInputMap* dst) { - ImPlotInputMap& map = dst ? *dst : GetInputMap(); - map.Pan = ImGuiMouseButton_Left; - map.PanMod = ImGuiModFlags_None; - map.Fit = ImGuiMouseButton_Left; - map.Menu = ImGuiMouseButton_Right; - map.Select = ImGuiMouseButton_Right; - map.SelectMod = ImGuiModFlags_None; - map.SelectCancel = ImGuiMouseButton_Left; - map.SelectHorzMod = ImGuiModFlags_Alt; - map.SelectVertMod = ImGuiModFlags_Shift; - map.OverrideMod = ImGuiModFlags_Ctrl; - map.ZoomMod = ImGuiModFlags_None; - map.ZoomRate = 0.1f; -} - -void MapInputReverse(ImPlotInputMap* dst) { - ImPlotInputMap& map = dst ? *dst : GetInputMap(); - map.Pan = ImGuiMouseButton_Right; - map.PanMod = ImGuiModFlags_None; - map.Fit = ImGuiMouseButton_Left; - map.Menu = ImGuiMouseButton_Right; - map.Select = ImGuiMouseButton_Left; - map.SelectMod = ImGuiModFlags_None; - map.SelectCancel = ImGuiMouseButton_Right; - map.SelectHorzMod = ImGuiModFlags_Alt; - map.SelectVertMod = ImGuiModFlags_Shift; - map.OverrideMod = ImGuiModFlags_Ctrl; - map.ZoomMod = ImGuiModFlags_None; - map.ZoomRate = 0.1f; -} - -//----------------------------------------------------------------------------- -// [Section] Miscellaneous -//----------------------------------------------------------------------------- - -void ItemIcon(const ImVec4& col) { - ItemIcon(ImGui::ColorConvertFloat4ToU32(col)); -} - -void ItemIcon(ImU32 col) { - const float txt_size = ImGui::GetTextLineHeight(); - ImVec2 size(txt_size-4,txt_size); - ImGuiWindow* window = ImGui::GetCurrentWindow(); - ImVec2 pos = window->DC.CursorPos; - ImGui::GetWindowDrawList()->AddRectFilled(pos + ImVec2(0,2), pos + size - ImVec2(0,2), col); - ImGui::Dummy(size); -} - -void ColormapIcon(ImPlotColormap cmap) { - ImPlotContext& gp = *GImPlot; - const float txt_size = ImGui::GetTextLineHeight(); - ImVec2 size(txt_size-4,txt_size); - ImGuiWindow* window = ImGui::GetCurrentWindow(); - ImVec2 pos = window->DC.CursorPos; - ImRect rect(pos+ImVec2(0,2),pos+size-ImVec2(0,2)); - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - RenderColorBar(gp.ColormapData.GetKeys(cmap),gp.ColormapData.GetKeyCount(cmap),DrawList,rect,false,false,!gp.ColormapData.IsQual(cmap)); - ImGui::Dummy(size); -} - -ImDrawList* GetPlotDrawList() { - return ImGui::GetWindowDrawList(); -} - -void PushPlotClipRect(float expand) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - ImRect rect = gp.CurrentPlot->PlotRect; - rect.Expand(expand); - ImGui::PushClipRect(rect.Min, rect.Max, true); -} - -void PopPlotClipRect() { - SetupLock(); - ImGui::PopClipRect(); -} - -static void HelpMarker(const char* desc) { - ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(desc); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } -} - -bool ShowStyleSelector(const char* label) -{ - static int style_idx = -1; - if (ImGui::Combo(label, &style_idx, "Auto\0Classic\0Dark\0Light\0")) - { - switch (style_idx) - { - case 0: StyleColorsAuto(); break; - case 1: StyleColorsClassic(); break; - case 2: StyleColorsDark(); break; - case 3: StyleColorsLight(); break; - } - return true; - } - return false; -} - -bool ShowColormapSelector(const char* label) { - ImPlotContext& gp = *GImPlot; - bool set = false; - if (ImGui::BeginCombo(label, gp.ColormapData.GetName(gp.Style.Colormap))) { - for (int i = 0; i < gp.ColormapData.Count; ++i) { - const char* name = gp.ColormapData.GetName(i); - if (ImGui::Selectable(name, gp.Style.Colormap == i)) { - gp.Style.Colormap = i; - ImPlot::BustItemCache(); - set = true; - } - } - ImGui::EndCombo(); - } - return set; -} - -bool ShowInputMapSelector(const char* label) { - static int map_idx = -1; - if (ImGui::Combo(label, &map_idx, "Default\0Reversed\0")) - { - switch (map_idx) - { - case 0: MapInputDefault(); break; - case 1: MapInputReverse(); break; - } - return true; - } - return false; -} - - -void ShowStyleEditor(ImPlotStyle* ref) { - ImPlotContext& gp = *GImPlot; - ImPlotStyle& style = GetStyle(); - static ImPlotStyle ref_saved_style; - // Default to using internal storage as reference - static bool init = true; - if (init && ref == NULL) - ref_saved_style = style; - init = false; - if (ref == NULL) - ref = &ref_saved_style; - - if (ImPlot::ShowStyleSelector("Colors##Selector")) - ref_saved_style = style; - - // Save/Revert button - if (ImGui::Button("Save Ref")) - *ref = ref_saved_style = style; - ImGui::SameLine(); - if (ImGui::Button("Revert Ref")) - style = *ref; - ImGui::SameLine(); - HelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. " - "Use \"Export\" below to save them somewhere."); - if (ImGui::BeginTabBar("##StyleEditor")) { - if (ImGui::BeginTabItem("Variables")) { - ImGui::Text("Item Styling"); - ImGui::SliderFloat("LineWeight", &style.LineWeight, 0.0f, 5.0f, "%.1f"); - ImGui::SliderFloat("MarkerSize", &style.MarkerSize, 2.0f, 10.0f, "%.1f"); - ImGui::SliderFloat("MarkerWeight", &style.MarkerWeight, 0.0f, 5.0f, "%.1f"); - ImGui::SliderFloat("FillAlpha", &style.FillAlpha, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat("ErrorBarSize", &style.ErrorBarSize, 0.0f, 10.0f, "%.1f"); - ImGui::SliderFloat("ErrorBarWeight", &style.ErrorBarWeight, 0.0f, 5.0f, "%.1f"); - ImGui::SliderFloat("DigitalBitHeight", &style.DigitalBitHeight, 0.0f, 20.0f, "%.1f"); - ImGui::SliderFloat("DigitalBitGap", &style.DigitalBitGap, 0.0f, 20.0f, "%.1f"); - ImGui::Text("Plot Styling"); - ImGui::SliderFloat("PlotBorderSize", &style.PlotBorderSize, 0.0f, 2.0f, "%.0f"); - ImGui::SliderFloat("MinorAlpha", &style.MinorAlpha, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat2("MajorTickLen", (float*)&style.MajorTickLen, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("MinorTickLen", (float*)&style.MinorTickLen, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("MajorTickSize", (float*)&style.MajorTickSize, 0.0f, 2.0f, "%.1f"); - ImGui::SliderFloat2("MinorTickSize", (float*)&style.MinorTickSize, 0.0f, 2.0f, "%.1f"); - ImGui::SliderFloat2("MajorGridSize", (float*)&style.MajorGridSize, 0.0f, 2.0f, "%.1f"); - ImGui::SliderFloat2("MinorGridSize", (float*)&style.MinorGridSize, 0.0f, 2.0f, "%.1f"); - ImGui::SliderFloat2("PlotDefaultSize", (float*)&style.PlotDefaultSize, 0.0f, 1000, "%.0f"); - ImGui::SliderFloat2("PlotMinSize", (float*)&style.PlotMinSize, 0.0f, 300, "%.0f"); - ImGui::Text("Plot Padding"); - ImGui::SliderFloat2("PlotPadding", (float*)&style.PlotPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("LabelPadding", (float*)&style.LabelPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("LegendPadding", (float*)&style.LegendPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("LegendInnerPadding", (float*)&style.LegendInnerPadding, 0.0f, 10.0f, "%.0f"); - ImGui::SliderFloat2("LegendSpacing", (float*)&style.LegendSpacing, 0.0f, 5.0f, "%.0f"); - ImGui::SliderFloat2("MousePosPadding", (float*)&style.MousePosPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("AnnotationPadding", (float*)&style.AnnotationPadding, 0.0f, 5.0f, "%.0f"); - ImGui::SliderFloat2("FitPadding", (float*)&style.FitPadding, 0, 0.2f, "%.2f"); - - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Colors")) { - static int output_dest = 0; - static bool output_only_modified = false; - - if (ImGui::Button("Export", ImVec2(75,0))) { - if (output_dest == 0) - ImGui::LogToClipboard(); - else - ImGui::LogToTTY(); - ImGui::LogText("ImVec4* colors = ImPlot::GetStyle().Colors;\n"); - for (int i = 0; i < ImPlotCol_COUNT; i++) { - const ImVec4& col = style.Colors[i]; - const char* name = ImPlot::GetStyleColorName(i); - if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) { - if (IsColorAuto(i)) - ImGui::LogText("colors[ImPlotCol_%s]%*s= IMPLOT_AUTO_COL;\n",name,14 - (int)strlen(name), ""); - else - ImGui::LogText("colors[ImPlotCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\n", - name, 14 - (int)strlen(name), "", col.x, col.y, col.z, col.w); - } - } - ImGui::LogFinish(); - } - ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); - ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); - - static ImGuiTextFilter filter; - filter.Draw("Filter colors", ImGui::GetFontSize() * 16); - - static ImGuiColorEditFlags alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; - if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); - if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine(); - if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); - HelpMarker( - "In the color list:\n" - "Left-click on colored square to open color picker,\n" - "Right-click to open edit options menu."); - ImGui::Separator(); - ImGui::PushItemWidth(-160); - for (int i = 0; i < ImPlotCol_COUNT; i++) { - const char* name = ImPlot::GetStyleColorName(i); - if (!filter.PassFilter(name)) - continue; - ImGui::PushID(i); - ImVec4 temp = GetStyleColorVec4(i); - const bool is_auto = IsColorAuto(i); - if (!is_auto) - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); - if (ImGui::Button("Auto")) { - if (is_auto) - style.Colors[i] = temp; - else - style.Colors[i] = IMPLOT_AUTO_COL; - BustItemCache(); - } - if (!is_auto) - ImGui::PopStyleVar(); - ImGui::SameLine(); - if (ImGui::ColorEdit4(name, &temp.x, ImGuiColorEditFlags_NoInputs | alpha_flags)) { - style.Colors[i] = temp; - BustItemCache(); - } - if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) { - ImGui::SameLine(175); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; } - ImGui::SameLine(); if (ImGui::Button("Revert")) { - style.Colors[i] = ref->Colors[i]; - BustItemCache(); - } - } - ImGui::PopID(); - } - ImGui::PopItemWidth(); - ImGui::Separator(); - ImGui::Text("Colors that are set to Auto (i.e. IMPLOT_AUTO_COL) will\n" - "be automatically deduced from your ImGui style or the\n" - "current ImPlot Colormap. If you want to style individual\n" - "plot items, use Push/PopStyleColor around its function."); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Colormaps")) { - static int output_dest = 0; - if (ImGui::Button("Export", ImVec2(75,0))) { - if (output_dest == 0) - ImGui::LogToClipboard(); - else - ImGui::LogToTTY(); - int size = GetColormapSize(); - const char* name = GetColormapName(gp.Style.Colormap); - ImGui::LogText("static const ImU32 %s_Data[%d] = {\n", name, size); - for (int i = 0; i < size; ++i) { - ImU32 col = GetColormapColorU32(i,gp.Style.Colormap); - ImGui::LogText(" %u%s\n", col, i == size - 1 ? "" : ","); - } - ImGui::LogText("};\nImPlotColormap %s = ImPlot::AddColormap(\"%s\", %s_Data, %d);", name, name, name, size); - ImGui::LogFinish(); - } - ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); - ImGui::SameLine(); - static bool edit = false; - ImGui::Checkbox("Edit Mode",&edit); - - // built-in/added - ImGui::Separator(); - for (int i = 0; i < gp.ColormapData.Count; ++i) { - ImGui::PushID(i); - int size = gp.ColormapData.GetKeyCount(i); - bool selected = i == gp.Style.Colormap; - - const char* name = GetColormapName(i); - if (!selected) - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); - if (ImGui::Button(name, ImVec2(100,0))) { - gp.Style.Colormap = i; - BustItemCache(); - } - if (!selected) - ImGui::PopStyleVar(); - ImGui::SameLine(); - ImGui::BeginGroup(); - if (edit) { - for (int c = 0; c < size; ++c) { - ImGui::PushID(c); - ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i,c)); - if (ImGui::ColorEdit4("",&col4.x,ImGuiColorEditFlags_NoInputs)) { - ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4); - gp.ColormapData.SetKeyColor(i,c,col32); - BustItemCache(); - } - if ((c + 1) % 12 != 0 && c != size -1) - ImGui::SameLine(); - ImGui::PopID(); - } - } - else { - if (ImPlot::ColormapButton("##",ImVec2(-1,0),i)) - edit = true; - } - ImGui::EndGroup(); - ImGui::PopID(); - } - - - static ImVector custom; - if (custom.Size == 0) { - custom.push_back(ImVec4(1,0,0,1)); - custom.push_back(ImVec4(0,1,0,1)); - custom.push_back(ImVec4(0,0,1,1)); - } - ImGui::Separator(); - ImGui::BeginGroup(); - static char name[16] = "MyColormap"; - - - if (ImGui::Button("+", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0))) - custom.push_back(ImVec4(0,0,0,1)); - ImGui::SameLine(); - if (ImGui::Button("-", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2) - custom.pop_back(); - ImGui::SetNextItemWidth(100); - ImGui::InputText("##Name",name,16,ImGuiInputTextFlags_CharsNoBlank); - static bool qual = true; - ImGui::Checkbox("Qualitative",&qual); - if (ImGui::Button("Add", ImVec2(100, 0)) && gp.ColormapData.GetIndex(name)==-1) - AddColormap(name,custom.Data,custom.Size,qual); - - ImGui::EndGroup(); - ImGui::SameLine(); - ImGui::BeginGroup(); - for (int c = 0; c < custom.Size; ++c) { - ImGui::PushID(c); - if (ImGui::ColorEdit4("##Col1", &custom[c].x, ImGuiColorEditFlags_NoInputs)) { - - } - if ((c + 1) % 12 != 0) - ImGui::SameLine(); - ImGui::PopID(); - } - ImGui::EndGroup(); - - - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } -} - -void ShowUserGuide() { - ImGui::BulletText("Left-click drag within the plot area to pan X and Y axes."); - ImGui::Indent(); - ImGui::BulletText("Left-click drag on axis labels to pan an individual axis."); - ImGui::Unindent(); - ImGui::BulletText("Scroll in the plot area to zoom both X any Y axes."); - ImGui::Indent(); - ImGui::BulletText("Scroll on axis labels to zoom an individual axis."); - ImGui::Unindent(); - ImGui::BulletText("Right-click drag to box select data."); - ImGui::Indent(); - ImGui::BulletText("Hold Alt to expand box selection horizontally."); - ImGui::BulletText("Hold Shift to expand box selection vertically."); - ImGui::BulletText("Left-click while box selecting to cancel the selection."); - ImGui::Unindent(); - ImGui::BulletText("Double left-click to fit all visible data."); - ImGui::Indent(); - ImGui::BulletText("Double left-click axis labels to fit the individual axis."); - ImGui::Unindent(); - ImGui::BulletText("Right-click open the full plot context menu."); - ImGui::Indent(); - ImGui::BulletText("Right-click axis labels to open an individual axis context menu."); - ImGui::Unindent(); - ImGui::BulletText("Click legend label icons to show/hide plot items."); -} - -void ShowTicksMetrics(const ImPlotTicker& ticker) { - ImGui::BulletText("Size: %d", ticker.TickCount()); - ImGui::BulletText("MaxSize: [%f,%f]", ticker.MaxSize.x, ticker.MaxSize.y); -} - -void ShowAxisMetrics(const ImPlotPlot& plot, const ImPlotAxis& axis) { - ImGui::BulletText("Label: %s", axis.LabelOffset == -1 ? "[none]" : plot.GetAxisLabel(axis)); - ImGui::BulletText("Flags: 0x%08X", axis.Flags); - ImGui::BulletText("Range: [%f,%f]",axis.Range.Min, axis.Range.Max); - ImGui::BulletText("Pixels: %f", axis.PixelSize()); - ImGui::BulletText("Aspect: %f", axis.GetAspect()); - ImGui::BulletText(axis.OrthoAxis == NULL ? "OrtherAxis: NULL" : "OrthoAxis: 0x%08X", axis.OrthoAxis->ID); - ImGui::BulletText("LinkedMin: %p", (void*)axis.LinkedMin); - ImGui::BulletText("LinkedMax: %p", (void*)axis.LinkedMax); - ImGui::BulletText("HasRange: %s", axis.HasRange ? "true" : "false"); - ImGui::BulletText("Hovered: %s", axis.Hovered ? "true" : "false"); - ImGui::BulletText("Held: %s", axis.Held ? "true" : "false"); - - if (ImGui::TreeNode("Transform")) { - ImGui::BulletText("PixelMin: %f", axis.PixelMin); - ImGui::BulletText("PixelMax: %f", axis.PixelMax); - ImGui::BulletText("ScaleToPixel: %f", axis.ScaleToPixel); - ImGui::BulletText("ScaleMax: %f", axis.ScaleMax); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Ticks")) { - ShowTicksMetrics(axis.Ticker); - ImGui::TreePop(); - } -} - -void ShowMetricsWindow(bool* p_popen) { - - static bool show_plot_rects = false; - static bool show_axes_rects = false; - static bool show_axis_rects = false; - static bool show_canvas_rects = false; - static bool show_frame_rects = false; - static bool show_subplot_frame_rects = false; - static bool show_subplot_grid_rects = false; - - ImDrawList& fg = *ImGui::GetForegroundDrawList(); - - ImPlotContext& gp = *GImPlot; - // ImGuiContext& g = *GImGui; - ImGuiIO& io = ImGui::GetIO(); - ImGui::Begin("ImPlot Metrics", p_popen); - ImGui::Text("ImPlot " IMPLOT_VERSION); - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); - ImGui::Text("Mouse Position: [%.0f,%.0f]", io.MousePos.x, io.MousePos.y); - ImGui::Separator(); - if (ImGui::TreeNode("Tools")) { - if (ImGui::Button("Bust Plot Cache")) - BustPlotCache(); - ImGui::SameLine(); - if (ImGui::Button("Bust Item Cache")) - BustItemCache(); - ImGui::Checkbox("Show Frame Rects", &show_frame_rects); - ImGui::Checkbox("Show Canvas Rects",&show_canvas_rects); - ImGui::Checkbox("Show Plot Rects", &show_plot_rects); - ImGui::Checkbox("Show Axes Rects", &show_axes_rects); - ImGui::Checkbox("Show Axis Rects", &show_axis_rects); - ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects); - ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects); - ImGui::TreePop(); - } - const int n_plots = gp.Plots.GetBufSize(); - const int n_subplots = gp.Subplots.GetBufSize(); - // render rects - for (int p = 0; p < n_plots; ++p) { - ImPlotPlot* plot = gp.Plots.GetByIndex(p); - if (show_frame_rects) - fg.AddRect(plot->FrameRect.Min, plot->FrameRect.Max, IM_COL32(255,0,255,255)); - if (show_canvas_rects) - fg.AddRect(plot->CanvasRect.Min, plot->CanvasRect.Max, IM_COL32(0,255,255,255)); - if (show_plot_rects) - fg.AddRect(plot->PlotRect.Min, plot->PlotRect.Max, IM_COL32(255,255,0,255)); - if (show_axes_rects) - fg.AddRect(plot->AxesRect.Min, plot->AxesRect.Max, IM_COL32(0,255,128,255)); - if (show_axis_rects) { - for (int i = 0; i < ImAxis_COUNT; ++i) { - if (plot->Axes[i].Enabled) - fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255)); - } - } - } - for (int p = 0; p < n_subplots; ++p) { - ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p); - if (show_subplot_frame_rects) - fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255)); - if (show_subplot_grid_rects) - fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255)); - } - if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) { - for (int p = 0; p < n_plots; ++p) { - // plot - ImPlotPlot& plot = *gp.Plots.GetByIndex(p); - ImGui::PushID(p); - if (ImGui::TreeNode("Plot", "Plot [0x%08X]", plot.ID)) { - int n_items = plot.Items.GetItemCount(); - if (ImGui::TreeNode("Items", "Items (%d)", n_items)) { - for (int i = 0; i < n_items; ++i) { - ImPlotItem* item = plot.Items.GetItemByIndex(i); - ImGui::PushID(i); - if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) { - ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); - ImGui::Bullet(); - ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); - if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) - item->Color = ImGui::ColorConvertFloat4ToU32(temp); - - ImGui::BulletText("NameOffset: %d",item->NameOffset); - ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A"); - ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false"); - ImGui::TreePop(); - } - ImGui::PopID(); - } - ImGui::TreePop(); - } - char buff[16]; - for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { - ImFormatString(buff,16,"X-Axis %d", i+1); - if (plot.XAxis(i).Enabled && ImGui::TreeNode(buff, "X-Axis %d [0x%08X]", i+1, plot.XAxis(i).ID)) { - ShowAxisMetrics(plot, plot.XAxis(i)); - ImGui::TreePop(); - } - } - for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { - ImFormatString(buff,16,"Y-Axis %d", i+1); - if (plot.YAxis(i).Enabled && ImGui::TreeNode(buff, "Y-Axis %d [0x%08X]", i+1, plot.YAxis(i).ID)) { - ShowAxisMetrics(plot, plot.YAxis(i)); - ImGui::TreePop(); - } - } - ImGui::BulletText("Title: %s", plot.HasTitle() ? plot.GetTitle() : "none"); - ImGui::BulletText("Flags: 0x%08X", plot.Flags); - ImGui::BulletText("Initialized: %s", plot.Initialized ? "true" : "false"); - ImGui::BulletText("Selecting: %s", plot.Selecting ? "true" : "false"); - ImGui::BulletText("Selected: %s", plot.Selected ? "true" : "false"); - ImGui::BulletText("Hovered: %s", plot.Hovered ? "true" : "false"); - ImGui::BulletText("Held: %s", plot.Held ? "true" : "false"); - ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false"); - ImGui::BulletText("ContextLocked: %s", plot.ContextLocked ? "true" : "false"); - ImGui::TreePop(); - } - ImGui::PopID(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Subplots","Subplots (%d)", n_subplots)) { - for (int p = 0; p < n_subplots; ++p) { - // plot - ImPlotSubplot& plot = *gp.Subplots.GetByIndex(p); - ImGui::PushID(p); - if (ImGui::TreeNode("Subplot", "Subplot [0x%08X]", plot.ID)) { - int n_items = plot.Items.GetItemCount(); - if (ImGui::TreeNode("Items", "Items (%d)", n_items)) { - for (int i = 0; i < n_items; ++i) { - ImPlotItem* item = plot.Items.GetItemByIndex(i); - ImGui::PushID(i); - if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) { - ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); - ImGui::Bullet(); - ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); - if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) - item->Color = ImGui::ColorConvertFloat4ToU32(temp); - - ImGui::BulletText("NameOffset: %d",item->NameOffset); - ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A"); - ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false"); - ImGui::TreePop(); - } - ImGui::PopID(); - } - ImGui::TreePop(); - } - ImGui::BulletText("Flags: 0x%08X", plot.Flags); - ImGui::BulletText("FrameHovered: %s", plot.FrameHovered ? "true" : "false"); - ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false"); - ImGui::TreePop(); - } - ImGui::PopID(); - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Colormaps")) { - ImGui::BulletText("Colormaps: %d", gp.ColormapData.Count); - ImGui::BulletText("Memory: %d bytes", gp.ColormapData.Tables.Size * 4); - if (ImGui::TreeNode("Data")) { - for (int m = 0; m < gp.ColormapData.Count; ++m) { - if (ImGui::TreeNode(gp.ColormapData.GetName(m))) { - int count = gp.ColormapData.GetKeyCount(m); - int size = gp.ColormapData.GetTableSize(m); - bool qual = gp.ColormapData.IsQual(m); - ImGui::BulletText("Qualitative: %s", qual ? "true" : "false"); - ImGui::BulletText("Key Count: %d", count); - ImGui::BulletText("Table Size: %d", size); - ImGui::Indent(); - - static float t = 0.5; - ImVec4 samp; - float wid = 32 * 10 - ImGui::GetFrameHeight() - ImGui::GetStyle().ItemSpacing.x; - ImGui::SetNextItemWidth(wid); - ImPlot::ColormapSlider("##Sample",&t,&samp,"%.3f",m); - ImGui::SameLine(); - ImGui::ColorButton("Sampler",samp); - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); - for (int c = 0; c < size; ++c) { - ImVec4 col = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetTableColor(m,c)); - ImGui::PushID(m*1000+c); - ImGui::ColorButton("",col,0,ImVec2(10,10)); - ImGui::PopID(); - if ((c + 1) % 32 != 0 && c != size - 1) - ImGui::SameLine(); - } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - ImGui::Unindent(); - ImGui::TreePop(); - } - } - ImGui::TreePop(); - } - ImGui::TreePop(); - } - ImGui::End(); -} - -bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1, const ImPlotTime* t2) { - - ImGui::PushID(id); - ImGui::BeginGroup(); - - ImGuiStyle& style = ImGui::GetStyle(); - ImVec4 col_txt = style.Colors[ImGuiCol_Text]; - ImVec4 col_dis = style.Colors[ImGuiCol_TextDisabled]; - ImVec4 col_btn = style.Colors[ImGuiCol_Button]; - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); - - const float ht = ImGui::GetFrameHeight(); - ImVec2 cell_size(ht*1.25f,ht); - char buff[32]; - bool clk = false; - tm& Tm = GImPlot->Tm; - - const int min_yr = 1970; - const int max_yr = 2999; - - // t1 parts - int t1_mo = 0; int t1_md = 0; int t1_yr = 0; - if (t1 != NULL) { - GetTime(*t1,&Tm); - t1_mo = Tm.tm_mon; - t1_md = Tm.tm_mday; - t1_yr = Tm.tm_year + 1900; - } - - // t2 parts - int t2_mo = 0; int t2_md = 0; int t2_yr = 0; - if (t2 != NULL) { - GetTime(*t2,&Tm); - t2_mo = Tm.tm_mon; - t2_md = Tm.tm_mday; - t2_yr = Tm.tm_year + 1900; - } - - // day widget - if (*level == 0) { - *t = FloorTime(*t, ImPlotTimeUnit_Day); - GetTime(*t, &Tm); - const int this_year = Tm.tm_year + 1900; - const int last_year = this_year - 1; - const int next_year = this_year + 1; - const int this_mon = Tm.tm_mon; - const int last_mon = this_mon == 0 ? 11 : this_mon - 1; - const int next_mon = this_mon == 11 ? 0 : this_mon + 1; - const int days_this_mo = GetDaysInMonth(this_year, this_mon); - const int days_last_mo = GetDaysInMonth(this_mon == 0 ? last_year : this_year, last_mon); - ImPlotTime t_first_mo = FloorTime(*t,ImPlotTimeUnit_Mo); - GetTime(t_first_mo,&Tm); - const int first_wd = Tm.tm_wday; - // month year - ImFormatString(buff, 32, "%s %d", MONTH_NAMES[this_mon], this_year); - if (ImGui::Button(buff)) - *level = 1; - ImGui::SameLine(5*cell_size.x); - BeginDisabledControls(this_year <= min_yr && this_mon == 0); - if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) - *t = AddTime(*t, ImPlotTimeUnit_Mo, -1); - EndDisabledControls(this_year <= min_yr && this_mon == 0); - ImGui::SameLine(); - BeginDisabledControls(this_year >= max_yr && this_mon == 11); - if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) - *t = AddTime(*t, ImPlotTimeUnit_Mo, 1); - EndDisabledControls(this_year >= max_yr && this_mon == 11); - // render weekday abbreviations - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - for (int i = 0; i < 7; ++i) { - ImGui::Button(WD_ABRVS[i],cell_size); - if (i != 6) { ImGui::SameLine(); } - } - ImGui::PopItemFlag(); - // 0 = last mo, 1 = this mo, 2 = next mo - int mo = first_wd > 0 ? 0 : 1; - int day = mo == 1 ? 1 : days_last_mo - first_wd + 1; - for (int i = 0; i < 6; ++i) { - for (int j = 0; j < 7; ++j) { - if (mo == 0 && day > days_last_mo) { - mo = 1; - day = 1; - } - else if (mo == 1 && day > days_this_mo) { - mo = 2; - day = 1; - } - const int now_yr = (mo == 0 && this_mon == 0) ? last_year : ((mo == 2 && this_mon == 11) ? next_year : this_year); - const int now_mo = mo == 0 ? last_mon : (mo == 1 ? this_mon : next_mon); - const int now_md = day; - - const bool off_mo = mo == 0 || mo == 2; - const bool t1_or_t2 = (t1 != NULL && t1_mo == now_mo && t1_yr == now_yr && t1_md == now_md) || - (t2 != NULL && t2_mo == now_mo && t2_yr == now_yr && t2_md == now_md); - - if (off_mo) - ImGui::PushStyleColor(ImGuiCol_Text, col_dis); - if (t1_or_t2) { - ImGui::PushStyleColor(ImGuiCol_Button, col_btn); - ImGui::PushStyleColor(ImGuiCol_Text, col_txt); - } - ImGui::PushID(i*7+j); - ImFormatString(buff,32,"%d",day); - if (now_yr == min_yr-1 || now_yr == max_yr+1) { - ImGui::Dummy(cell_size); - } - else if (ImGui::Button(buff,cell_size) && !clk) { - *t = MakeTime(now_yr, now_mo, now_md); - clk = true; - } - ImGui::PopID(); - if (t1_or_t2) - ImGui::PopStyleColor(2); - if (off_mo) - ImGui::PopStyleColor(); - if (j != 6) - ImGui::SameLine(); - day++; - } - } - } - // month widget - else if (*level == 1) { - *t = FloorTime(*t, ImPlotTimeUnit_Mo); - GetTime(*t, &Tm); - int this_yr = Tm.tm_year + 1900; - ImFormatString(buff, 32, "%d", this_yr); - if (ImGui::Button(buff)) - *level = 2; - BeginDisabledControls(this_yr <= min_yr); - ImGui::SameLine(5*cell_size.x); - if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) - *t = AddTime(*t, ImPlotTimeUnit_Yr, -1); - EndDisabledControls(this_yr <= min_yr); - ImGui::SameLine(); - BeginDisabledControls(this_yr >= max_yr); - if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) - *t = AddTime(*t, ImPlotTimeUnit_Yr, 1); - EndDisabledControls(this_yr >= max_yr); - // ImGui::Dummy(cell_size); - cell_size.x *= 7.0f/4.0f; - cell_size.y *= 7.0f/3.0f; - int mo = 0; - for (int i = 0; i < 3; ++i) { - for (int j = 0; j < 4; ++j) { - const bool t1_or_t2 = (t1 != NULL && t1_yr == this_yr && t1_mo == mo) || - (t2 != NULL && t2_yr == this_yr && t2_mo == mo); - if (t1_or_t2) - ImGui::PushStyleColor(ImGuiCol_Button, col_btn); - if (ImGui::Button(MONTH_ABRVS[mo],cell_size) && !clk) { - *t = MakeTime(this_yr, mo); - *level = 0; - } - if (t1_or_t2) - ImGui::PopStyleColor(); - if (j != 3) - ImGui::SameLine(); - mo++; - } - } - } - else if (*level == 2) { - *t = FloorTime(*t, ImPlotTimeUnit_Yr); - int this_yr = GetYear(*t); - int yr = this_yr - this_yr % 20; - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImFormatString(buff,32,"%d-%d",yr,yr+19); - ImGui::Button(buff); - ImGui::PopItemFlag(); - ImGui::SameLine(5*cell_size.x); - BeginDisabledControls(yr <= min_yr); - if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) - *t = MakeTime(yr-20); - EndDisabledControls(yr <= min_yr); - ImGui::SameLine(); - BeginDisabledControls(yr + 20 >= max_yr); - if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) - *t = MakeTime(yr+20); - EndDisabledControls(yr+ 20 >= max_yr); - // ImGui::Dummy(cell_size); - cell_size.x *= 7.0f/4.0f; - cell_size.y *= 7.0f/5.0f; - for (int i = 0; i < 5; ++i) { - for (int j = 0; j < 4; ++j) { - const bool t1_or_t2 = (t1 != NULL && t1_yr == yr) || (t2 != NULL && t2_yr == yr); - if (t1_or_t2) - ImGui::PushStyleColor(ImGuiCol_Button, col_btn); - ImFormatString(buff,32,"%d",yr); - if (yr<1970||yr>3000) { - ImGui::Dummy(cell_size); - } - else if (ImGui::Button(buff,cell_size)) { - *t = MakeTime(yr); - *level = 1; - } - if (t1_or_t2) - ImGui::PopStyleColor(); - if (j != 3) - ImGui::SameLine(); - yr++; - } - } - } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - ImGui::EndGroup(); - ImGui::PopID(); - return clk; -} - -bool ShowTimePicker(const char* id, ImPlotTime* t) { - ImGui::PushID(id); - tm& Tm = GImPlot->Tm; - GetTime(*t,&Tm); - - static const char* nums[] = { "00","01","02","03","04","05","06","07","08","09", - "10","11","12","13","14","15","16","17","18","19", - "20","21","22","23","24","25","26","27","28","29", - "30","31","32","33","34","35","36","37","38","39", - "40","41","42","43","44","45","46","47","48","49", - "50","51","52","53","54","55","56","57","58","59"}; - - static const char* am_pm[] = {"am","pm"}; - - bool hour24 = GImPlot->Style.Use24HourClock; - - int hr = hour24 ? Tm.tm_hour : ((Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12); - int min = Tm.tm_min; - int sec = Tm.tm_sec; - int ap = Tm.tm_hour < 12 ? 0 : 1; - - bool changed = false; - - ImVec2 spacing = ImGui::GetStyle().ItemSpacing; - spacing.x = 0; - float width = ImGui::CalcTextSize("888").x; - float height = ImGui::GetFrameHeight(); - - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, spacing); - ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize,2.0f); - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered)); - - ImGui::SetNextItemWidth(width); - if (ImGui::BeginCombo("##hr",nums[hr],ImGuiComboFlags_NoArrowButton)) { - const int ia = hour24 ? 0 : 1; - const int ib = hour24 ? 24 : 13; - for (int i = ia; i < ib; ++i) { - if (ImGui::Selectable(nums[i],i==hr)) { - hr = i; - changed = true; - } - } - ImGui::EndCombo(); - } - ImGui::SameLine(); - ImGui::Text(":"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(width); - if (ImGui::BeginCombo("##min",nums[min],ImGuiComboFlags_NoArrowButton)) { - for (int i = 0; i < 60; ++i) { - if (ImGui::Selectable(nums[i],i==min)) { - min = i; - changed = true; - } - } - ImGui::EndCombo(); - } - ImGui::SameLine(); - ImGui::Text(":"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(width); - if (ImGui::BeginCombo("##sec",nums[sec],ImGuiComboFlags_NoArrowButton)) { - for (int i = 0; i < 60; ++i) { - if (ImGui::Selectable(nums[i],i==sec)) { - sec = i; - changed = true; - } - } - ImGui::EndCombo(); - } - if (!hour24) { - ImGui::SameLine(); - if (ImGui::Button(am_pm[ap],ImVec2(0,height))) { - ap = 1 - ap; - changed = true; - } - } - - ImGui::PopStyleColor(3); - ImGui::PopStyleVar(2); - ImGui::PopID(); - - if (changed) { - if (!hour24) - hr = hr % 12 + ap * 12; - Tm.tm_hour = hr; - Tm.tm_min = min; - Tm.tm_sec = sec; - *t = MkTime(&Tm); - } - - return changed; -} - -void StyleColorsAuto(ImPlotStyle* dst) { - ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); - ImVec4* colors = style->Colors; - - style->MinorAlpha = 0.25f; - - colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; - colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; - colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; - colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; - colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; - colors[ImPlotCol_FrameBg] = IMPLOT_AUTO_COL; - colors[ImPlotCol_PlotBg] = IMPLOT_AUTO_COL; - colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL; - colors[ImPlotCol_LegendBg] = IMPLOT_AUTO_COL; - colors[ImPlotCol_LegendBorder] = IMPLOT_AUTO_COL; - colors[ImPlotCol_LegendText] = IMPLOT_AUTO_COL; - colors[ImPlotCol_TitleText] = IMPLOT_AUTO_COL; - colors[ImPlotCol_InlayText] = IMPLOT_AUTO_COL; - colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL; - colors[ImPlotCol_AxisText] = IMPLOT_AUTO_COL; - colors[ImPlotCol_AxisGrid] = IMPLOT_AUTO_COL; - colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; - colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; - colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; - colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; - colors[ImPlotCol_Selection] = IMPLOT_AUTO_COL; - colors[ImPlotCol_Crosshairs] = IMPLOT_AUTO_COL; -} - -void StyleColorsClassic(ImPlotStyle* dst) { - ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); - ImVec4* colors = style->Colors; - - style->MinorAlpha = 0.5f; - - colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; - colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; - colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; - colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; - colors[ImPlotCol_ErrorBar] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImPlotCol_FrameBg] = ImVec4(0.43f, 0.43f, 0.43f, 0.39f); - colors[ImPlotCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.35f); - colors[ImPlotCol_PlotBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); - colors[ImPlotCol_LegendBg] = ImVec4(0.11f, 0.11f, 0.14f, 0.92f); - colors[ImPlotCol_LegendBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); - colors[ImPlotCol_LegendText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImPlotCol_TitleText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImPlotCol_InlayText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImPlotCol_AxisText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImPlotCol_AxisGrid] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f); - colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_Selection] = ImVec4(0.97f, 0.97f, 0.39f, 1.00f); - colors[ImPlotCol_Crosshairs] = ImVec4(0.50f, 0.50f, 0.50f, 0.75f); -} - -void StyleColorsDark(ImPlotStyle* dst) { - ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); - ImVec4* colors = style->Colors; - - style->MinorAlpha = 0.25f; - - colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; - colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; - colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; - colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; - colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; - colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f); - colors[ImPlotCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); - colors[ImPlotCol_PlotBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); - colors[ImPlotCol_LegendBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); - colors[ImPlotCol_LegendBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); - colors[ImPlotCol_LegendText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_TitleText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_InlayText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_AxisText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f); - colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_Selection] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); - colors[ImPlotCol_Crosshairs] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f); -} - -void StyleColorsLight(ImPlotStyle* dst) { - ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); - ImVec4* colors = style->Colors; - - style->MinorAlpha = 1.0f; - - colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; - colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; - colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; - colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; - colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; - colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_PlotBg] = ImVec4(0.42f, 0.57f, 1.00f, 0.13f); - colors[ImPlotCol_PlotBorder] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImPlotCol_LegendBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f); - colors[ImPlotCol_LegendBorder] = ImVec4(0.82f, 0.82f, 0.82f, 0.80f); - colors[ImPlotCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_AxisTick] = ImVec4(0.00f, 0.00f, 0.00f, 0.25f); - colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO - colors[ImPlotCol_Selection] = ImVec4(0.82f, 0.64f, 0.03f, 1.00f); - colors[ImPlotCol_Crosshairs] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); -} - -//----------------------------------------------------------------------------- -// [SECTION] Obsolete Functions/Types -//----------------------------------------------------------------------------- - -#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS - -bool BeginPlot(const char* title, const char* x_label, const char* y1_label, const ImVec2& size, - ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags, - const char* y2_label, const char* y3_label) -{ - if (!BeginPlot(title, size, flags)) - return false; - SetupAxis(ImAxis_X1, x_label, x_flags); - SetupAxis(ImAxis_Y1, y1_label, y1_flags); - if (ImHasFlag(flags, ImPlotFlags_YAxis2)) - SetupAxis(ImAxis_Y2, y2_label, y2_flags); - if (ImHasFlag(flags, ImPlotFlags_YAxis3)) - SetupAxis(ImAxis_Y3, y3_label, y3_flags); - return true; -} - -#endif - -} // namespace ImPlot diff --git a/libs/zgui/libs/imgui/implot.h b/libs/zgui/libs/imgui/implot.h deleted file mode 100644 index f4174eb2e..000000000 --- a/libs/zgui/libs/imgui/implot.h +++ /dev/null @@ -1,1285 +0,0 @@ -// MIT License - -// Copyright (c) 2022 Evan Pezent - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// ImPlot v0.14 - -// Table of Contents: -// -// [SECTION] Macros and Defines -// [SECTION] Enums and Types -// [SECTION] Callbacks -// [SECTION] Contexts -// [SECTION] Begin/End Plot -// [SECTION] Begin/End Subplot -// [SECTION] Setup -// [SECTION] SetNext -// [SECTION] Plot Items -// [SECTION] Plot Tools -// [SECTION] Plot Utils -// [SECTION] Legend Utils -// [SECTION] Drag and Drop -// [SECTION] Styling -// [SECTION] Colormaps -// [SECTION] Input Mapping -// [SECTION] Miscellaneous -// [SECTION] Demo -// [SECTION] Obsolete API - -#pragma once -#include "imgui.h" - -//----------------------------------------------------------------------------- -// [SECTION] Macros and Defines -//----------------------------------------------------------------------------- - -// Define attributes of all API symbols declarations (e.g. for DLL under Windows) -// Using ImPlot via a shared library is not recommended, because we don't guarantee -// backward nor forward ABI compatibility and also function call overhead. If you -// do use ImPlot as a DLL, be sure to call SetImGuiContext (see Miscellanous section). -#ifndef IMPLOT_API -#define IMPLOT_API -#endif - -// ImPlot version string. -#define IMPLOT_VERSION "0.14" -// Indicates variable should deduced automatically. -#define IMPLOT_AUTO -1 -// Special color used to indicate that a color should be deduced automatically. -#define IMPLOT_AUTO_COL ImVec4(0,0,0,-1) -// Macro for templated plotting functions; keeps header clean. -#define IMPLOT_TMP template IMPLOT_API - -//----------------------------------------------------------------------------- -// [SECTION] Enums and Types -//----------------------------------------------------------------------------- - -// Forward declarations -struct ImPlotContext; // ImPlot context (opaque struct, see implot_internal.h) - -// Enums/Flags -typedef int ImAxis; // -> enum ImAxis_ -typedef int ImPlotFlags; // -> enum ImPlotFlags_ -typedef int ImPlotAxisFlags; // -> enum ImPlotAxisFlags_ -typedef int ImPlotSubplotFlags; // -> enum ImPlotSubplotFlags_ -typedef int ImPlotLegendFlags; // -> enum ImPlotLegendFlags_ -typedef int ImPlotMouseTextFlags; // -> enum ImPlotMouseTextFlags_ -typedef int ImPlotDragToolFlags; // -> ImPlotDragToolFlags_ -typedef int ImPlotColormapScaleFlags; // -> ImPlotColormapScaleFlags_ - -typedef int ImPlotItemFlags; // -> ImPlotItemFlags_ -typedef int ImPlotLineFlags; // -> ImPlotLineFlags_ -typedef int ImPlotScatterFlags; // -> ImPlotScatterFlags -typedef int ImPlotStairsFlags; // -> ImPlotStairsFlags_ -typedef int ImPlotShadedFlags; // -> ImPlotShadedFlags_ -typedef int ImPlotBarsFlags; // -> ImPlotBarsFlags_ -typedef int ImPlotBarGroupsFlags; // -> ImPlotBarGroupsFlags_ -typedef int ImPlotErrorBarsFlags; // -> ImPlotErrorBarsFlags_ -typedef int ImPlotStemsFlags; // -> ImPlotStemsFlags_ -typedef int ImPlotInfLinesFlags; // -> ImPlotInfLinesFlags_ -typedef int ImPlotPieChartFlags; // -> ImPlotPieChartFlags_ -typedef int ImPlotHeatmapFlags; // -> ImPlotHeatmapFlags_ -typedef int ImPlotHistogramFlags; // -> ImPlotHistogramFlags_ -typedef int ImPlotDigitalFlags; // -> ImPlotDigitalFlags_ -typedef int ImPlotImageFlags; // -> ImPlotImageFlags_ -typedef int ImPlotTextFlags; // -> ImPlotTextFlags_ -typedef int ImPlotDummyFlags; // -> ImPlotDummyFlags_ - -typedef int ImPlotCond; // -> enum ImPlotCond_ -typedef int ImPlotCol; // -> enum ImPlotCol_ -typedef int ImPlotStyleVar; // -> enum ImPlotStyleVar_ -typedef int ImPlotScale; // -> enum ImPlotScale_ -typedef int ImPlotMarker; // -> enum ImPlotMarker_ -typedef int ImPlotColormap; // -> enum ImPlotColormap_ -typedef int ImPlotLocation; // -> enum ImPlotLocation_ -typedef int ImPlotBin; // -> enum ImPlotBin_ - -// Axis indices. The values assigned may change; NEVER hardcode these. -enum ImAxis_ { - // horizontal axes - ImAxis_X1 = 0, // enabled by default - ImAxis_X2, // disabled by default - ImAxis_X3, // disabled by default - // vertical axes - ImAxis_Y1, // enabled by default - ImAxis_Y2, // disabled by default - ImAxis_Y3, // disabled by default - // bookeeping - ImAxis_COUNT -}; - -// Options for plots (see BeginPlot). -enum ImPlotFlags_ { - ImPlotFlags_None = 0, // default - ImPlotFlags_NoTitle = 1 << 0, // the plot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. "##MyPlot") - ImPlotFlags_NoLegend = 1 << 1, // the legend will not be displayed - ImPlotFlags_NoMouseText = 1 << 2, // the mouse position, in plot coordinates, will not be displayed inside of the plot - ImPlotFlags_NoInputs = 1 << 3, // the user will not be able to interact with the plot - ImPlotFlags_NoMenus = 1 << 4, // the user will not be able to open context menus - ImPlotFlags_NoBoxSelect = 1 << 5, // the user will not be able to box-select - ImPlotFlags_NoChild = 1 << 6, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications) - ImPlotFlags_NoFrame = 1 << 7, // the ImGui frame will not be rendered - ImPlotFlags_Equal = 1 << 8, // x and y axes pairs will be constrained to have the same units/pixel - ImPlotFlags_Crosshairs = 1 << 9, // the default mouse cursor will be replaced with a crosshair when hovered - ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText -}; - -// Options for plot axes (see SetupAxis). -enum ImPlotAxisFlags_ { - ImPlotAxisFlags_None = 0, // default - ImPlotAxisFlags_NoLabel = 1 << 0, // the axis label will not be displayed (axis labels are also hidden if the supplied string name is NULL) - ImPlotAxisFlags_NoGridLines = 1 << 1, // no grid lines will be displayed - ImPlotAxisFlags_NoTickMarks = 1 << 2, // no tick marks will be displayed - ImPlotAxisFlags_NoTickLabels = 1 << 3, // no text labels will be displayed - ImPlotAxisFlags_NoInitialFit = 1 << 4, // axis will not be initially fit to data extents on the first rendered frame - ImPlotAxisFlags_NoMenus = 1 << 5, // the user will not be able to open context menus with right-click - ImPlotAxisFlags_NoSideSwitch = 1 << 6, // the user will not be able to switch the axis side by dragging it - ImPlotAxisFlags_NoHighlight = 1 << 7, // the axis will not have its background highlighted when hovered or held - ImPlotAxisFlags_Opposite = 1 << 8, // axis ticks and labels will be rendered on the conventionally opposite side (i.e, right or top) - ImPlotAxisFlags_Foreground = 1 << 9, // grid lines will be displayed in the foreground (i.e. on top of data) instead of the background - ImPlotAxisFlags_Invert = 1 << 10, // the axis will be inverted - ImPlotAxisFlags_AutoFit = 1 << 11, // axis will be auto-fitting to data extents - ImPlotAxisFlags_RangeFit = 1 << 12, // axis will only fit points if the point is in the visible range of the **orthogonal** axis - ImPlotAxisFlags_PanStretch = 1 << 13, // panning in a locked or constrained state will cause the axis to stretch if possible - ImPlotAxisFlags_LockMin = 1 << 14, // the axis minimum value will be locked when panning/zooming - ImPlotAxisFlags_LockMax = 1 << 15, // the axis maximum value will be locked when panning/zooming - ImPlotAxisFlags_Lock = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax, - ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels, - ImPlotAxisFlags_AuxDefault = ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_Opposite -}; - -// Options for subplots (see BeginSubplot) -enum ImPlotSubplotFlags_ { - ImPlotSubplotFlags_None = 0, // default - ImPlotSubplotFlags_NoTitle = 1 << 0, // the subplot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. "##MySubplot") - ImPlotSubplotFlags_NoLegend = 1 << 1, // the legend will not be displayed (only applicable if ImPlotSubplotFlags_ShareItems is enabled) - ImPlotSubplotFlags_NoMenus = 1 << 2, // the user will not be able to open context menus with right-click - ImPlotSubplotFlags_NoResize = 1 << 3, // resize splitters between subplot cells will be not be provided - ImPlotSubplotFlags_NoAlign = 1 << 4, // subplot edges will not be aligned vertically or horizontally - ImPlotSubplotFlags_ShareItems = 1 << 5, // items across all subplots will be shared and rendered into a single legend entry - ImPlotSubplotFlags_LinkRows = 1 << 6, // link the y-axis limits of all plots in each row (does not apply to auxiliary axes) - ImPlotSubplotFlags_LinkCols = 1 << 7, // link the x-axis limits of all plots in each column (does not apply to auxiliary axes) - ImPlotSubplotFlags_LinkAllX = 1 << 8, // link the x-axis limits in every plot in the subplot (does not apply to auxiliary axes) - ImPlotSubplotFlags_LinkAllY = 1 << 9, // link the y-axis limits in every plot in the subplot (does not apply to auxiliary axes) - ImPlotSubplotFlags_ColMajor = 1 << 10 // subplots are added in column major order instead of the default row major order -}; - -// Options for legends (see SetupLegend) -enum ImPlotLegendFlags_ { - ImPlotLegendFlags_None = 0, // default - ImPlotLegendFlags_NoButtons = 1 << 0, // legend icons will not function as hide/show buttons - ImPlotLegendFlags_NoHighlightItem = 1 << 1, // plot items will not be highlighted when their legend entry is hovered - ImPlotLegendFlags_NoHighlightAxis = 1 << 2, // axes will not be highlighted when legend entries are hovered (only relevant if x/y-axis count > 1) - ImPlotLegendFlags_NoMenus = 1 << 3, // the user will not be able to open context menus with right-click - ImPlotLegendFlags_Outside = 1 << 4, // legend will be rendered outside of the plot area - ImPlotLegendFlags_Horizontal = 1 << 5, // legend entries will be displayed horizontally -}; - -// Options for mouse hover text (see SetupMouseText) -enum ImPlotMouseTextFlags_ { - ImPlotMouseTextFlags_None = 0, // default - ImPlotMouseTextFlags_NoAuxAxes = 1 << 0, // only show the mouse position for primary axes - ImPlotMouseTextFlags_NoFormat = 1 << 1, // axes label formatters won't be used to render text - ImPlotMouseTextFlags_ShowAlways = 1 << 2, // always display mouse position even if plot not hovered -}; - -// Options for DragPoint, DragLine, DragRect -enum ImPlotDragToolFlags_ { - ImPlotDragToolFlags_None = 0, // default - ImPlotDragToolFlags_NoCursors = 1 << 0, // drag tools won't change cursor icons when hovered or held - ImPlotDragToolFlags_NoFit = 1 << 1, // the drag tool won't be considered for plot fits - ImPlotDragToolFlags_NoInputs = 1 << 2, // lock the tool from user inputs - ImPlotDragToolFlags_Delayed = 1 << 3, // tool rendering will be delayed one frame; useful when applying position-constraints -}; - -// Flags for ColormapScale -enum ImPlotColormapScaleFlags_ { - ImPlotColormapScaleFlags_None = 0, // default - ImPlotColormapScaleFlags_NoLabel = 1 << 0, // the colormap axis label will not be displayed - ImPlotColormapScaleFlags_Opposite = 1 << 1, // render the colormap label and tick labels on the opposite side - ImPlotColormapScaleFlags_Invert = 1 << 2, // invert the colormap bar and axis scale (this only affects rendering; if you only want to reverse the scale mapping, make scale_min > scale_max) -}; - -// Flags for ANY PlotX function -enum ImPlotItemFlags_ { - ImPlotItemFlags_None = 0, - ImPlotItemFlags_NoLegend = 1 << 0, // the item won't have a legend entry displayed - ImPlotItemFlags_NoFit = 1 << 1, // the item won't be considered for plot fits -}; - -// Flags for PlotLine -enum ImPlotLineFlags_ { - ImPlotLineFlags_None = 0, // default - ImPlotLineFlags_Segments = 1 << 10, // a line segment will be rendered from every two consecutive points - ImPlotLineFlags_Loop = 1 << 11, // the last and first point will be connected to form a closed loop - ImPlotLineFlags_SkipNaN = 1 << 12, // NaNs values will be skipped instead of rendered as missing data - ImPlotLineFlags_NoClip = 1 << 13, // markers (if displayed) on the edge of a plot will not be clipped - ImPlotLineFlags_Shaded = 1 << 14, // a filled region between the line and horizontal origin will be rendered; use PlotShaded for more advanced cases -}; - -// Flags for PlotScatter -enum ImPlotScatterFlags_ { - ImPlotScatterFlags_None = 0, // default - ImPlotScatterFlags_NoClip = 1 << 10, // markers on the edge of a plot will not be clipped -}; - -// Flags for PlotStairs -enum ImPlotStairsFlags_ { - ImPlotStairsFlags_None = 0, // default - ImPlotStairsFlags_PreStep = 1 << 10, // the y value is continued constantly to the left from every x position, i.e. the interval (x[i-1], x[i]] has the value y[i] - ImPlotStairsFlags_Shaded = 1 << 11 // a filled region between the stairs and horizontal origin will be rendered; use PlotShaded for more advanced cases -}; - -// Flags for PlotShaded (placeholder) -enum ImPlotShadedFlags_ { - ImPlotShadedFlags_None = 0 // default -}; - -// Flags for PlotBars -enum ImPlotBarsFlags_ { - ImPlotBarsFlags_None = 0, // default - ImPlotBarsFlags_Horizontal = 1 << 10, // bars will be rendered horizontally on the current y-axis -}; - -// Flags for PlotBarGroups -enum ImPlotBarGroupsFlags_ { - ImPlotBarGroupsFlags_None = 0, // default - ImPlotBarGroupsFlags_Horizontal = 1 << 10, // bar groups will be rendered horizontally on the current y-axis - ImPlotBarGroupsFlags_Stacked = 1 << 11, // items in a group will be stacked on top of each other -}; - -// Flags for PlotErrorBars -enum ImPlotErrorBarsFlags_ { - ImPlotErrorBarsFlags_None = 0, // default - ImPlotErrorBarsFlags_Horizontal = 1 << 10, // error bars will be rendered horizontally on the current y-axis -}; - -// Flags for PlotStems -enum ImPlotStemsFlags_ { - ImPlotStemsFlags_None = 0, // default - ImPlotStemsFlags_Horizontal = 1 << 10, // stems will be rendered horizontally on the current y-axis -}; - -// Flags for PlotInfLines -enum ImPlotInfLinesFlags_ { - ImPlotInfLinesFlags_None = 0, // default - ImPlotInfLinesFlags_Horizontal = 1 << 10 // lines will be rendered horizontally on the current y-axis -}; - -// Flags for PlotPieChart -enum ImPlotPieChartFlags_ { - ImPlotPieChartFlags_None = 0, // default - ImPlotPieChartFlags_Normalize = 1 << 10 // force normalization of pie chart values (i.e. always make a full circle if sum < 0) -}; - -// Flags for PlotHeatmap -enum ImPlotHeatmapFlags_ { - ImPlotHeatmapFlags_None = 0, // default - ImPlotHeatmapFlags_ColMajor = 1 << 10, // data will be read in column major order -}; - -// Flags for PlotHistogram and PlotHistogram2D -enum ImPlotHistogramFlags_ { - ImPlotHistogramFlags_None = 0, // default - ImPlotHistogramFlags_Horizontal = 1 << 10, // histogram bars will be rendered horizontally (not supported by PlotHistogram2D) - ImPlotHistogramFlags_Cumulative = 1 << 11, // each bin will contain its count plus the counts of all previous bins (not supported by PlotHistogram2D) - ImPlotHistogramFlags_Density = 1 << 12, // counts will be normalized, i.e. the PDF will be visualized, or the CDF will be visualized if Cumulative is also set - ImPlotHistogramFlags_NoOutliers = 1 << 13, // exclude values outside the specifed histogram range from the count toward normalizing and cumulative counts - ImPlotHistogramFlags_ColMajor = 1 << 14 // data will be read in column major order (not supported by PlotHistogram) -}; - -// Flags for PlotDigital (placeholder) -enum ImPlotDigitalFlags_ { - ImPlotDigitalFlags_None = 0 // default -}; - -// Flags for PlotImage (placeholder) -enum ImPlotImageFlags_ { - ImPlotImageFlags_None = 0 // default -}; - -// Flags for PlotText -enum ImPlotTextFlags_ { - ImPlotTextFlags_None = 0, // default - ImPlotTextFlags_Vertical = 1 << 10 // text will be rendered vertically -}; - -// Flags for PlotDummy (placeholder) -enum ImPlotDummyFlags_ { - ImPlotDummyFlags_None = 0 // default -}; - -// Represents a condition for SetupAxisLimits etc. (same as ImGuiCond, but we only support a subset of those enums) -enum ImPlotCond_ -{ - ImPlotCond_None = ImGuiCond_None, // No condition (always set the variable), same as _Always - ImPlotCond_Always = ImGuiCond_Always, // No condition (always set the variable) - ImPlotCond_Once = ImGuiCond_Once, // Set the variable once per runtime session (only the first call will succeed) -}; - -// Plot styling colors. -enum ImPlotCol_ { - // item styling colors - ImPlotCol_Line, // plot line/outline color (defaults to next unused color in current colormap) - ImPlotCol_Fill, // plot fill color for bars (defaults to the current line color) - ImPlotCol_MarkerOutline, // marker outline color (defaults to the current line color) - ImPlotCol_MarkerFill, // marker fill color (defaults to the current line color) - ImPlotCol_ErrorBar, // error bar color (defaults to ImGuiCol_Text) - // plot styling colors - ImPlotCol_FrameBg, // plot frame background color (defaults to ImGuiCol_FrameBg) - ImPlotCol_PlotBg, // plot area background color (defaults to ImGuiCol_WindowBg) - ImPlotCol_PlotBorder, // plot area border color (defaults to ImGuiCol_Border) - ImPlotCol_LegendBg, // legend background color (defaults to ImGuiCol_PopupBg) - ImPlotCol_LegendBorder, // legend border color (defaults to ImPlotCol_PlotBorder) - ImPlotCol_LegendText, // legend text color (defaults to ImPlotCol_InlayText) - ImPlotCol_TitleText, // plot title text color (defaults to ImGuiCol_Text) - ImPlotCol_InlayText, // color of text appearing inside of plots (defaults to ImGuiCol_Text) - ImPlotCol_AxisText, // axis label and tick lables color (defaults to ImGuiCol_Text) - ImPlotCol_AxisGrid, // axis grid color (defaults to 25% ImPlotCol_AxisText) - ImPlotCol_AxisTick, // axis tick color (defaults to AxisGrid) - ImPlotCol_AxisBg, // background color of axis hover region (defaults to transparent) - ImPlotCol_AxisBgHovered, // axis hover color (defaults to ImGuiCol_ButtonHovered) - ImPlotCol_AxisBgActive, // axis active color (defaults to ImGuiCol_ButtonActive) - ImPlotCol_Selection, // box-selection color (defaults to yellow) - ImPlotCol_Crosshairs, // crosshairs color (defaults to ImPlotCol_PlotBorder) - ImPlotCol_COUNT -}; - -// Plot styling variables. -enum ImPlotStyleVar_ { - // item styling variables - ImPlotStyleVar_LineWeight, // float, plot item line weight in pixels - ImPlotStyleVar_Marker, // int, marker specification - ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius") - ImPlotStyleVar_MarkerWeight, // float, plot outline weight of markers in pixels - ImPlotStyleVar_FillAlpha, // float, alpha modifier applied to all plot item fills - ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels - ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels - ImPlotStyleVar_DigitalBitHeight, // float, digital channels bit height (at 1) in pixels - ImPlotStyleVar_DigitalBitGap, // float, digital channels bit padding gap in pixels - // plot styling variables - ImPlotStyleVar_PlotBorderSize, // float, thickness of border around plot area - ImPlotStyleVar_MinorAlpha, // float, alpha multiplier applied to minor axis grid lines - ImPlotStyleVar_MajorTickLen, // ImVec2, major tick lengths for X and Y axes - ImPlotStyleVar_MinorTickLen, // ImVec2, minor tick lengths for X and Y axes - ImPlotStyleVar_MajorTickSize, // ImVec2, line thickness of major ticks - ImPlotStyleVar_MinorTickSize, // ImVec2, line thickness of minor ticks - ImPlotStyleVar_MajorGridSize, // ImVec2, line thickness of major grid lines - ImPlotStyleVar_MinorGridSize, // ImVec2, line thickness of minor grid lines - ImPlotStyleVar_PlotPadding, // ImVec2, padding between widget frame and plot area, labels, or outside legends (i.e. main padding) - ImPlotStyleVar_LabelPadding, // ImVec2, padding between axes labels, tick labels, and plot edge - ImPlotStyleVar_LegendPadding, // ImVec2, legend padding from plot edges - ImPlotStyleVar_LegendInnerPadding, // ImVec2, legend inner padding from legend edges - ImPlotStyleVar_LegendSpacing, // ImVec2, spacing between legend entries - ImPlotStyleVar_MousePosPadding, // ImVec2, padding between plot edge and interior info text - ImPlotStyleVar_AnnotationPadding, // ImVec2, text padding around annotation labels - ImPlotStyleVar_FitPadding, // ImVec2, additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y) - ImPlotStyleVar_PlotDefaultSize, // ImVec2, default size used when ImVec2(0,0) is passed to BeginPlot - ImPlotStyleVar_PlotMinSize, // ImVec2, minimum size plot frame can be when shrunk - ImPlotStyleVar_COUNT -}; - -// Axis scale -enum ImPlotScale_ { - ImPlotScale_Linear = 0, // default linear scale - ImPlotScale_Time, // date/time scale - ImPlotScale_Log10, // base 10 logartithmic scale - ImPlotScale_SymLog, // symmetric log scale -}; - -// Marker specifications. -enum ImPlotMarker_ { - ImPlotMarker_None = -1, // no marker - ImPlotMarker_Circle, // a circle marker (default) - ImPlotMarker_Square, // a square maker - ImPlotMarker_Diamond, // a diamond marker - ImPlotMarker_Up, // an upward-pointing triangle marker - ImPlotMarker_Down, // an downward-pointing triangle marker - ImPlotMarker_Left, // an leftward-pointing triangle marker - ImPlotMarker_Right, // an rightward-pointing triangle marker - ImPlotMarker_Cross, // a cross marker (not fillable) - ImPlotMarker_Plus, // a plus marker (not fillable) - ImPlotMarker_Asterisk, // a asterisk marker (not fillable) - ImPlotMarker_COUNT -}; - -// Built-in colormaps -enum ImPlotColormap_ { - ImPlotColormap_Deep = 0, // a.k.a. seaborn deep (qual=true, n=10) (default) - ImPlotColormap_Dark = 1, // a.k.a. matplotlib "Set1" (qual=true, n=9 ) - ImPlotColormap_Pastel = 2, // a.k.a. matplotlib "Pastel1" (qual=true, n=9 ) - ImPlotColormap_Paired = 3, // a.k.a. matplotlib "Paired" (qual=true, n=12) - ImPlotColormap_Viridis = 4, // a.k.a. matplotlib "viridis" (qual=false, n=11) - ImPlotColormap_Plasma = 5, // a.k.a. matplotlib "plasma" (qual=false, n=11) - ImPlotColormap_Hot = 6, // a.k.a. matplotlib/MATLAB "hot" (qual=false, n=11) - ImPlotColormap_Cool = 7, // a.k.a. matplotlib/MATLAB "cool" (qual=false, n=11) - ImPlotColormap_Pink = 8, // a.k.a. matplotlib/MATLAB "pink" (qual=false, n=11) - ImPlotColormap_Jet = 9, // a.k.a. MATLAB "jet" (qual=false, n=11) - ImPlotColormap_Twilight = 10, // a.k.a. matplotlib "twilight" (qual=false, n=11) - ImPlotColormap_RdBu = 11, // red/blue, Color Brewer (qual=false, n=11) - ImPlotColormap_BrBG = 12, // brown/blue-green, Color Brewer (qual=false, n=11) - ImPlotColormap_PiYG = 13, // pink/yellow-green, Color Brewer (qual=false, n=11) - ImPlotColormap_Spectral = 14, // color spectrum, Color Brewer (qual=false, n=11) - ImPlotColormap_Greys = 15, // white/black (qual=false, n=2 ) -}; - -// Used to position items on a plot (e.g. legends, labels, etc.) -enum ImPlotLocation_ { - ImPlotLocation_Center = 0, // center-center - ImPlotLocation_North = 1 << 0, // top-center - ImPlotLocation_South = 1 << 1, // bottom-center - ImPlotLocation_West = 1 << 2, // center-left - ImPlotLocation_East = 1 << 3, // center-right - ImPlotLocation_NorthWest = ImPlotLocation_North | ImPlotLocation_West, // top-left - ImPlotLocation_NorthEast = ImPlotLocation_North | ImPlotLocation_East, // top-right - ImPlotLocation_SouthWest = ImPlotLocation_South | ImPlotLocation_West, // bottom-left - ImPlotLocation_SouthEast = ImPlotLocation_South | ImPlotLocation_East // bottom-right -}; - -// Enums for different automatic histogram binning methods (k = bin count or w = bin width) -enum ImPlotBin_ { - ImPlotBin_Sqrt = -1, // k = sqrt(n) - ImPlotBin_Sturges = -2, // k = 1 + log2(n) - ImPlotBin_Rice = -3, // k = 2 * cbrt(n) - ImPlotBin_Scott = -4, // w = 3.49 * sigma / cbrt(n) -}; - -// Double precision version of ImVec2 used by ImPlot. Extensible by end users. -struct ImPlotPoint { - double x, y; - ImPlotPoint() { x = y = 0.0; } - ImPlotPoint(double _x, double _y) { x = _x; y = _y; } - ImPlotPoint(const ImVec2& p) { x = p.x; y = p.y; } - double operator[] (size_t idx) const { return (&x)[idx]; } - double& operator[] (size_t idx) { return (&x)[idx]; } -#ifdef IMPLOT_POINT_CLASS_EXTRA - IMPLOT_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h - // to convert back and forth between your math types and ImPlotPoint. -#endif -}; - -// Range defined by a min/max value. -struct ImPlotRange { - double Min, Max; - ImPlotRange() { Min = 0; Max = 0; } - ImPlotRange(double _min, double _max) { Min = _min; Max = _max; } - bool Contains(double value) const { return value >= Min && value <= Max; } - double Size() const { return Max - Min; } - double Clamp(double value) const { return (value < Min) ? Min : (value > Max) ? Max : value; } -}; - -// Combination of two range limits for X and Y axes. Also an AABB defined by Min()/Max(). -struct ImPlotRect { - ImPlotRange X, Y; - ImPlotRect() { } - ImPlotRect(double x_min, double x_max, double y_min, double y_max) { X.Min = x_min; X.Max = x_max; Y.Min = y_min; Y.Max = y_max; } - bool Contains(const ImPlotPoint& p) const { return Contains(p.x, p.y); } - bool Contains(double x, double y) const { return X.Contains(x) && Y.Contains(y); } - ImPlotPoint Size() const { return ImPlotPoint(X.Size(), Y.Size()); } - ImPlotPoint Clamp(const ImPlotPoint& p) { return Clamp(p.x, p.y); } - ImPlotPoint Clamp(double x, double y) { return ImPlotPoint(X.Clamp(x),Y.Clamp(y)); } - ImPlotPoint Min() const { return ImPlotPoint(X.Min, Y.Min); } - ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); } -}; - -// Plot style structure -struct ImPlotStyle { - // item styling variables - float LineWeight; // = 1, item line weight in pixels - int Marker; // = ImPlotMarker_None, marker specification - float MarkerSize; // = 4, marker size in pixels (roughly the marker's "radius") - float MarkerWeight; // = 1, outline weight of markers in pixels - float FillAlpha; // = 1, alpha modifier applied to plot fills - float ErrorBarSize; // = 5, error bar whisker width in pixels - float ErrorBarWeight; // = 1.5, error bar whisker weight in pixels - float DigitalBitHeight; // = 8, digital channels bit height (at y = 1.0f) in pixels - float DigitalBitGap; // = 4, digital channels bit padding gap in pixels - // plot styling variables - float PlotBorderSize; // = 1, line thickness of border around plot area - float MinorAlpha; // = 0.25 alpha multiplier applied to minor axis grid lines - ImVec2 MajorTickLen; // = 10,10 major tick lengths for X and Y axes - ImVec2 MinorTickLen; // = 5,5 minor tick lengths for X and Y axes - ImVec2 MajorTickSize; // = 1,1 line thickness of major ticks - ImVec2 MinorTickSize; // = 1,1 line thickness of minor ticks - ImVec2 MajorGridSize; // = 1,1 line thickness of major grid lines - ImVec2 MinorGridSize; // = 1,1 line thickness of minor grid lines - ImVec2 PlotPadding; // = 10,10 padding between widget frame and plot area, labels, or outside legends (i.e. main padding) - ImVec2 LabelPadding; // = 5,5 padding between axes labels, tick labels, and plot edge - ImVec2 LegendPadding; // = 10,10 legend padding from plot edges - ImVec2 LegendInnerPadding; // = 5,5 legend inner padding from legend edges - ImVec2 LegendSpacing; // = 5,0 spacing between legend entries - ImVec2 MousePosPadding; // = 10,10 padding between plot edge and interior mouse location text - ImVec2 AnnotationPadding; // = 2,2 text padding around annotation labels - ImVec2 FitPadding; // = 0,0 additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y) - ImVec2 PlotDefaultSize; // = 400,300 default size used when ImVec2(0,0) is passed to BeginPlot - ImVec2 PlotMinSize; // = 200,150 minimum size plot frame can be when shrunk - // style colors - ImVec4 Colors[ImPlotCol_COUNT]; // Array of styling colors. Indexable with ImPlotCol_ enums. - // colormap - ImPlotColormap Colormap; // The current colormap. Set this to either an ImPlotColormap_ enum or an index returned by AddColormap. - // settings/flags - bool UseLocalTime; // = false, axis labels will be formatted for your timezone when ImPlotAxisFlag_Time is enabled - bool UseISO8601; // = false, dates will be formatted according to ISO 8601 where applicable (e.g. YYYY-MM-DD, YYYY-MM, --MM-DD, etc.) - bool Use24HourClock; // = false, times will be formatted using a 24 hour clock - IMPLOT_API ImPlotStyle(); -}; - -#if (IMGUI_VERSION_NUM < 18716) // Renamed in 1.88 -#define ImGuiModFlags ImGuiKeyModFlags -#define ImGuiModFlags_None ImGuiKeyModFlags_None -#define ImGuiModFlags_Ctrl ImGuiKeyModFlags_Ctrl -#define ImGuiModFlags_Shift ImGuiKeyModFlags_Shift -#define ImGuiModFlags_Alt ImGuiKeyModFlags_Alt -#define ImGuiModFlags_Super ImGuiKeyModFlags_Super -#endif - -// Input mapping structure. Default values listed. See also MapInputDefault, MapInputReverse. -struct ImPlotInputMap { - ImGuiMouseButton Pan; // LMB enables panning when held, - ImGuiModFlags PanMod; // none optional modifier that must be held for panning/fitting - ImGuiMouseButton Fit; // LMB initiates fit when double clicked - ImGuiMouseButton Select; // RMB begins box selection when pressed and confirms selection when released - ImGuiMouseButton SelectCancel; // LMB cancels active box selection when pressed; cannot be same as Select - ImGuiModFlags SelectMod; // none optional modifier that must be held for box selection - ImGuiModFlags SelectHorzMod; // Alt expands active box selection horizontally to plot edge when held - ImGuiModFlags SelectVertMod; // Shift expands active box selection vertically to plot edge when held - ImGuiMouseButton Menu; // RMB opens context menus (if enabled) when clicked - ImGuiModFlags OverrideMod; // Ctrl when held, all input is ignored; used to enable axis/plots as DND sources - ImGuiModFlags ZoomMod; // none optional modifier that must be held for scroll wheel zooming - float ZoomRate; // 0.1f zoom rate for scroll (e.g. 0.1f = 10% plot range every scroll click); make negative to invert - IMPLOT_API ImPlotInputMap(); -}; - -//----------------------------------------------------------------------------- -// [SECTION] Callbacks -//----------------------------------------------------------------------------- - -// Callback signature for axis tick label formatter. -typedef int (*ImPlotFormatter)(double value, char* buff, int size, void* user_data); - -// Callback signature for data getter. -typedef ImPlotPoint (*ImPlotGetter)(int idx, void* user_data); - -// Callback signature for axis transform. -typedef double (*ImPlotTransform)(double value, void* user_data); - -namespace ImPlot { - -//----------------------------------------------------------------------------- -// [SECTION] Contexts -//----------------------------------------------------------------------------- - -// Creates a new ImPlot context. Call this after ImGui::CreateContext. -IMPLOT_API ImPlotContext* CreateContext(); -// Destroys an ImPlot context. Call this before ImGui::DestroyContext. NULL = destroy current context. -IMPLOT_API void DestroyContext(ImPlotContext* ctx = NULL); -// Returns the current ImPlot context. NULL if no context has ben set. -IMPLOT_API ImPlotContext* GetCurrentContext(); -// Sets the current ImPlot context. -IMPLOT_API void SetCurrentContext(ImPlotContext* ctx); - -// Sets the current **ImGui** context. This is ONLY necessary if you are compiling -// ImPlot as a DLL (not recommended) separate from your ImGui compilation. It -// sets the global variable GImGui, which is not shared across DLL boundaries. -// See GImGui documentation in imgui.cpp for more details. -IMPLOT_API void SetImGuiContext(ImGuiContext* ctx); - -//----------------------------------------------------------------------------- -// [SECTION] Begin/End Plot -//----------------------------------------------------------------------------- - -// Starts a 2D plotting context. If this function returns true, EndPlot() MUST -// be called! You are encouraged to use the following convention: -// -// if (BeginPlot(...)) { -// PlotLine(...); -// ... -// EndPlot(); -// } -// -// Important notes: -// -// - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID -// collisions or don't want to display a title in the plot, use double hashes -// (e.g. "MyPlot##HiddenIdText" or "##NoTitle"). -// - #size is the **frame** size of the plot widget, not the plot area. The default -// size of plots (i.e. when ImVec2(0,0)) can be modified in your ImPlotStyle. -IMPLOT_API bool BeginPlot(const char* title_id, const ImVec2& size=ImVec2(-1,0), ImPlotFlags flags=0); - -// Only call EndPlot() if BeginPlot() returns true! Typically called at the end -// of an if statement conditioned on BeginPlot(). See example above. -IMPLOT_API void EndPlot(); - -//----------------------------------------------------------------------------- -// [SECTION] Begin/End Subplots -//----------------------------------------------------------------------------- - -// Starts a subdivided plotting context. If the function returns true, -// EndSubplots() MUST be called! Call BeginPlot/EndPlot AT MOST [rows*cols] -// times in between the begining and end of the subplot context. Plots are -// added in row major order. -// -// Example: -// -// if (BeginSubplots("My Subplot",2,3,ImVec2(800,400)) { -// for (int i = 0; i < 6; ++i) { -// if (BeginPlot(...)) { -// ImPlot::PlotLine(...); -// ... -// EndPlot(); -// } -// } -// EndSubplots(); -// } -// -// Produces: -// -// [0] | [1] | [2] -// ----|-----|---- -// [3] | [4] | [5] -// -// Important notes: -// -// - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID -// collisions or don't want to display a title in the plot, use double hashes -// (e.g. "MySubplot##HiddenIdText" or "##NoTitle"). -// - #rows and #cols must be greater than 0. -// - #size is the size of the entire grid of subplots, not the individual plots -// - #row_ratios and #col_ratios must have AT LEAST #rows and #cols elements, -// respectively. These are the sizes of the rows and columns expressed in ratios. -// If the user adjusts the dimensions, the arrays are updated with new ratios. -// -// Important notes regarding BeginPlot from inside of BeginSubplots: -// -// - The #title_id parameter of _BeginPlot_ (see above) does NOT have to be -// unique when called inside of a subplot context. Subplot IDs are hashed -// for your convenience so you don't have call PushID or generate unique title -// strings. Simply pass an empty string to BeginPlot unless you want to title -// each subplot. -// - The #size parameter of _BeginPlot_ (see above) is ignored when inside of a -// subplot context. The actual size of the subplot will be based on the -// #size value you pass to _BeginSubplots_ and #row/#col_ratios if provided. - -IMPLOT_API bool BeginSubplots(const char* title_id, - int rows, - int cols, - const ImVec2& size, - ImPlotSubplotFlags flags = 0, - float* row_ratios = NULL, - float* col_ratios = NULL); - -// Only call EndSubplots() if BeginSubplots() returns true! Typically called at the end -// of an if statement conditioned on BeginSublots(). See example above. -IMPLOT_API void EndSubplots(); - -//----------------------------------------------------------------------------- -// [SECTION] Setup -//----------------------------------------------------------------------------- - -// The following API allows you to setup and customize various aspects of the -// current plot. The functions should be called immediately after BeginPlot -// and before any other API calls. Typical usage is as follows: - -// if (BeginPlot(...)) { 1) begin a new plot -// SetupAxis(ImAxis_X1, "My X-Axis"); 2) make Setup calls -// SetupAxis(ImAxis_Y1, "My Y-Axis"); -// SetupLegend(ImPlotLocation_North); -// ... -// SetupFinish(); 3) [optional] explicitly finish setup -// PlotLine(...); 4) plot items -// ... -// EndPlot(); 5) end the plot -// } -// -// Important notes: -// -// - Always call Setup code at the top of your BeginPlot conditional statement. -// - Setup is locked once you start plotting or explicitly call SetupFinish. -// Do NOT call Setup code after you begin plotting or after you make -// any non-Setup API calls (e.g. utils like PlotToPixels also lock Setup) -// - Calling SetupFinish is OPTIONAL, but probably good practice. If you do not -// call it yourself, then the first subsequent plotting or utility function will -// call it for you. - -// Enables an axis or sets the label and/or flags for an existing axis. Leave #label = NULL for no label. -IMPLOT_API void SetupAxis(ImAxis axis, const char* label=NULL, ImPlotAxisFlags flags=0); -// Sets an axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. -IMPLOT_API void SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once); -// Links an axis range limits to external values. Set to NULL for no linkage. The pointer data must remain valid until EndPlot. -IMPLOT_API void SetupAxisLinks(ImAxis axis, double* link_min, double* link_max); -// Sets the format of numeric axis labels via formater specifier (default="%g"). Formated values will be double (i.e. use %f). -IMPLOT_API void SetupAxisFormat(ImAxis axis, const char* fmt); -// Sets the format of numeric axis labels via formatter callback. Given #value, write a label into #buff. Optionally pass user data. -IMPLOT_API void SetupAxisFormat(ImAxis axis, ImPlotFormatter formatter, void* data=NULL); -// Sets an axis' ticks and optionally the labels. To keep the default ticks, set #keep_default=true. -IMPLOT_API void SetupAxisTicks(ImAxis axis, const double* values, int n_ticks, const char* const labels[]=NULL, bool keep_default=false); -// Sets an axis' ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true. -IMPLOT_API void SetupAxisTicks(ImAxis axis, double v_min, double v_max, int n_ticks, const char* const labels[]=NULL, bool keep_default=false); -// Sets an axis' scale using built-in options. -IMPLOT_API void SetupAxisScale(ImAxis axis, ImPlotScale scale); -// Sets an axis' scale using user supplied forward and inverse transfroms. -IMPLOT_API void SetupAxisScale(ImAxis axis, ImPlotTransform forward, ImPlotTransform inverse, void* data=NULL); -// Sets an axis' limits constraints. -IMPLOT_API void SetupAxisLimitsConstraints(ImAxis axis, double v_min, double v_max); -// Sets an axis' zoom constraints. -IMPLOT_API void SetupAxisZoomConstraints(ImAxis axis, double z_min, double z_max); - -// Sets the label and/or flags for primary X and Y axes (shorthand for two calls to SetupAxis). -IMPLOT_API void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags=0, ImPlotAxisFlags y_flags=0); -// Sets the primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits). -IMPLOT_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once); - -// Sets up the plot legend. -IMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags=0); -// Set the location of the current plot's mouse position text (default = South|East). -IMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags=0); - -// Explicitly finalize plot setup. Once you call this, you cannot make anymore Setup calls for the current plot! -// Note that calling this function is OPTIONAL; it will be called by the first subsequent setup-locking API call. -IMPLOT_API void SetupFinish(); - -//----------------------------------------------------------------------------- -// [SECTION] SetNext -//----------------------------------------------------------------------------- - -// Though you should default to the `Setup` API above, there are some scenarios -// where (re)configuring a plot or axis before `BeginPlot` is needed (e.g. if -// using a preceding button or slider widget to change the plot limits). In -// this case, you can use the `SetNext` API below. While this is not as feature -// rich as the Setup API, most common needs are provided. These functions can be -// called anwhere except for inside of `Begin/EndPlot`. For example: - -// if (ImGui::Button("Center Plot")) -// ImPlot::SetNextPlotLimits(-1,1,-1,1); -// if (ImPlot::BeginPlot(...)) { -// ... -// ImPlot::EndPlot(); -// } -// -// Important notes: -// -// - You must still enable non-default axes with SetupAxis for these functions -// to work properly. - -// Sets an upcoming axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. -IMPLOT_API void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once); -// Links an upcoming axis range limits to external values. Set to NULL for no linkage. The pointer data must remain valid until EndPlot! -IMPLOT_API void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max); -// Set an upcoming axis to auto fit to its data. -IMPLOT_API void SetNextAxisToFit(ImAxis axis); - -// Sets the upcoming primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits). -IMPLOT_API void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once); -// Sets all upcoming axes to auto fit to their data. -IMPLOT_API void SetNextAxesToFit(); - -//----------------------------------------------------------------------------- -// [SECTION] Plot Items -//----------------------------------------------------------------------------- - -// The main plotting API is provied below. Call these functions between -// Begin/EndPlot and after any Setup API calls. Each plots data on the current -// x and y axes, which can be changed with `SetAxis/Axes`. -// -// The templated functions are explicitly instantiated in implot_items.cpp. -// They are not intended to be used generically with custom types. You will get -// a linker error if you try! All functions support the following scalar types: -// -// float, double, ImS8, ImU8, ImS16, ImU16, ImS32, ImU32, ImS64, ImU64 -// -// -// If you need to plot custom or non-homogenous data you have a few options: -// -// 1. If your data is a simple struct/class (e.g. Vector2f), you can use striding. -// This is the most performant option if applicable. -// -// struct Vector2f { float X, Y; }; -// ... -// Vector2f data[42]; -// ImPlot::PlotLine("line", &data[0].x, &data[0].y, 42, 0, 0, sizeof(Vector2f)); -// -// 2. Write a custom getter C function or C++ lambda and pass it and optionally your data to -// an ImPlot function post-fixed with a G (e.g. PlotScatterG). This has a slight performance -// cost, but probably not enough to worry about unless your data is very large. Examples: -// -// ImPlotPoint MyDataGetter(void* data, int idx) { -// MyData* my_data = (MyData*)data; -// ImPlotPoint p; -// p.x = my_data->GetTime(idx); -// p.y = my_data->GetValue(idx); -// return p -// } -// ... -// auto my_lambda = [](int idx, void*) { -// double t = idx / 999.0; -// return ImPlotPoint(t, 0.5+0.5*std::sin(2*PI*10*t)); -// }; -// ... -// if (ImPlot::BeginPlot("MyPlot")) { -// MyData my_data; -// ImPlot::PlotScatterG("scatter", MyDataGetter, &my_data, my_data.Size()); -// ImPlot::PlotLineG("line", my_lambda, nullptr, 1000); -// ImPlot::EndPlot(); -// } -// -// NB: All types are converted to double before plotting. You may lose information -// if you try plotting extremely large 64-bit integral types. Proceed with caution! - -// Plots a standard 2D line plot. -IMPLOT_TMP void PlotLine(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, int count, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotLineG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotLineFlags flags=0); - -// Plots a standard 2D scatter plot. Default marker is ImPlotMarker_Circle. -IMPLOT_TMP void PlotScatter(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotScatterG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotScatterFlags flags=0); - -// Plots a a stairstep graph. The y value is continued constantly to the right from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i] -IMPLOT_TMP void PlotStairs(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotStairsG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotStairsFlags flags=0); - -// Plots a shaded (filled) region between two lines, or a line and a horizontal reference. Set yref to +/-INFINITY for infinite fill extents. -IMPLOT_TMP void PlotShaded(const char* label_id, const T* values, int count, double yref=0, double xscale=1, double xstart=0, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double yref=0, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotShadedG(const char* label_id, ImPlotGetter getter1, void* data1, ImPlotGetter getter2, void* data2, int count, ImPlotShadedFlags flags=0); - -// Plots a bar graph. Vertical by default. #bar_size and #shift are in plot units. -IMPLOT_TMP void PlotBars(const char* label_id, const T* values, int count, double bar_size=0.67, double shift=0, ImPlotBarsFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotBars(const char* label_id, const T* xs, const T* ys, int count, double bar_size, ImPlotBarsFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotBarsG(const char* label_id, ImPlotGetter getter, void* data, int count, double bar_size, ImPlotBarsFlags flags=0); - -// Plots a group of bars. #values is a row-major matrix with #item_count rows and #group_count cols. #label_ids should have #item_count elements. -IMPLOT_TMP void PlotBarGroups(const char* const label_ids[], const T* values, int item_count, int group_count, double group_size=0.67, double shift=0, ImPlotBarGroupsFlags flags=0); - -// Plots vertical error bar. The label_id should be the same as the label_id of the associated line or bar plot. -IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, ImPlotErrorBarsFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, ImPlotErrorBarsFlags flags=0, int offset=0, int stride=sizeof(T)); - -// Plots stems. Vertical by default. -IMPLOT_TMP void PlotStems(const char* label_id, const T* values, int count, double ref=0, double scale=1, double start=0, ImPlotStemsFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotStems(const char* label_id, const T* xs, const T* ys, int count, double ref=0, ImPlotStemsFlags flags=0, int offset=0, int stride=sizeof(T)); - -// Plots infinite vertical or horizontal lines (e.g. for references or asymptotes). -IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags=0, int offset=0, int stride=sizeof(T)); - -// Plots a pie chart. Center and radius are in plot units. #label_fmt can be set to NULL for no labels. -IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); - -// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to NULL for no labels. -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0); - -// Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. -// Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. -IMPLOT_TMP double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, double bar_scale=1.0, ImPlotRange range=ImPlotRange(), ImPlotHistogramFlags flags=0); - -// Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBin. If #range is left unspecified, the min/max of -// #xs an #ys will be used as the ranges. Otherwise, outlier values outside of range are not binned. The largest bin count or density is returned. -IMPLOT_TMP double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBin_Sturges, int y_bins=ImPlotBin_Sturges, ImPlotRect range=ImPlotRect(), ImPlotHistogramFlags flags=0); - -// Plots digital data. Digital plots do not respond to y drag or zoom, and are always referenced to the bottom of the plot. -IMPLOT_TMP void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, ImPlotDigitalFlags flags=0, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotDigitalG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotDigitalFlags flags=0); - -// Plots an axis-aligned image. #bounds_min/bounds_max are in plot coordinates (y-up) and #uv0/uv1 are in texture coordinates (y-down). -IMPLOT_API void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0=ImVec2(0,0), const ImVec2& uv1=ImVec2(1,1), const ImVec4& tint_col=ImVec4(1,1,1,1), ImPlotImageFlags flags=0); - -// Plots a centered text label at point x,y with an optional pixel offset. Text color can be changed with ImPlot::PushStyleColor(ImPlotCol_InlayText, ...). -IMPLOT_API void PlotText(const char* text, double x, double y, const ImVec2& pix_offset=ImVec2(0,0), ImPlotTextFlags flags=0); - -// Plots a dummy item (i.e. adds a legend entry colored by ImPlotCol_Line) -IMPLOT_API void PlotDummy(const char* label_id, ImPlotDummyFlags flags=0); - -//----------------------------------------------------------------------------- -// [SECTION] Plot Tools -//----------------------------------------------------------------------------- - -// The following can be used to render interactive elements and/or annotations. -// Like the item plotting functions above, they apply to the current x and y -// axes, which can be changed with `SetAxis/SetAxes`. - -// Shows a draggable point at x,y. #col defaults to ImGuiCol_Text. -IMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags=0); -// Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text. -IMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags=0); -// Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text. -IMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags=0); -// Shows a draggable and resizeable rectangle. -IMPLOT_API bool DragRect(int id, double* x1, double* y1, double* x2, double* y2, const ImVec4& col, ImPlotDragToolFlags flags=0); - -// Shows an annotation callout at a chosen point. Clamping keeps annotations in the plot area. Annotations are always rendered on top. -IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, bool round = false); -IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, const char* fmt, ...) IM_FMTARGS(6); -IMPLOT_API void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, const char* fmt, va_list args) IM_FMTLIST(6); - -// Shows a x-axis tag at the specified coordinate value. -IMPLOT_API void TagX(double x, const ImVec4& col, bool round = false); -IMPLOT_API void TagX(double x, const ImVec4& col, const char* fmt, ...) IM_FMTARGS(3); -IMPLOT_API void TagXV(double x, const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(3); - -// Shows a y-axis tag at the specified coordinate value. -IMPLOT_API void TagY(double y, const ImVec4& col, bool round = false); -IMPLOT_API void TagY(double y, const ImVec4& col, const char* fmt, ...) IM_FMTARGS(3); -IMPLOT_API void TagYV(double y, const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(3); - -//----------------------------------------------------------------------------- -// [SECTION] Plot Utils -//----------------------------------------------------------------------------- - -// Select which axis/axes will be used for subsequent plot elements. -IMPLOT_API void SetAxis(ImAxis axis); -IMPLOT_API void SetAxes(ImAxis x_axis, ImAxis y_axis); - -// Convert pixels to a position in the current plot's coordinate system. Passing IMPLOT_AUTO uses the current axes. -IMPLOT_API ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); -IMPLOT_API ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); - -// Convert a position in the current plot's coordinate system to pixels. Passing IMPLOT_AUTO uses the current axes. -IMPLOT_API ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); -IMPLOT_API ImVec2 PlotToPixels(double x, double y, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); - -// Get the current Plot position (top-left) in pixels. -IMPLOT_API ImVec2 GetPlotPos(); -// Get the curent Plot size in pixels. -IMPLOT_API ImVec2 GetPlotSize(); - -// Returns the mouse position in x,y coordinates of the current plot. Passing IMPLOT_AUTO uses the current axes. -IMPLOT_API ImPlotPoint GetPlotMousePos(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); -// Returns the current plot axis range. -IMPLOT_API ImPlotRect GetPlotLimits(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); - -// Returns true if the plot area in the current plot is hovered. -IMPLOT_API bool IsPlotHovered(); -// Returns true if the axis label area in the current plot is hovered. -IMPLOT_API bool IsAxisHovered(ImAxis axis); -// Returns true if the bounding frame of a subplot is hovered. -IMPLOT_API bool IsSubplotsHovered(); - -// Returns true if the current plot is being box selected. -IMPLOT_API bool IsPlotSelected(); -// Returns the current plot box selection bounds. Passing IMPLOT_AUTO uses the current axes. -IMPLOT_API ImPlotRect GetPlotSelection(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); -// Cancels a the current plot box selection. -IMPLOT_API void CancelPlotSelection(); - -// Hides or shows the next plot item (i.e. as if it were toggled from the legend). -// Use ImPlotCond_Always if you need to forcefully set this every frame. -IMPLOT_API void HideNextItem(bool hidden = true, ImPlotCond cond = ImPlotCond_Once); - -// Use the following around calls to Begin/EndPlot to align l/r/t/b padding. -// Consider using Begin/EndSubplots first. They are more feature rich and -// accomplish the same behaviour by default. The functions below offer lower -// level control of plot alignment. - -// Align axis padding over multiple plots in a single row or column. #group_id must -// be unique. If this function returns true, EndAlignedPlots() must be called. -IMPLOT_API bool BeginAlignedPlots(const char* group_id, bool vertical = true); -// Only call EndAlignedPlots() if BeginAlignedPlots() returns true! -IMPLOT_API void EndAlignedPlots(); - -//----------------------------------------------------------------------------- -// [SECTION] Legend Utils -//----------------------------------------------------------------------------- - -// Begin a popup for a legend entry. -IMPLOT_API bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button=1); -// End a popup for a legend entry. -IMPLOT_API void EndLegendPopup(); -// Returns true if a plot item legend entry is hovered. -IMPLOT_API bool IsLegendEntryHovered(const char* label_id); - -//----------------------------------------------------------------------------- -// [SECTION] Drag and Drop -//----------------------------------------------------------------------------- - -// Turns the current plot's plotting area into a drag and drop target. Don't forget to call EndDragDropTarget! -IMPLOT_API bool BeginDragDropTargetPlot(); -// Turns the current plot's X-axis into a drag and drop target. Don't forget to call EndDragDropTarget! -IMPLOT_API bool BeginDragDropTargetAxis(ImAxis axis); -// Turns the current plot's legend into a drag and drop target. Don't forget to call EndDragDropTarget! -IMPLOT_API bool BeginDragDropTargetLegend(); -// Ends a drag and drop target (currently just an alias for ImGui::EndDragDropTarget). -IMPLOT_API void EndDragDropTarget(); - -// NB: By default, plot and axes drag and drop *sources* require holding the Ctrl modifier to initiate the drag. -// You can change the modifier if desired. If ImGuiModFlags_None is provided, the axes will be locked from panning. - -// Turns the current plot's plotting area into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource! -IMPLOT_API bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags=0); -// Turns the current plot's X-axis into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource! -IMPLOT_API bool BeginDragDropSourceAxis(ImAxis axis, ImGuiDragDropFlags flags=0); -// Turns an item in the current plot's legend into drag and drop source. Don't forget to call EndDragDropSource! -IMPLOT_API bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags=0); -// Ends a drag and drop source (currently just an alias for ImGui::EndDragDropSource). -IMPLOT_API void EndDragDropSource(); - -//----------------------------------------------------------------------------- -// [SECTION] Styling -//----------------------------------------------------------------------------- - -// Styling colors in ImPlot works similarly to styling colors in ImGui, but -// with one important difference. Like ImGui, all style colors are stored in an -// indexable array in ImPlotStyle. You can permanently modify these values through -// GetStyle().Colors, or temporarily modify them with Push/Pop functions below. -// However, by default all style colors in ImPlot default to a special color -// IMPLOT_AUTO_COL. The behavior of this color depends upon the style color to -// which it as applied: -// -// 1) For style colors associated with plot items (e.g. ImPlotCol_Line), -// IMPLOT_AUTO_COL tells ImPlot to color the item with the next unused -// color in the current colormap. Thus, every item will have a different -// color up to the number of colors in the colormap, at which point the -// colormap will roll over. For most use cases, you should not need to -// set these style colors to anything but IMPLOT_COL_AUTO; you are -// probably better off changing the current colormap. However, if you -// need to explicitly color a particular item you may either Push/Pop -// the style color around the item in question, or use the SetNextXXXStyle -// API below. If you permanently set one of these style colors to a specific -// color, or forget to call Pop, then all subsequent items will be styled -// with the color you set. -// -// 2) For style colors associated with plot styling (e.g. ImPlotCol_PlotBg), -// IMPLOT_AUTO_COL tells ImPlot to set that color from color data in your -// **ImGuiStyle**. The ImGuiCol_ that these style colors default to are -// detailed above, and in general have been mapped to produce plots visually -// consistent with your current ImGui style. Of course, you are free to -// manually set these colors to whatever you like, and further can Push/Pop -// them around individual plots for plot-specific styling (e.g. coloring axes). - -// Provides access to plot style structure for permanant modifications to colors, sizes, etc. -IMPLOT_API ImPlotStyle& GetStyle(); - -// Style plot colors for current ImGui style (default). -IMPLOT_API void StyleColorsAuto(ImPlotStyle* dst = NULL); -// Style plot colors for ImGui "Classic". -IMPLOT_API void StyleColorsClassic(ImPlotStyle* dst = NULL); -// Style plot colors for ImGui "Dark". -IMPLOT_API void StyleColorsDark(ImPlotStyle* dst = NULL); -// Style plot colors for ImGui "Light". -IMPLOT_API void StyleColorsLight(ImPlotStyle* dst = NULL); - -// Use PushStyleX to temporarily modify your ImPlotStyle. The modification -// will last until the matching call to PopStyleX. You MUST call a pop for -// every push, otherwise you will leak memory! This behaves just like ImGui. - -// Temporarily modify a style color. Don't forget to call PopStyleColor! -IMPLOT_API void PushStyleColor(ImPlotCol idx, ImU32 col); -IMPLOT_API void PushStyleColor(ImPlotCol idx, const ImVec4& col); -// Undo temporary style color modification(s). Undo multiple pushes at once by increasing count. -IMPLOT_API void PopStyleColor(int count = 1); - -// Temporarily modify a style variable of float type. Don't forget to call PopStyleVar! -IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, float val); -// Temporarily modify a style variable of int type. Don't forget to call PopStyleVar! -IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, int val); -// Temporarily modify a style variable of ImVec2 type. Don't forget to call PopStyleVar! -IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, const ImVec2& val); -// Undo temporary style variable modification(s). Undo multiple pushes at once by increasing count. -IMPLOT_API void PopStyleVar(int count = 1); - -// The following can be used to modify the style of the next plot item ONLY. They do -// NOT require calls to PopStyleX. Leave style attributes you don't want modified to -// IMPLOT_AUTO or IMPLOT_AUTO_COL. Automatic styles will be deduced from the current -// values in your ImPlotStyle or from Colormap data. - -// Set the line color and weight for the next item only. -IMPLOT_API void SetNextLineStyle(const ImVec4& col = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO); -// Set the fill color for the next item only. -IMPLOT_API void SetNextFillStyle(const ImVec4& col = IMPLOT_AUTO_COL, float alpha_mod = IMPLOT_AUTO); -// Set the marker style for the next item only. -IMPLOT_API void SetNextMarkerStyle(ImPlotMarker marker = IMPLOT_AUTO, float size = IMPLOT_AUTO, const ImVec4& fill = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO, const ImVec4& outline = IMPLOT_AUTO_COL); -// Set the error bar style for the next item only. -IMPLOT_API void SetNextErrorBarStyle(const ImVec4& col = IMPLOT_AUTO_COL, float size = IMPLOT_AUTO, float weight = IMPLOT_AUTO); - -// Gets the last item primary color (i.e. its legend icon color) -IMPLOT_API ImVec4 GetLastItemColor(); - -// Returns the null terminated string name for an ImPlotCol. -IMPLOT_API const char* GetStyleColorName(ImPlotCol idx); -// Returns the null terminated string name for an ImPlotMarker. -IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); - -//----------------------------------------------------------------------------- -// [SECTION] Colormaps -//----------------------------------------------------------------------------- - -// Item styling is based on colormaps when the relevant ImPlotCol_XXX is set to -// IMPLOT_AUTO_COL (default). Several built-in colormaps are available. You can -// add and then push/pop your own colormaps as well. To permanently set a colormap, -// modify the Colormap index member of your ImPlotStyle. - -// Colormap data will be ignored and a custom color will be used if you have done one of the following: -// 1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL. -// 2) Pushed an item style color using PushStyleColor(). -// 3) Set the next item style with a SetNextXXXStyle function. - -// Add a new colormap. The color data will be copied. The colormap can be used by pushing either the returned index or the -// string name with PushColormap. The colormap name must be unique and the size must be greater than 1. You will receive -// an assert otherwise! By default colormaps are considered to be qualitative (i.e. discrete). If you want to create a -// continuous colormap, set #qual=false. This will treat the colors you provide as keys, and ImPlot will build a linearly -// interpolated lookup table. The memory footprint of this table will be exactly ((size-1)*255+1)*4 bytes. -IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImVec4* cols, int size, bool qual=true); -IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImU32* cols, int size, bool qual=true); - -// Returns the number of available colormaps (i.e. the built-in + user-added count). -IMPLOT_API int GetColormapCount(); -// Returns a null terminated string name for a colormap given an index. Returns NULL if index is invalid. -IMPLOT_API const char* GetColormapName(ImPlotColormap cmap); -// Returns an index number for a colormap given a valid string name. Returns -1 if name is invalid. -IMPLOT_API ImPlotColormap GetColormapIndex(const char* name); - -// Temporarily switch to one of the built-in (i.e. ImPlotColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to call PopColormap! -IMPLOT_API void PushColormap(ImPlotColormap cmap); -// Push a colormap by string name. Use built-in names such as "Default", "Deep", "Jet", etc. or a string you provided to AddColormap. Don't forget to call PopColormap! -IMPLOT_API void PushColormap(const char* name); -// Undo temporary colormap modification(s). Undo multiple pushes at once by increasing count. -IMPLOT_API void PopColormap(int count = 1); - -// Returns the next color from the current colormap and advances the colormap for the current plot. -// Can also be used with no return value to skip colors if desired. You need to call this between Begin/EndPlot! -IMPLOT_API ImVec4 NextColormapColor(); - -// Colormap utils. If cmap = IMPLOT_AUTO (default), the current colormap is assumed. -// Pass an explicit colormap index (built-in or user-added) to specify otherwise. - -// Returns the size of a colormap. -IMPLOT_API int GetColormapSize(ImPlotColormap cmap = IMPLOT_AUTO); -// Returns a color from a colormap given an index >= 0 (modulo will be performed). -IMPLOT_API ImVec4 GetColormapColor(int idx, ImPlotColormap cmap = IMPLOT_AUTO); -// Sample a color from the current colormap given t between 0 and 1. -IMPLOT_API ImVec4 SampleColormap(float t, ImPlotColormap cmap = IMPLOT_AUTO); - -// Shows a vertical color scale with linear spaced ticks using the specified color map. Use double hashes to hide label (e.g. "##NoLabel"). If scale_min > scale_max, the scale to color mapping will be reversed. -IMPLOT_API void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0), const char* format = "%g", ImPlotColormapScaleFlags flags = 0, ImPlotColormap cmap = IMPLOT_AUTO); -// Shows a horizontal slider with a colormap gradient background. Optionally returns the color sampled at t in [0 1]. -IMPLOT_API bool ColormapSlider(const char* label, float* t, ImVec4* out = NULL, const char* format = "", ImPlotColormap cmap = IMPLOT_AUTO); -// Shows a button with a colormap gradient brackground. -IMPLOT_API bool ColormapButton(const char* label, const ImVec2& size = ImVec2(0,0), ImPlotColormap cmap = IMPLOT_AUTO); - -// When items in a plot sample their color from a colormap, the color is cached and does not change -// unless explicitly overriden. Therefore, if you change the colormap after the item has already been plotted, -// item colors will NOT update. If you need item colors to resample the new colormap, then use this -// function to bust the cached colors. If #plot_title_id is NULL, then every item in EVERY existing plot -// will be cache busted. Otherwise only the plot specified by #plot_title_id will be busted. For the -// latter, this function must be called in the same ImGui ID scope that the plot is in. You should rarely if ever -// need this function, but it is available for applications that require runtime colormap swaps (e.g. Heatmaps demo). -IMPLOT_API void BustColorCache(const char* plot_title_id = NULL); - -//----------------------------------------------------------------------------- -// [SECTION] Input Mapping -//----------------------------------------------------------------------------- - -// Provides access to input mapping structure for permanant modifications to controls for pan, select, etc. -IMPLOT_API ImPlotInputMap& GetInputMap(); - -// Default input mapping: pan = LMB drag, box select = RMB drag, fit = LMB double click, context menu = RMB click, zoom = scroll. -IMPLOT_API void MapInputDefault(ImPlotInputMap* dst = NULL); -// Reverse input mapping: pan = RMB drag, box select = LMB drag, fit = LMB double click, context menu = RMB click, zoom = scroll. -IMPLOT_API void MapInputReverse(ImPlotInputMap* dst = NULL); - -//----------------------------------------------------------------------------- -// [SECTION] Miscellaneous -//----------------------------------------------------------------------------- - -// Render icons similar to those that appear in legends (nifty for data lists). -IMPLOT_API void ItemIcon(const ImVec4& col); -IMPLOT_API void ItemIcon(ImU32 col); -IMPLOT_API void ColormapIcon(ImPlotColormap cmap); - -// Get the plot draw list for custom rendering to the current plot area. Call between Begin/EndPlot. -IMPLOT_API ImDrawList* GetPlotDrawList(); -// Push clip rect for rendering to current plot area. The rect can be expanded or contracted by #expand pixels. Call between Begin/EndPlot. -IMPLOT_API void PushPlotClipRect(float expand=0); -// Pop plot clip rect. Call between Begin/EndPlot. -IMPLOT_API void PopPlotClipRect(); - -// Shows ImPlot style selector dropdown menu. -IMPLOT_API bool ShowStyleSelector(const char* label); -// Shows ImPlot colormap selector dropdown menu. -IMPLOT_API bool ShowColormapSelector(const char* label); -// Shows ImPlot input map selector dropdown menu. -IMPLOT_API bool ShowInputMapSelector(const char* label); -// Shows ImPlot style editor block (not a window). -IMPLOT_API void ShowStyleEditor(ImPlotStyle* ref = NULL); -// Add basic help/info block for end users (not a window). -IMPLOT_API void ShowUserGuide(); -// Shows ImPlot metrics/debug information window. -IMPLOT_API void ShowMetricsWindow(bool* p_popen = NULL); - -//----------------------------------------------------------------------------- -// [SECTION] Demo -//----------------------------------------------------------------------------- - -// Shows the ImPlot demo window (add implot_demo.cpp to your sources!) -IMPLOT_API void ShowDemoWindow(bool* p_open = NULL); - -} // namespace ImPlot - -//----------------------------------------------------------------------------- -// [SECTION] Obsolete API -//----------------------------------------------------------------------------- - -// The following functions will be removed! Keep your copy of implot up to date! -// Occasionally set '#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS' to stay ahead. -// If you absolutely must use these functions and do not want to receive compiler -// warnings, set '#define IMPLOT_DISABLE_OBSOLETE_WARNINGS'. - -#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS - -#ifndef IMPLOT_DISABLE_DEPRECATED_WARNINGS -#if __cplusplus > 201402L -#define IMPLOT_DEPRECATED(method) [[deprecated]] method -#elif defined( __GNUC__ ) && !defined( __INTEL_COMPILER ) && ( __GNUC__ > 3 || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) ) -#define IMPLOT_DEPRECATED(method) method __attribute__( ( deprecated ) ) -#elif defined( _MSC_VER ) -#define IMPLOT_DEPRECATED(method) __declspec(deprecated) method -#else -#define IMPLOT_DEPRECATED(method) method -#endif -#else -#define IMPLOT_DEPRECATED(method) method -#endif - -enum ImPlotFlagsObsolete_ { - ImPlotFlags_YAxis2 = 1 << 20, - ImPlotFlags_YAxis3 = 1 << 21, -}; - -namespace ImPlot { - -// OBSOLETED in v0.13 -> PLANNED REMOVAL in v1.0 -IMPLOT_DEPRECATED( IMPLOT_API bool BeginPlot(const char* title_id, - const char* x_label, // = NULL, - const char* y_label, // = NULL, - const ImVec2& size = ImVec2(-1,0), - ImPlotFlags flags = ImPlotFlags_None, - ImPlotAxisFlags x_flags = 0, - ImPlotAxisFlags y_flags = 0, - ImPlotAxisFlags y2_flags = ImPlotAxisFlags_AuxDefault, - ImPlotAxisFlags y3_flags = ImPlotAxisFlags_AuxDefault, - const char* y2_label = NULL, - const char* y3_label = NULL) ); - -} // namespace ImPlot - -#endif diff --git a/libs/zgui/libs/imgui/implot_demo.cpp b/libs/zgui/libs/imgui/implot_demo.cpp deleted file mode 100644 index a52ea84da..000000000 --- a/libs/zgui/libs/imgui/implot_demo.cpp +++ /dev/null @@ -1,2446 +0,0 @@ -// MIT License - -// Copyright (c) 2022 Evan Pezent - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// ImPlot v0.14 - -// We define this so that the demo does not accidentally use deprecated API -#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS -#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS -#endif - -#include "implot.h" -#include -#include -#include -#include - -#ifdef _MSC_VER -#define sprintf sprintf_s -#endif - -#ifndef PI -#define PI 3.14159265358979323846 -#endif - -#define CHECKBOX_FLAG(flags, flag) ImGui::CheckboxFlags(#flag, (unsigned int*)&flags, flag) - -// Encapsulates examples for customizing ImPlot. -namespace MyImPlot { - -// Example for Custom Data and Getters section. -struct Vector2f { - Vector2f(float _x, float _y) { x = _x; y = _y; } - float x, y; -}; - -// Example for Custom Data and Getters section. -struct WaveData { - double X, Amp, Freq, Offset; - WaveData(double x, double amp, double freq, double offset) { X = x; Amp = amp; Freq = freq; Offset = offset; } -}; -ImPlotPoint SineWave(int idx, void* wave_data); -ImPlotPoint SawWave(int idx, void* wave_data); -ImPlotPoint Spiral(int idx, void* wave_data); - -// Example for Tables section. -void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size); - -// Example for Custom Plotters and Tooltips section. -void PlotCandlestick(const char* label_id, const double* xs, const double* opens, const double* closes, const double* lows, const double* highs, int count, bool tooltip = true, float width_percent = 0.25f, ImVec4 bullCol = ImVec4(0,1,0,1), ImVec4 bearCol = ImVec4(1,0,0,1)); - -// Example for Custom Styles section. -void StyleSeaborn(); - -} // namespace MyImPlot - -namespace ImPlot { - -template -inline T RandomRange(T min, T max) { - T scale = rand() / (T) RAND_MAX; - return min + scale * ( max - min ); -} - -ImVec4 RandomColor() { - ImVec4 col; - col.x = RandomRange(0.0f,1.0f); - col.y = RandomRange(0.0f,1.0f); - col.z = RandomRange(0.0f,1.0f); - col.w = 1.0f; - return col; -} - -double RandomGauss() { - static double V1, V2, S; - static int phase = 0; - double X; - if(phase == 0) { - do { - double U1 = (double)rand() / RAND_MAX; - double U2 = (double)rand() / RAND_MAX; - V1 = 2 * U1 - 1; - V2 = 2 * U2 - 1; - S = V1 * V1 + V2 * V2; - } while(S >= 1 || S == 0); - - X = V1 * sqrt(-2 * log(S) / S); - } else - X = V2 * sqrt(-2 * log(S) / S); - phase = 1 - phase; - return X; -} - -template -struct NormalDistribution { - NormalDistribution(double mean, double sd) { - for (int i = 0; i < N; ++i) - Data[i] = RandomGauss()*sd + mean; - } - double Data[N]; -}; - -// utility structure for realtime plot -struct ScrollingBuffer { - int MaxSize; - int Offset; - ImVector Data; - ScrollingBuffer(int max_size = 2000) { - MaxSize = max_size; - Offset = 0; - Data.reserve(MaxSize); - } - void AddPoint(float x, float y) { - if (Data.size() < MaxSize) - Data.push_back(ImVec2(x,y)); - else { - Data[Offset] = ImVec2(x,y); - Offset = (Offset + 1) % MaxSize; - } - } - void Erase() { - if (Data.size() > 0) { - Data.shrink(0); - Offset = 0; - } - } -}; - -// utility structure for realtime plot -struct RollingBuffer { - float Span; - ImVector Data; - RollingBuffer() { - Span = 10.0f; - Data.reserve(2000); - } - void AddPoint(float x, float y) { - float xmod = fmodf(x, Span); - if (!Data.empty() && xmod < Data.back().x) - Data.shrink(0); - Data.push_back(ImVec2(xmod, y)); - } -}; - -// Huge data used by Time Formatting example (~500 MB allocation!) -struct HugeTimeData { - HugeTimeData(double min) { - Ts = new double[Size]; - Ys = new double[Size]; - for (int i = 0; i < Size; ++i) { - Ts[i] = min + i; - Ys[i] = GetY(Ts[i]); - } - } - ~HugeTimeData() { delete[] Ts; delete[] Ys; } - static double GetY(double t) { - return 0.5 + 0.25 * sin(t/86400/12) + 0.005 * sin(t/3600); - } - double* Ts; - double* Ys; - static const int Size = 60*60*24*366; -}; - -//----------------------------------------------------------------------------- -// [SECTION] Demo Functions -//----------------------------------------------------------------------------- - -void Demo_Help() { - ImGui::Text("ABOUT THIS DEMO:"); - ImGui::BulletText("Sections below are demonstrating many aspects of the library."); - ImGui::BulletText("The \"Tools\" menu above gives access to: Style Editors (ImPlot/ImGui)\n" - "and Metrics (general purpose Dear ImGui debugging tool)."); - ImGui::Separator(); - ImGui::Text("PROGRAMMER GUIDE:"); - ImGui::BulletText("See the ShowDemoWindow() code in implot_demo.cpp. <- you are here!"); - ImGui::BulletText("If you see visual artifacts, do one of the following:"); - ImGui::Indent(); - ImGui::BulletText("Handle ImGuiBackendFlags_RendererHasVtxOffset for 16-bit indices in your backend."); - ImGui::BulletText("Or, enable 32-bit indices in imconfig.h."); - ImGui::BulletText("Your current configuration is:"); - ImGui::Indent(); - ImGui::BulletText("ImDrawIdx: %d-bit", (int)(sizeof(ImDrawIdx) * 8)); - ImGui::BulletText("ImGuiBackendFlags_RendererHasVtxOffset: %s", (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ? "True" : "False"); - ImGui::Unindent(); - ImGui::Unindent(); - ImGui::Separator(); - ImGui::Text("USER GUIDE:"); - ShowUserGuide(); -} - -//----------------------------------------------------------------------------- - -void ButtonSelector(const char* label, ImGuiMouseButton* b) { - ImGui::PushID(label); - if (ImGui::RadioButton("LMB",*b == ImGuiMouseButton_Left)) - *b = ImGuiMouseButton_Left; - ImGui::SameLine(); - if (ImGui::RadioButton("RMB",*b == ImGuiMouseButton_Right)) - *b = ImGuiMouseButton_Right; - ImGui::SameLine(); - if (ImGui::RadioButton("MMB",*b == ImGuiMouseButton_Middle)) - *b = ImGuiMouseButton_Middle; - ImGui::PopID(); -} - -void ModSelector(const char* label, ImGuiModFlags* k) { - ImGui::PushID(label); - ImGui::CheckboxFlags("Ctrl", (unsigned int*)k, ImGuiModFlags_Ctrl); ImGui::SameLine(); - ImGui::CheckboxFlags("Shift", (unsigned int*)k, ImGuiModFlags_Shift); ImGui::SameLine(); - ImGui::CheckboxFlags("Alt", (unsigned int*)k, ImGuiModFlags_Alt); ImGui::SameLine(); - ImGui::CheckboxFlags("Super", (unsigned int*)k, ImGuiModFlags_Super); - ImGui::PopID(); -} - -void InputMapping(const char* label, ImGuiMouseButton* b, ImGuiModFlags* k) { - ImGui::LabelText("##","%s",label); - if (b != NULL) { - ImGui::SameLine(100); - ButtonSelector(label,b); - } - if (k != NULL) { - ImGui::SameLine(300); - ModSelector(label,k); - } -} - -void ShowInputMapping() { - ImPlotInputMap& map = ImPlot::GetInputMap(); - InputMapping("Pan",&map.Pan,&map.PanMod); - InputMapping("Fit",&map.Fit,NULL); - InputMapping("Select",&map.Select,&map.SelectMod); - InputMapping("SelectHorzMod",NULL,&map.SelectHorzMod); - InputMapping("SelectVertMod",NULL,&map.SelectVertMod); - InputMapping("SelectCancel",&map.SelectCancel,NULL); - InputMapping("Menu",&map.Menu,NULL); - InputMapping("OverrideMod",NULL,&map.OverrideMod); - InputMapping("ZoomMod",NULL,&map.ZoomMod); - ImGui::SliderFloat("ZoomRate",&map.ZoomRate,-1,1); -} - -void Demo_Config() { - ImGui::ShowFontSelector("Font"); - ImGui::ShowStyleSelector("ImGui Style"); - ImPlot::ShowStyleSelector("ImPlot Style"); - ImPlot::ShowColormapSelector("ImPlot Colormap"); - ImPlot::ShowInputMapSelector("Input Map"); - ImGui::Separator(); - ImGui::Checkbox("Use Local Time", &ImPlot::GetStyle().UseLocalTime); - ImGui::Checkbox("Use ISO 8601", &ImPlot::GetStyle().UseISO8601); - ImGui::Checkbox("Use 24 Hour Clock", &ImPlot::GetStyle().Use24HourClock); - ImGui::Separator(); - if (ImPlot::BeginPlot("Preview")) { - static double now = (double)time(0); - ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); - ImPlot::SetupAxisLimits(ImAxis_X1, now, now + 24*3600); - for (int i = 0; i < 10; ++i) { - double x[2] = {now, now + 24*3600}; - double y[2] = {0,i/9.0}; - ImGui::PushID(i); - ImPlot::PlotLine("##Line",x,y,2); - ImGui::PopID(); - } - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_LinePlots() { - static float xs1[1001], ys1[1001]; - for (int i = 0; i < 1001; ++i) { - xs1[i] = i * 0.001f; - ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)ImGui::GetTime() / 10)); - } - static double xs2[20], ys2[20]; - for (int i = 0; i < 20; ++i) { - xs2[i] = i * 1/19.0f; - ys2[i] = xs2[i] * xs2[i]; - } - if (ImPlot::BeginPlot("Line Plots")) { - ImPlot::SetupAxes("x","y"); - ImPlot::PlotLine("f(x)", xs1, ys1, 1001); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); - ImPlot::PlotLine("g(x)", xs2, ys2, 20,ImPlotLineFlags_Segments); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_FilledLinePlots() { - static double xs1[101], ys1[101], ys2[101], ys3[101]; - srand(0); - for (int i = 0; i < 101; ++i) { - xs1[i] = (float)i; - ys1[i] = RandomRange(400.0,450.0); - ys2[i] = RandomRange(275.0,350.0); - ys3[i] = RandomRange(150.0,225.0); - } - static bool show_lines = true; - static bool show_fills = true; - static float fill_ref = 0; - static int shade_mode = 0; - static ImPlotShadedFlags flags = 0; - ImGui::Checkbox("Lines",&show_lines); ImGui::SameLine(); - ImGui::Checkbox("Fills",&show_fills); - if (show_fills) { - ImGui::SameLine(); - if (ImGui::RadioButton("To -INF",shade_mode == 0)) - shade_mode = 0; - ImGui::SameLine(); - if (ImGui::RadioButton("To +INF",shade_mode == 1)) - shade_mode = 1; - ImGui::SameLine(); - if (ImGui::RadioButton("To Ref",shade_mode == 2)) - shade_mode = 2; - if (shade_mode == 2) { - ImGui::SameLine(); - ImGui::SetNextItemWidth(100); - ImGui::DragFloat("##Ref",&fill_ref, 1, -100, 500); - } - } - - if (ImPlot::BeginPlot("Stock Prices")) { - ImPlot::SetupAxes("Days","Price"); - ImPlot::SetupAxesLimits(0,100,0,500); - if (show_fills) { - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); - ImPlot::PlotShaded("Stock 1", xs1, ys1, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); - ImPlot::PlotShaded("Stock 2", xs1, ys2, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); - ImPlot::PlotShaded("Stock 3", xs1, ys3, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); - ImPlot::PopStyleVar(); - } - if (show_lines) { - ImPlot::PlotLine("Stock 1", xs1, ys1, 101); - ImPlot::PlotLine("Stock 2", xs1, ys2, 101); - ImPlot::PlotLine("Stock 3", xs1, ys3, 101); - } - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_ShadedPlots() { - static float xs[1001], ys[1001], ys1[1001], ys2[1001], ys3[1001], ys4[1001]; - srand(0); - for (int i = 0; i < 1001; ++i) { - xs[i] = i * 0.001f; - ys[i] = 0.25f + 0.25f * sinf(25 * xs[i]) * sinf(5 * xs[i]) + RandomRange(-0.01f, 0.01f); - ys1[i] = ys[i] + RandomRange(0.1f, 0.12f); - ys2[i] = ys[i] - RandomRange(0.1f, 0.12f); - ys3[i] = 0.75f + 0.2f * sinf(25 * xs[i]); - ys4[i] = 0.75f + 0.1f * cosf(25 * xs[i]); - } - static float alpha = 0.25f; - ImGui::DragFloat("Alpha",&alpha,0.01f,0,1); - - if (ImPlot::BeginPlot("Shaded Plots")) { - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); - ImPlot::PlotShaded("Uncertain Data",xs,ys1,ys2,1001); - ImPlot::PlotLine("Uncertain Data", xs, ys, 1001); - ImPlot::PlotShaded("Overlapping",xs,ys3,ys4,1001); - ImPlot::PlotLine("Overlapping",xs,ys3,1001); - ImPlot::PlotLine("Overlapping",xs,ys4,1001); - ImPlot::PopStyleVar(); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_ScatterPlots() { - srand(0); - static float xs1[100], ys1[100]; - for (int i = 0; i < 100; ++i) { - xs1[i] = i * 0.01f; - ys1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX); - } - static float xs2[50], ys2[50]; - for (int i = 0; i < 50; i++) { - xs2[i] = 0.25f + 0.2f * ((float)rand() / (float)RAND_MAX); - ys2[i] = 0.75f + 0.2f * ((float)rand() / (float)RAND_MAX); - } - - if (ImPlot::BeginPlot("Scatter Plot")) { - ImPlot::PlotScatter("Data 1", xs1, ys1, 100); - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImPlot::GetColormapColor(1), IMPLOT_AUTO, ImPlot::GetColormapColor(1)); - ImPlot::PlotScatter("Data 2", xs2, ys2, 50); - ImPlot::PopStyleVar(); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_StairstepPlots() { - static float ys1[21], ys2[21]; - for (int i = 0; i < 21; ++i) { - ys1[i] = 0.75f + 0.2f * sinf(10 * i * 0.05f); - ys2[i] = 0.25f + 0.2f * sinf(10 * i * 0.05f); - } - static ImPlotStairsFlags flags = 0; - CHECKBOX_FLAG(flags, ImPlotStairsFlags_Shaded); - if (ImPlot::BeginPlot("Stairstep Plot")) { - ImPlot::SetupAxes("x","f(x)"); - ImPlot::SetupAxesLimits(0,1,0,1); - - ImPlot::PushStyleColor(ImPlotCol_Line, ImVec4(0.5f,0.5f,0.5f,1.0f)); - ImPlot::PlotLine("##1",ys1,21,0.05f); - ImPlot::PlotLine("##2",ys2,21,0.05f); - ImPlot::PopStyleColor(); - - ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.25f); - ImPlot::PlotStairs("Post Step (default)", ys1, 21, 0.05f, 0, flags); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.25f); - ImPlot::PlotStairs("Pre Step", ys2, 21, 0.05f, 0, flags|ImPlotStairsFlags_PreStep); - - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_BarPlots() { - static ImS8 data[10] = {1,2,3,4,5,6,7,8,9,10}; - if (ImPlot::BeginPlot("Bar Plot")) { - ImPlot::PlotBars("Vertical",data,10,0.7,1); - ImPlot::PlotBars("Horizontal",data,10,0.4,1,ImPlotBarsFlags_Horizontal); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_BarGroups() { - static ImS8 data[30] = {83, 67, 23, 89, 83, 78, 91, 82, 85, 90, // midterm - 80, 62, 56, 99, 55, 78, 88, 78, 90, 100, // final - 80, 69, 52, 92, 72, 78, 75, 76, 89, 95}; // course - - static const char* ilabels[] = {"Midterm Exam","Final Exam","Course Grade"}; - static const char* glabels[] = {"S1","S2","S3","S4","S5","S6","S7","S8","S9","S10"}; - static const double positions[] = {0,1,2,3,4,5,6,7,8,9}; - - static int items = 3; - static int groups = 10; - static float size = 0.67f; - - static ImPlotBarGroupsFlags flags = 0; - static bool horz = false; - - ImGui::CheckboxFlags("Stacked", (unsigned int*)&flags, ImPlotBarGroupsFlags_Stacked); - ImGui::SameLine(); - ImGui::Checkbox("Horizontal",&horz); - - ImGui::SliderInt("Items",&items,1,3); - ImGui::SliderFloat("Size",&size,0,1); - - if (ImPlot::BeginPlot("Bar Group")) { - ImPlot::SetupLegend(ImPlotLocation_East, ImPlotLegendFlags_Outside); - if (horz) { - ImPlot::SetupAxes("Score","Student",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); - ImPlot::SetupAxisTicks(ImAxis_Y1,positions, groups, glabels); - ImPlot::PlotBarGroups(ilabels,data,items,groups,size,0,flags|ImPlotBarGroupsFlags_Horizontal); - } - else { - ImPlot::SetupAxes("Student","Score",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); - ImPlot::SetupAxisTicks(ImAxis_X1,positions, groups, glabels); - ImPlot::PlotBarGroups(ilabels,data,items,groups,size,0,flags); - } - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_BarStacks() { - - static ImPlotColormap Liars = -1; - if (Liars == -1) { - static const ImU32 Liars_Data[6] = { 4282515870, 4282609140, 4287357182, 4294630301, 4294945280, 4294921472 }; - Liars = ImPlot::AddColormap("Liars", Liars_Data, 6); - } - - static bool diverging = true; - ImGui::Checkbox("Diverging",&diverging); - - static const char* politicians[] = {"Trump","Bachman","Cruz","Gingrich","Palin","Santorum","Walker","Perry","Ryan","McCain","Rubio","Romney","Rand Paul","Christie","Biden","Kasich","Sanders","J Bush","H Clinton","Obama"}; - static int data_reg[] = {18,26,7,14,10,8,6,11,4,4,3,8,6,8,6,5,0,3,1,2, // Pants on Fire - 43,36,30,21,30,27,25,17,11,22,15,16,16,17,12,12,14,6,13,12, // False - 16,13,28,22,15,21,15,18,30,17,24,18,13,10,14,15,17,22,14,12, // Mostly False - 17,10,13,25,12,22,19,26,23,17,22,27,20,26,29,17,18,22,21,27, // Half True - 5,7,16,10,10,12,23,13,17,20,22,16,23,19,20,26,36,29,27,26, // Mostly True - 1,8,6,8,23,10,12,15,15,20,14,15,22,20,19,25,15,18,24,21}; // True - static const char* labels_reg[] = {"Pants on Fire","False","Mostly False","Half True","Mostly True","True"}; - - - static int data_div[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // Pants on Fire (dummy, to order legend logically) - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // False (dummy, to order legend logically) - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // Mostly False (dummy, to order legend logically) - -16,-13,-28,-22,-15,-21,-15,-18,-30,-17,-24,-18,-13,-10,-14,-15,-17,-22,-14,-12, // Mostly False - -43,-36,-30,-21,-30,-27,-25,-17,-11,-22,-15,-16,-16,-17,-12,-12,-14,-6,-13,-12, // False - -18,-26,-7,-14,-10,-8,-6,-11,-4,-4,-3,-8,-6,-8,-6,-5,0,-3,-1,-2, // Pants on Fire - 17,10,13,25,12,22,19,26,23,17,22,27,20,26,29,17,18,22,21,27, // Half True - 5,7,16,10,10,12,23,13,17,20,22,16,23,19,20,26,36,29,27,26, // Mostly True - 1,8,6,8,23,10,12,15,15,20,14,15,22,20,19,25,15,18,24,21}; // True - static const char* labels_div[] = {"Pants on Fire","False","Mostly False","Mostly False","False","Pants on Fire","Half True","Mostly True","True"}; - - ImPlot::PushColormap(Liars); - if (ImPlot::BeginPlot("PolitiFact: Who Lies More?",ImVec2(-1,400),ImPlotFlags_NoMouseText)) { - ImPlot::SetupLegend(ImPlotLocation_South, ImPlotLegendFlags_Outside|ImPlotLegendFlags_Horizontal); - ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Invert); - ImPlot::SetupAxisTicks(ImAxis_Y1,0,19,20,politicians,false); - if (diverging) - ImPlot::PlotBarGroups(labels_div,data_div,9,20,0.75,0,ImPlotBarGroupsFlags_Stacked|ImPlotBarGroupsFlags_Horizontal); - else - ImPlot::PlotBarGroups(labels_reg,data_reg,6,20,0.75,0,ImPlotBarGroupsFlags_Stacked|ImPlotBarGroupsFlags_Horizontal); - ImPlot::EndPlot(); - } - ImPlot::PopColormap(); -} - -//----------------------------------------------------------------------------- - -void Demo_ErrorBars() { - static float xs[5] = {1,2,3,4,5}; - static float bar[5] = {1,2,5,3,4}; - static float lin1[5] = {8,8,9,7,8}; - static float lin2[5] = {6,7,6,9,6}; - static float err1[5] = {0.2f, 0.4f, 0.2f, 0.6f, 0.4f}; - static float err2[5] = {0.4f, 0.2f, 0.4f, 0.8f, 0.6f}; - static float err3[5] = {0.09f, 0.14f, 0.09f, 0.12f, 0.16f}; - static float err4[5] = {0.02f, 0.08f, 0.15f, 0.05f, 0.2f}; - - - if (ImPlot::BeginPlot("##ErrorBars")) { - ImPlot::SetupAxesLimits(0, 6, 0, 10); - ImPlot::PlotBars("Bar", xs, bar, 5, 0.5f); - ImPlot::PlotErrorBars("Bar", xs, bar, err1, 5); - ImPlot::SetNextErrorBarStyle(ImPlot::GetColormapColor(1), 0); - ImPlot::PlotErrorBars("Line", xs, lin1, err1, err2, 5); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); - ImPlot::PlotLine("Line", xs, lin1, 5); - ImPlot::PushStyleColor(ImPlotCol_ErrorBar, ImPlot::GetColormapColor(2)); - ImPlot::PlotErrorBars("Scatter", xs, lin2, err2, 5); - ImPlot::PlotErrorBars("Scatter", xs, lin2, err3, err4, 5, ImPlotErrorBarsFlags_Horizontal); - ImPlot::PopStyleColor(); - ImPlot::PlotScatter("Scatter", xs, lin2, 5); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_StemPlots() { - static double xs[51], ys1[51], ys2[51]; - for (int i = 0; i < 51; ++i) { - xs[i] = i * 0.02; - ys1[i] = 1.0 + 0.5 * sin(25*xs[i])*cos(2*xs[i]); - ys2[i] = 0.5 + 0.25 * sin(10*xs[i]) * sin(xs[i]); - } - if (ImPlot::BeginPlot("Stem Plots")) { - ImPlot::SetupAxisLimits(ImAxis_X1,0,1.0); - ImPlot::SetupAxisLimits(ImAxis_Y1,0,1.6); - ImPlot::PlotStems("Stems 1",xs,ys1,51); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); - ImPlot::PlotStems("Stems 2", xs, ys2,51); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_InfiniteLines() { - static double vals[] = {0.25, 0.5, 0.75}; - if (ImPlot::BeginPlot("##Infinite")) { - ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoInitialFit,ImPlotAxisFlags_NoInitialFit); - ImPlot::PlotInfLines("Vertical",vals,3); - ImPlot::PlotInfLines("Horizontal",vals,3,ImPlotInfLinesFlags_Horizontal); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_PieCharts() { - static const char* labels1[] = {"Frogs","Hogs","Dogs","Logs"}; - static float data1[] = {0.15f, 0.30f, 0.2f, 0.05f}; - static ImPlotPieChartFlags flags = 0; - ImGui::SetNextItemWidth(250); - ImGui::DragFloat4("Values", data1, 0.01f, 0, 1); - if ((data1[0] + data1[1] + data1[2] + data1[3]) < 1) { - ImGui::SameLine(); - CHECKBOX_FLAG(flags,ImPlotPieChartFlags_Normalize); - } - - if (ImPlot::BeginPlot("##Pie1", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { - ImPlot::SetupAxes(NULL, NULL, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); - ImPlot::SetupAxesLimits(0, 1, 0, 1); - ImPlot::PlotPieChart(labels1, data1, 4, 0.5, 0.5, 0.4, "%.2f", 90, flags); - ImPlot::EndPlot(); - } - - ImGui::SameLine(); - - static const char* labels2[] = {"A","B","C","D","E"}; - static int data2[] = {1,1,2,3,5}; - - ImPlot::PushColormap(ImPlotColormap_Pastel); - if (ImPlot::BeginPlot("##Pie2", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { - ImPlot::SetupAxes(NULL, NULL, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); - ImPlot::SetupAxesLimits(0, 1, 0, 1); - ImPlot::PlotPieChart(labels2, data2, 5, 0.5, 0.5, 0.4, "%.0f", 180, flags); - ImPlot::EndPlot(); - } - ImPlot::PopColormap(); -} - -//----------------------------------------------------------------------------- - -void Demo_Heatmaps() { - static float values1[7][7] = {{0.8f, 2.4f, 2.5f, 3.9f, 0.0f, 4.0f, 0.0f}, - {2.4f, 0.0f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f}, - {1.1f, 2.4f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f}, - {0.6f, 0.0f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f}, - {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f}, - {1.3f, 1.2f, 0.0f, 0.0f, 0.0f, 3.2f, 5.1f}, - {0.1f, 2.0f, 0.0f, 1.4f, 0.0f, 1.9f, 6.3f}}; - static float scale_min = 0; - static float scale_max = 6.3f; - static const char* xlabels[] = {"C1","C2","C3","C4","C5","C6","C7"}; - static const char* ylabels[] = {"R1","R2","R3","R4","R5","R6","R7"}; - - static ImPlotColormap map = ImPlotColormap_Viridis; - if (ImPlot::ColormapButton(ImPlot::GetColormapName(map),ImVec2(225,0),map)) { - map = (map + 1) % ImPlot::GetColormapCount(); - // We bust the color cache of our plots so that item colors will - // resample the new colormap in the event that they have already - // been created. See documentation in implot.h. - BustColorCache("##Heatmap1"); - BustColorCache("##Heatmap2"); - } - - ImGui::SameLine(); - ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); - ImGui::SetNextItemWidth(225); - ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20); - - static ImPlotHeatmapFlags hm_flags = 0; - - ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); - - static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; - - ImPlot::PushColormap(map); - - if (ImPlot::BeginPlot("##Heatmap1",ImVec2(225,225),ImPlotFlags_NoLegend|ImPlotFlags_NoMouseText)) { - ImPlot::SetupAxes(NULL, NULL, axes_flags, axes_flags); - ImPlot::SetupAxisTicks(ImAxis_X1,0 + 1.0/14.0, 1 - 1.0/14.0, 7, xlabels); - ImPlot::SetupAxisTicks(ImAxis_Y1,1 - 1.0/14.0, 0 + 1.0/14.0, 7, ylabels); - ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max,"%g",ImPlotPoint(0,0),ImPlotPoint(1,1),hm_flags); - ImPlot::EndPlot(); - } - ImGui::SameLine(); - ImPlot::ColormapScale("##HeatScale",scale_min, scale_max, ImVec2(60,225)); - - ImGui::SameLine(); - - const int size = 80; - static double values2[size*size]; - srand((unsigned int)(ImGui::GetTime()*1000000)); - for (int i = 0; i < size*size; ++i) - values2[i] = RandomRange(0.0,1.0); - - if (ImPlot::BeginPlot("##Heatmap2",ImVec2(225,225))) { - ImPlot::SetupAxes(NULL, NULL, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); - ImPlot::SetupAxesLimits(-1,1,-1,1); - ImPlot::PlotHeatmap("heat1",values2,size,size,0,1,NULL); - ImPlot::PlotHeatmap("heat2",values2,size,size,0,1,NULL, ImPlotPoint(-1,-1), ImPlotPoint(0,0)); - ImPlot::EndPlot(); - } - ImPlot::PopColormap(); - -} - -//----------------------------------------------------------------------------- - -void Demo_Histogram() { - static ImPlotHistogramFlags hist_flags = ImPlotHistogramFlags_Density; - static int bins = 50; - static double mu = 5; - static double sigma = 2; - ImGui::SetNextItemWidth(200); - if (ImGui::RadioButton("Sqrt",bins==ImPlotBin_Sqrt)) { bins = ImPlotBin_Sqrt; } ImGui::SameLine(); - if (ImGui::RadioButton("Sturges",bins==ImPlotBin_Sturges)) { bins = ImPlotBin_Sturges; } ImGui::SameLine(); - if (ImGui::RadioButton("Rice",bins==ImPlotBin_Rice)) { bins = ImPlotBin_Rice; } ImGui::SameLine(); - if (ImGui::RadioButton("Scott",bins==ImPlotBin_Scott)) { bins = ImPlotBin_Scott; } ImGui::SameLine(); - if (ImGui::RadioButton("N Bins",bins>=0)) { bins = 50; } - if (bins>=0) { - ImGui::SameLine(); - ImGui::SetNextItemWidth(200); - ImGui::SliderInt("##Bins", &bins, 1, 100); - } - ImGui::CheckboxFlags("Horizontal", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Horizontal); - ImGui::SameLine(); - ImGui::CheckboxFlags("Density", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Density); - ImGui::SameLine(); - ImGui::CheckboxFlags("Cumulative", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Cumulative); - - static bool range = false; - ImGui::Checkbox("Range", &range); - static float rmin = -3; - static float rmax = 13; - if (range) { - ImGui::SameLine(); - ImGui::SetNextItemWidth(200); - ImGui::DragFloat2("##Range",&rmin,0.1f,-3,13); - ImGui::SameLine(); - ImGui::CheckboxFlags("Exclude Outliers", (unsigned int*)&hist_flags, ImPlotHistogramFlags_NoOutliers); - } - static NormalDistribution<10000> dist(mu, sigma); - static double x[100]; - static double y[100]; - if (hist_flags & ImPlotHistogramFlags_Density) { - for (int i = 0; i < 100; ++i) { - x[i] = -3 + 16 * (double)i/99.0; - y[i] = exp( - (x[i]-mu)*(x[i]-mu) / (2*sigma*sigma)) / (sigma * sqrt(2*3.141592653589793238)); - } - if (hist_flags & ImPlotHistogramFlags_Cumulative) { - for (int i = 1; i < 100; ++i) - y[i] += y[i-1]; - for (int i = 0; i < 100; ++i) - y[i] /= y[99]; - } - } - - if (ImPlot::BeginPlot("##Histograms")) { - ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); - ImPlot::PlotHistogram("Empirical", dist.Data, 10000, bins, 1.0, range ? ImPlotRange(rmin,rmax) : ImPlotRange(), hist_flags); - if ((hist_flags & ImPlotHistogramFlags_Density) && !(hist_flags & ImPlotHistogramFlags_NoOutliers)) { - if (hist_flags & ImPlotHistogramFlags_Horizontal) - ImPlot::PlotLine("Theoretical",y,x,100); - else - ImPlot::PlotLine("Theoretical",x,y,100); - } - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_Histogram2D() { - static int count = 50000; - static int xybins[2] = {100,100}; - - static ImPlotHistogramFlags hist_flags = 0; - - ImGui::SliderInt("Count",&count,100,100000); - ImGui::SliderInt2("Bins",xybins,1,500); - ImGui::SameLine(); - ImGui::CheckboxFlags("Density", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Density); - - static NormalDistribution<100000> dist1(1, 2); - static NormalDistribution<100000> dist2(1, 1); - double max_count = 0; - ImPlotAxisFlags flags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Foreground; - ImPlot::PushColormap("Hot"); - if (ImPlot::BeginPlot("##Hist2D",ImVec2(ImGui::GetContentRegionAvail().x-100-ImGui::GetStyle().ItemSpacing.x,0))) { - ImPlot::SetupAxes(NULL, NULL, flags, flags); - ImPlot::SetupAxesLimits(-6,6,-6,6); - max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],ImPlotRect(-6,6,-6,6), hist_flags); - ImPlot::EndPlot(); - } - ImGui::SameLine(); - ImPlot::ColormapScale(hist_flags & ImPlotHistogramFlags_Density ? "Density" : "Count",0,max_count,ImVec2(100,0)); - ImPlot::PopColormap(); -} - -//----------------------------------------------------------------------------- - -void Demo_DigitalPlots() { - ImGui::BulletText("Digital plots do not respond to Y drag and zoom, so that"); - ImGui::Indent(); - ImGui::Text("you can drag analog plots over the rising/falling digital edge."); - ImGui::Unindent(); - - static bool paused = false; - static ScrollingBuffer dataDigital[2]; - static ScrollingBuffer dataAnalog[2]; - static bool showDigital[2] = {true, false}; - static bool showAnalog[2] = {true, false}; - - char label[32]; - ImGui::Checkbox("digital_0", &showDigital[0]); ImGui::SameLine(); - ImGui::Checkbox("digital_1", &showDigital[1]); ImGui::SameLine(); - ImGui::Checkbox("analog_0", &showAnalog[0]); ImGui::SameLine(); - ImGui::Checkbox("analog_1", &showAnalog[1]); - - static float t = 0; - if (!paused) { - t += ImGui::GetIO().DeltaTime; - //digital signal values - if (showDigital[0]) - dataDigital[0].AddPoint(t, sinf(2*t) > 0.45); - if (showDigital[1]) - dataDigital[1].AddPoint(t, sinf(2*t) < 0.45); - //Analog signal values - if (showAnalog[0]) - dataAnalog[0].AddPoint(t, sinf(2*t)); - if (showAnalog[1]) - dataAnalog[1].AddPoint(t, cosf(2*t)); - } - if (ImPlot::BeginPlot("##Digital")) { - ImPlot::SetupAxisLimits(ImAxis_X1, t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always); - ImPlot::SetupAxisLimits(ImAxis_Y1, -1, 1); - for (int i = 0; i < 2; ++i) { - if (showDigital[i] && dataDigital[i].Data.size() > 0) { - sprintf(label, "digital_%d", i); - ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), 0, dataDigital[i].Offset, 2 * sizeof(float)); - } - } - for (int i = 0; i < 2; ++i) { - if (showAnalog[i]) { - sprintf(label, "analog_%d", i); - if (dataAnalog[i].Data.size() > 0) - ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), 0, dataAnalog[i].Offset, 2 * sizeof(float)); - } - } - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_Images() { - ImGui::BulletText("Below we are displaying the font texture, which is the only texture we have\naccess to in this demo."); - ImGui::BulletText("Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\nown texture data."); - ImGui::BulletText("See ImGui Wiki page 'Image Loading and Displaying Examples'."); - static ImVec2 bmin(0,0); - static ImVec2 bmax(1,1); - static ImVec2 uv0(0,0); - static ImVec2 uv1(1,1); - static ImVec4 tint(1,1,1,1); - ImGui::SliderFloat2("Min", &bmin.x, -2, 2, "%.1f"); - ImGui::SliderFloat2("Max", &bmax.x, -2, 2, "%.1f"); - ImGui::SliderFloat2("UV0", &uv0.x, -2, 2, "%.1f"); - ImGui::SliderFloat2("UV1", &uv1.x, -2, 2, "%.1f"); - ImGui::ColorEdit4("Tint",&tint.x); - if (ImPlot::BeginPlot("##image")) { - ImPlot::PlotImage("my image",ImGui::GetIO().Fonts->TexID, bmin, bmax, uv0, uv1, tint); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_RealtimePlots() { - ImGui::BulletText("Move your mouse to change the data!"); - ImGui::BulletText("This example assumes 60 FPS. Higher FPS requires larger buffer size."); - static ScrollingBuffer sdata1, sdata2; - static RollingBuffer rdata1, rdata2; - ImVec2 mouse = ImGui::GetMousePos(); - static float t = 0; - t += ImGui::GetIO().DeltaTime; - sdata1.AddPoint(t, mouse.x * 0.0005f); - rdata1.AddPoint(t, mouse.x * 0.0005f); - sdata2.AddPoint(t, mouse.y * 0.0005f); - rdata2.AddPoint(t, mouse.y * 0.0005f); - - static float history = 10.0f; - ImGui::SliderFloat("History",&history,1,30,"%.1f s"); - rdata1.Span = history; - rdata2.Span = history; - - static ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels; - - if (ImPlot::BeginPlot("##Scrolling", ImVec2(-1,150))) { - ImPlot::SetupAxes(NULL, NULL, flags, flags); - ImPlot::SetupAxisLimits(ImAxis_X1,t - history, t, ImGuiCond_Always); - ImPlot::SetupAxisLimits(ImAxis_Y1,0,1); - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); - ImPlot::PlotShaded("Mouse X", &sdata1.Data[0].x, &sdata1.Data[0].y, sdata1.Data.size(), -INFINITY, 0, sdata1.Offset, 2 * sizeof(float)); - ImPlot::PlotLine("Mouse Y", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), 0, sdata2.Offset, 2*sizeof(float)); - ImPlot::EndPlot(); - } - if (ImPlot::BeginPlot("##Rolling", ImVec2(-1,150))) { - ImPlot::SetupAxes(NULL, NULL, flags, flags); - ImPlot::SetupAxisLimits(ImAxis_X1,0,history, ImGuiCond_Always); - ImPlot::SetupAxisLimits(ImAxis_Y1,0,1); - ImPlot::PlotLine("Mouse X", &rdata1.Data[0].x, &rdata1.Data[0].y, rdata1.Data.size(), 0, 0, 2 * sizeof(float)); - ImPlot::PlotLine("Mouse Y", &rdata2.Data[0].x, &rdata2.Data[0].y, rdata2.Data.size(), 0, 0, 2 * sizeof(float)); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_MarkersAndText() { - static float mk_size = ImPlot::GetStyle().MarkerSize; - static float mk_weight = ImPlot::GetStyle().MarkerWeight; - ImGui::DragFloat("Marker Size",&mk_size,0.1f,2.0f,10.0f,"%.2f px"); - ImGui::DragFloat("Marker Weight", &mk_weight,0.05f,0.5f,3.0f,"%.2f px"); - - if (ImPlot::BeginPlot("##MarkerStyles", ImVec2(-1,0), ImPlotFlags_CanvasOnly)) { - - ImPlot::SetupAxes(NULL, NULL, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); - ImPlot::SetupAxesLimits(0, 10, 0, 12); - - ImS8 xs[2] = {1,4}; - ImS8 ys[2] = {10,11}; - - // filled markers - for (int m = 0; m < ImPlotMarker_COUNT; ++m) { - ImGui::PushID(m); - ImPlot::SetNextMarkerStyle(m, mk_size, IMPLOT_AUTO_COL, mk_weight); - ImPlot::PlotLine("##Filled", xs, ys, 2); - ImGui::PopID(); - ys[0]--; ys[1]--; - } - xs[0] = 6; xs[1] = 9; ys[0] = 10; ys[1] = 11; - // open markers - for (int m = 0; m < ImPlotMarker_COUNT; ++m) { - ImGui::PushID(m); - ImPlot::SetNextMarkerStyle(m, mk_size, ImVec4(0,0,0,0), mk_weight); - ImPlot::PlotLine("##Open", xs, ys, 2); - ImGui::PopID(); - ys[0]--; ys[1]--; - } - - ImPlot::PlotText("Filled Markers", 2.5f, 6.0f); - ImPlot::PlotText("Open Markers", 7.5f, 6.0f); - - ImPlot::PushStyleColor(ImPlotCol_InlayText, ImVec4(1,0,1,1)); - ImPlot::PlotText("Vertical Text", 5.0f, 6.0f, ImVec2(0,0), ImPlotTextFlags_Vertical); - ImPlot::PopStyleColor(); - - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_NaNValues() { - - static bool include_nan = true; - static ImPlotLineFlags flags = 0; - - float data1[5] = {0.0f,0.25f,0.5f,0.75f,1.0f}; - float data2[5] = {0.0f,0.25f,0.5f,0.75f,1.0f}; - - if (include_nan) - data1[2] = NAN; - - ImGui::Checkbox("Include NaN",&include_nan); - ImGui::SameLine(); - ImGui::CheckboxFlags("Skip NaN", (unsigned int*)&flags, ImPlotLineFlags_SkipNaN); - - if (ImPlot::BeginPlot("##NaNValues")) { - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); - ImPlot::PlotLine("line", data1, data2, 5, flags); - ImPlot::PlotBars("bars", data1, 5); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_LogScale() { - static double xs[1001], ys1[1001], ys2[1001], ys3[1001]; - for (int i = 0; i < 1001; ++i) { - xs[i] = i*0.1f; - ys1[i] = sin(xs[i]) + 1; - ys2[i] = log(xs[i]); - ys3[i] = pow(10.0, xs[i]); - } - if (ImPlot::BeginPlot("Log Plot", ImVec2(-1,0))) { - ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Log10); - ImPlot::SetupAxesLimits(0.1, 100, 0, 10); - ImPlot::PlotLine("f(x) = x", xs, xs, 1001); - ImPlot::PlotLine("f(x) = sin(x)+1", xs, ys1, 1001); - ImPlot::PlotLine("f(x) = log(x)", xs, ys2, 1001); - ImPlot::PlotLine("f(x) = 10^x", xs, ys3, 21); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_SymmetricLogScale() { - static double xs[1001], ys1[1001], ys2[1001]; - for (int i = 0; i < 1001; ++i) { - xs[i] = i*0.1f-50; - ys1[i] = sin(xs[i]); - ys2[i] = i*0.002 - 1; - } - if (ImPlot::BeginPlot("SymLog Plot", ImVec2(-1,0))) { - ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_SymLog); - ImPlot::PlotLine("f(x) = a*x+b",xs,ys2,1001); - ImPlot::PlotLine("f(x) = sin(x)",xs,ys1,1001); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_TimeScale() { - - static double t_min = 1609459200; // 01/01/2021 @ 12:00:00am (UTC) - static double t_max = 1640995200; // 01/01/2022 @ 12:00:00am (UTC) - - ImGui::BulletText("When ImPlotAxisFlags_Time is enabled on the X-Axis, values are interpreted as\n" - "UNIX timestamps in seconds and axis labels are formated as date/time."); - ImGui::BulletText("By default, labels are in UTC time but can be set to use local time instead."); - - ImGui::Checkbox("Local Time",&ImPlot::GetStyle().UseLocalTime); - ImGui::SameLine(); - ImGui::Checkbox("ISO 8601",&ImPlot::GetStyle().UseISO8601); - ImGui::SameLine(); - ImGui::Checkbox("24 Hour Clock",&ImPlot::GetStyle().Use24HourClock); - - static HugeTimeData* data = NULL; - if (data == NULL) { - ImGui::SameLine(); - if (ImGui::Button("Generate Huge Data (~500MB!)")) { - static HugeTimeData sdata(t_min); - data = &sdata; - } - } - - if (ImPlot::BeginPlot("##Time", ImVec2(-1,0))) { - ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); - ImPlot::SetupAxesLimits(t_min,t_max,0,1); - if (data != NULL) { - // downsample our data - int downsample = (int)ImPlot::GetPlotLimits().X.Size() / 1000 + 1; - int start = (int)(ImPlot::GetPlotLimits().X.Min - t_min); - start = start < 0 ? 0 : start > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : start; - int end = (int)(ImPlot::GetPlotLimits().X.Max - t_min) + 1000; - end = end < 0 ? 0 : end > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : end; - int size = (end - start)/downsample; - // plot it - ImPlot::PlotLine("Time Series", &data->Ts[start], &data->Ys[start], size, 0, 0, sizeof(double)*downsample); - } - // plot time now - double t_now = (double)time(0); - double y_now = HugeTimeData::GetY(t_now); - ImPlot::PlotScatter("Now",&t_now,&y_now,1); - ImPlot::Annotation(t_now,y_now,ImPlot::GetLastItemColor(),ImVec2(10,10),false,"Now"); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -static inline double TransformForward_Sqrt(double v, void*) { - return sqrt(v); -} - -static inline double TransformInverse_Sqrt(double v, void*) { - return v*v; -} - -void Demo_CustomScale() { - static float v[100]; - for (int i = 0; i < 100; ++i) { - v[i] = i*0.01f; - } - if (ImPlot::BeginPlot("Sqrt")) { - ImPlot::SetupAxis(ImAxis_X1, "Linear"); - ImPlot::SetupAxis(ImAxis_Y1, "Sqrt"); - ImPlot::SetupAxisScale(ImAxis_Y1, TransformForward_Sqrt, TransformInverse_Sqrt); - ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1, 0, INFINITY); - ImPlot::PlotLine("##data",v,v,100); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_MultipleAxes() { - static float xs[1001], xs2[1001], ys1[1001], ys2[1001], ys3[1001]; - for (int i = 0; i < 1001; ++i) { - xs[i] = (i*0.1f); - xs2[i] = xs[i] + 10.0f; - ys1[i] = sinf(xs[i]) * 3 + 1; - ys2[i] = cosf(xs[i]) * 0.2f + 0.5f; - ys3[i] = sinf(xs[i]+0.5f) * 100 + 200; - } - - static bool x2_axis = true; - static bool y2_axis = true; - static bool y3_axis = true; - - ImGui::Checkbox("X-Axis 2", &x2_axis); - ImGui::SameLine(); - ImGui::Checkbox("Y-Axis 2", &y2_axis); - ImGui::SameLine(); - ImGui::Checkbox("Y-Axis 3", &y3_axis); - - ImGui::BulletText("You can drag axes to the opposite side of the plot."); - ImGui::BulletText("Hover over legend items to see which axis they are plotted on."); - - if (ImPlot::BeginPlot("Multi-Axis Plot", ImVec2(-1,0))) { - ImPlot::SetupAxes("X-Axis 1", "Y-Axis 1"); - ImPlot::SetupAxesLimits(0, 100, 0, 10); - if (x2_axis) { - ImPlot::SetupAxis(ImAxis_X2, "X-Axis 2",ImPlotAxisFlags_AuxDefault); - ImPlot::SetupAxisLimits(ImAxis_X2, 0, 100); - } - if (y2_axis) { - ImPlot::SetupAxis(ImAxis_Y2, "Y-Axis 2",ImPlotAxisFlags_AuxDefault); - ImPlot::SetupAxisLimits(ImAxis_Y2, 0, 1); - } - if (y3_axis) { - ImPlot::SetupAxis(ImAxis_Y3, "Y-Axis 3",ImPlotAxisFlags_AuxDefault); - ImPlot::SetupAxisLimits(ImAxis_Y3, 0, 300); - } - - ImPlot::PlotLine("f(x) = x", xs, xs, 1001); - if (x2_axis) { - ImPlot::SetAxes(ImAxis_X2, ImAxis_Y1); - ImPlot::PlotLine("f(x) = sin(x)*3+1", xs2, ys1, 1001); - } - if (y2_axis) { - ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2); - ImPlot::PlotLine("f(x) = cos(x)*.2+.5", xs, ys2, 1001); - } - if (y3_axis) { - ImPlot::SetAxes(ImAxis_X2, ImAxis_Y3); - ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 ", xs2, ys3, 1001); - } - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_LinkedAxes() { - static ImPlotRect lims(0,1,0,1); - static bool linkx = true, linky = true; - int data[2] = {0,1}; - ImGui::Checkbox("Link X", &linkx); - ImGui::SameLine(); - ImGui::Checkbox("Link Y", &linky); - - ImGui::DragScalarN("Limits",ImGuiDataType_Double,&lims.X.Min,4,0.01f); - - if (BeginAlignedPlots("AlignedGroup")) { - if (ImPlot::BeginPlot("Plot A")) { - ImPlot::SetupAxisLinks(ImAxis_X1, linkx ? &lims.X.Min : NULL, linkx ? &lims.X.Max : NULL); - ImPlot::SetupAxisLinks(ImAxis_Y1, linky ? &lims.Y.Min : NULL, linky ? &lims.Y.Max : NULL); - ImPlot::PlotLine("Line",data,2); - ImPlot::EndPlot(); - } - if (ImPlot::BeginPlot("Plot B")) { - ImPlot::SetupAxisLinks(ImAxis_X1, linkx ? &lims.X.Min : NULL, linkx ? &lims.X.Max : NULL); - ImPlot::SetupAxisLinks(ImAxis_Y1, linky ? &lims.Y.Min : NULL, linky ? &lims.Y.Max : NULL); - ImPlot::PlotLine("Line",data,2); - ImPlot::EndPlot(); - } - ImPlot::EndAlignedPlots(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_AxisConstraints() { - static float constraints[4] = {-10,10,1,20}; - static ImPlotAxisFlags flags; - ImGui::DragFloat2("Limits Constraints", &constraints[0], 0.01); - ImGui::DragFloat2("Zoom Constraints", &constraints[2], 0.01); - CHECKBOX_FLAG(flags, ImPlotAxisFlags_PanStretch); - if (ImPlot::BeginPlot("##AxisConstraints",ImVec2(-1,0))) { - ImPlot::SetupAxes("X","Y",flags,flags); - ImPlot::SetupAxesLimits(-1,1,-1,1); - ImPlot::SetupAxisLimitsConstraints(ImAxis_X1,constraints[0], constraints[1]); - ImPlot::SetupAxisZoomConstraints(ImAxis_X1,constraints[2], constraints[3]); - ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1,constraints[0], constraints[1]); - ImPlot::SetupAxisZoomConstraints(ImAxis_Y1,constraints[2], constraints[3]); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_EqualAxes() { - ImGui::BulletText("Equal constraint applies to axis pairs (e.g ImAxis_X1/Y1, ImAxis_X2/Y2)"); - static double xs1[360], ys1[360]; - for (int i = 0; i < 360; ++i) { - double angle = i * 2 * PI / 359.0; - xs1[i] = cos(angle); ys1[i] = sin(angle); - } - float xs2[] = {-1,0,1,0,-1}; - float ys2[] = {0,1,0,-1,0}; - if (ImPlot::BeginPlot("##EqualAxes",ImVec2(-1,0),ImPlotFlags_Equal)) { - ImPlot::SetupAxis(ImAxis_X2, NULL, ImPlotAxisFlags_AuxDefault); - ImPlot::SetupAxis(ImAxis_Y2, NULL, ImPlotAxisFlags_AuxDefault); - ImPlot::PlotLine("Circle",xs1,ys1,360); - ImPlot::SetAxes(ImAxis_X2, ImAxis_Y2); - ImPlot::PlotLine("Diamond",xs2,ys2,5); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_AutoFittingData() { - ImGui::BulletText("The Y-axis has been configured to auto-fit to only the data visible in X-axis range."); - ImGui::BulletText("Zoom and pan the X-axis. Disable Stems to see a difference in fit."); - ImGui::BulletText("If ImPlotAxisFlags_RangeFit is disabled, the axis will fit ALL data."); - - static ImPlotAxisFlags xflags = ImPlotAxisFlags_None; - static ImPlotAxisFlags yflags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit; - - ImGui::TextUnformatted("X: "); ImGui::SameLine(); - ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine(); - ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_RangeFit); - - ImGui::TextUnformatted("Y: "); ImGui::SameLine(); - ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine(); - ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_RangeFit); - - static double data[101]; - srand(0); - for (int i = 0; i < 101; ++i) - data[i] = 1 + sin(i/10.0f); - - if (ImPlot::BeginPlot("##DataFitting")) { - ImPlot::SetupAxes("X","Y",xflags,yflags); - ImPlot::PlotLine("Line",data,101); - ImPlot::PlotStems("Stems",data,101); - ImPlot::EndPlot(); - }; -} - -//----------------------------------------------------------------------------- - -ImPlotPoint SinewaveGetter(int i, void* data) { - float f = *(float*)data; - return ImPlotPoint(i,sinf(f*i)); -} - -void Demo_SubplotsSizing() { - - static ImPlotSubplotFlags flags = ImPlotSubplotFlags_None; - ImGui::CheckboxFlags("ImPlotSubplotFlags_NoResize", (unsigned int*)&flags, ImPlotSubplotFlags_NoResize); - ImGui::CheckboxFlags("ImPlotSubplotFlags_NoTitle", (unsigned int*)&flags, ImPlotSubplotFlags_NoTitle); - - static int rows = 3; - static int cols = 3; - ImGui::SliderInt("Rows",&rows,1,5); - ImGui::SliderInt("Cols",&cols,1,5); - static float rratios[] = {5,1,1,1,1,1}; - static float cratios[] = {5,1,1,1,1,1}; - ImGui::DragScalarN("Row Ratios",ImGuiDataType_Float,rratios,rows,0.01f,0); - ImGui::DragScalarN("Col Ratios",ImGuiDataType_Float,cratios,cols,0.01f,0); - if (ImPlot::BeginSubplots("My Subplots", rows, cols, ImVec2(-1,400), flags, rratios, cratios)) { - for (int i = 0; i < rows*cols; ++i) { - if (ImPlot::BeginPlot("",ImVec2(),ImPlotFlags_NoLegend)) { - ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); - float fi = 0.01f * (i+1); - ImPlot::SetNextLineStyle(SampleColormap((float)i/(float)(rows*cols-1),ImPlotColormap_Jet)); - ImPlot::PlotLineG("data",SinewaveGetter,&fi,1000); - ImPlot::EndPlot(); - } - } - ImPlot::EndSubplots(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_SubplotItemSharing() { - static ImPlotSubplotFlags flags = ImPlotSubplotFlags_ShareItems; - ImGui::CheckboxFlags("ImPlotSubplotFlags_ShareItems", (unsigned int*)&flags, ImPlotSubplotFlags_ShareItems); - ImGui::CheckboxFlags("ImPlotSubplotFlags_ColMajor", (unsigned int*)&flags, ImPlotSubplotFlags_ColMajor); - ImGui::BulletText("Drag and drop items from the legend onto plots (except for 'common')"); - static int rows = 2; - static int cols = 3; - static int id[] = {0,1,2,3,4,5}; - static int curj = -1; - if (ImPlot::BeginSubplots("##ItemSharing", rows, cols, ImVec2(-1,400), flags)) { - for (int i = 0; i < rows*cols; ++i) { - if (ImPlot::BeginPlot("")) { - float fc = 0.01f; - ImPlot::PlotLineG("common",SinewaveGetter,&fc,1000); - for (int j = 0; j < 6; ++j) { - if (id[j] == i) { - char label[8]; - float fj = 0.01f * (j+2); - sprintf(label, "data%d", j); - ImPlot::PlotLineG(label,SinewaveGetter,&fj,1000); - if (ImPlot::BeginDragDropSourceItem(label)) { - curj = j; - ImGui::SetDragDropPayload("MY_DND",NULL,0); - ImPlot::ItemIcon(GetLastItemColor()); ImGui::SameLine(); - ImGui::TextUnformatted(label); - ImPlot::EndDragDropSource(); - } - } - } - if (ImPlot::BeginDragDropTargetPlot()) { - if (ImGui::AcceptDragDropPayload("MY_DND")) - id[curj] = i; - ImPlot::EndDragDropTarget(); - } - ImPlot::EndPlot(); - } - } - ImPlot::EndSubplots(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_SubplotAxisLinking() { - static ImPlotSubplotFlags flags = ImPlotSubplotFlags_LinkRows | ImPlotSubplotFlags_LinkCols; - ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkRows", (unsigned int*)&flags, ImPlotSubplotFlags_LinkRows); - ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkCols", (unsigned int*)&flags, ImPlotSubplotFlags_LinkCols); - ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkAllX", (unsigned int*)&flags, ImPlotSubplotFlags_LinkAllX); - ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkAllY", (unsigned int*)&flags, ImPlotSubplotFlags_LinkAllY); - - static int rows = 2; - static int cols = 2; - if (ImPlot::BeginSubplots("##AxisLinking", rows, cols, ImVec2(-1,400), flags)) { - for (int i = 0; i < rows*cols; ++i) { - if (ImPlot::BeginPlot("")) { - ImPlot::SetupAxesLimits(0,1000,-1,1); - float fc = 0.01f; - ImPlot::PlotLineG("common",SinewaveGetter,&fc,1000); - ImPlot::EndPlot(); - } - } - ImPlot::EndSubplots(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_LegendOptions() { - static ImPlotLocation loc = ImPlotLocation_East; - static bool h = false; static bool o = true; - ImGui::CheckboxFlags("North", (unsigned int*)&loc, ImPlotLocation_North); ImGui::SameLine(); - ImGui::CheckboxFlags("South", (unsigned int*)&loc, ImPlotLocation_South); ImGui::SameLine(); - ImGui::CheckboxFlags("West", (unsigned int*)&loc, ImPlotLocation_West); ImGui::SameLine(); - ImGui::CheckboxFlags("East", (unsigned int*)&loc, ImPlotLocation_East); ImGui::SameLine(); - ImGui::Checkbox("Horizontal##2", &h); ImGui::SameLine(); - ImGui::Checkbox("Outside", &o); - - ImGui::SliderFloat2("LegendPadding", (float*)&GetStyle().LegendPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, "%.0f"); - ImGui::SliderFloat2("LegendSpacing", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, "%.0f"); - - if (ImPlot::BeginPlot("##Legend",ImVec2(-1,0))) { - ImPlotLegendFlags flags = ImPlotLegendFlags_None; - if (h) flags |= ImPlotLegendFlags_Horizontal; - if (o) flags |= ImPlotLegendFlags_Outside; - ImPlot::SetupLegend(loc, flags); - static MyImPlot::WaveData data1(0.001, 0.2, 2, 0.75); - static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.25); - static MyImPlot::WaveData data3(0.001, 0.2, 6, 0.5); - ImPlot::PlotLineG("Item 1", MyImPlot::SineWave, &data1, 1000); // "Item 1" added to legend - ImPlot::PlotLineG("Item 2##IDText", MyImPlot::SawWave, &data2, 1000); // "Item 2" added to legend, text after ## used for ID only - ImPlot::PlotLineG("##NotListed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend - ImPlot::PlotLineG("Item 3", MyImPlot::SineWave, &data1, 1000); // "Item 3" added to legend - ImPlot::PlotLineG("Item 3", MyImPlot::SawWave, &data2, 1000); // combined with previous "Item 3" - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_DragPoints() { - ImGui::BulletText("Click and drag each point."); - static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; - ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); - ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); - ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); - ImPlotAxisFlags ax_flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks; - if (ImPlot::BeginPlot("##Bezier",ImVec2(-1,0),ImPlotFlags_CanvasOnly)) { - ImPlot::SetupAxes(0,0,ax_flags,ax_flags); - ImPlot::SetupAxesLimits(0,1,0,1); - static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(.95f,.95f)}; - - ImPlot::DragPoint(0,&P[0].x,&P[0].y, ImVec4(0,0.9f,0,1),4,flags); - ImPlot::DragPoint(1,&P[1].x,&P[1].y, ImVec4(1,0.5f,1,1),4,flags); - ImPlot::DragPoint(2,&P[2].x,&P[2].y, ImVec4(0,0.5f,1,1),4,flags); - ImPlot::DragPoint(3,&P[3].x,&P[3].y, ImVec4(0,0.9f,0,1),4,flags); - - static ImPlotPoint B[100]; - for (int i = 0; i < 100; ++i) { - double t = i / 99.0; - double u = 1 - t; - double w1 = u*u*u; - double w2 = 3*u*u*t; - double w3 = 3*u*t*t; - double w4 = t*t*t; - B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y); - } - - - ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1)); - ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, 0, sizeof(ImPlotPoint)); - ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1)); - ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, 0, sizeof(ImPlotPoint)); - ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), 2); - ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint)); - - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_DragLines() { - ImGui::BulletText("Click and drag the horizontal and vertical lines."); - static double x1 = 0.2; - static double x2 = 0.8; - static double y1 = 0.25; - static double y2 = 0.75; - static double f = 0.1; - static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; - ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); - ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); - ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); - if (ImPlot::BeginPlot("##lines",ImVec2(-1,0))) { - ImPlot::SetupAxesLimits(0,1,0,1); - ImPlot::DragLineX(0,&x1,ImVec4(1,1,1,1),1,flags); - ImPlot::DragLineX(1,&x2,ImVec4(1,1,1,1),1,flags); - ImPlot::DragLineY(2,&y1,ImVec4(1,1,1,1),1,flags); - ImPlot::DragLineY(3,&y2,ImVec4(1,1,1,1),1,flags); - double xs[1000], ys[1000]; - for (int i = 0; i < 1000; ++i) { - xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f); - ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10); - } - ImPlot::PlotLine("Interactive Data", xs, ys, 1000); - ImPlot::DragLineY(120482,&f,ImVec4(1,0.5f,1,1),1,flags); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_DragRects() { - - static float x_data[512]; - static float y_data1[512]; - static float y_data2[512]; - static float y_data3[512]; - static float sampling_freq = 44100; - static float freq = 500; - for (size_t i = 0; i < 512; ++i) { - const float t = i / sampling_freq; - x_data[i] = t; - const float arg = 2 * 3.14f * freq * t; - y_data1[i] = sinf(arg); - y_data2[i] = y_data1[i] * -0.6f + sinf(2 * arg) * 0.4f; - y_data3[i] = y_data2[i] * -0.6f + sinf(3 * arg) * 0.4f; - } - ImGui::BulletText("Click and drag the edges, corners, and center of the rect."); - static ImPlotRect rect(0.0025,0.0045,0,0.5); - static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; - ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); - ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); - ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); - - if (ImPlot::BeginPlot("##Main",ImVec2(-1,150))) { - ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoTickLabels,ImPlotAxisFlags_NoTickLabels); - ImPlot::SetupAxesLimits(0,0.01,-1,1); - ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); - ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); - ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); - ImPlot::DragRect(0,&rect.X.Min,&rect.Y.Min,&rect.X.Max,&rect.Y.Max,ImVec4(1,0,1,1),flags); - ImPlot::EndPlot(); - } - if (ImPlot::BeginPlot("##rect",ImVec2(-1,150), ImPlotFlags_CanvasOnly)) { - ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); - ImPlot::SetupAxesLimits(rect.X.Min, rect.X.Max, rect.Y.Min, rect.Y.Max, ImGuiCond_Always); - ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); - ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); - ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -ImPlotPoint FindCentroid(const ImVector& data, const ImPlotRect& bounds, int& cnt) { - cnt = 0; - ImPlotPoint avg; - ImPlotRect bounds_fixed; - bounds_fixed.X.Min = bounds.X.Min < bounds.X.Max ? bounds.X.Min : bounds.X.Max; - bounds_fixed.X.Max = bounds.X.Min < bounds.X.Max ? bounds.X.Max : bounds.X.Min; - bounds_fixed.Y.Min = bounds.Y.Min < bounds.Y.Max ? bounds.Y.Min : bounds.Y.Max; - bounds_fixed.Y.Max = bounds.Y.Min < bounds.Y.Max ? bounds.Y.Max : bounds.Y.Min; - for (int i = 0; i < data.size(); ++i) { - if (bounds_fixed.Contains(data[i].x, data[i].y)) { - avg.x += data[i].x; - avg.y += data[i].y; - cnt++; - } - } - if (cnt > 0) { - avg.x = avg.x / cnt; - avg.y = avg.y / cnt; - } - return avg; -} - -//----------------------------------------------------------------------------- - -void Demo_Querying() { - static ImVector data; - static ImVector rects; - static ImPlotRect limits, select; - static bool init = true; - if (init) { - for (int i = 0; i < 50; ++i) - { - double x = RandomRange(0.1, 0.9); - double y = RandomRange(0.1, 0.9); - data.push_back(ImPlotPoint(x,y)); - } - init = false; - } - - ImGui::BulletText("Box select and left click mouse to create a new query rect."); - ImGui::BulletText("Ctrl + click in the plot area to draw points."); - - if (ImGui::Button("Clear Queries")) - rects.shrink(0); - - if (ImPlot::BeginPlot("##Centroid")) { - ImPlot::SetupAxesLimits(0,1,0,1); - if (ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyCtrl) { - ImPlotPoint pt = ImPlot::GetPlotMousePos(); - data.push_back(pt); - } - ImPlot::PlotScatter("Points", &data[0].x, &data[0].y, data.size(), 0, 0, 2 * sizeof(double)); - if (ImPlot::IsPlotSelected()) { - select = ImPlot::GetPlotSelection(); - int cnt; - ImPlotPoint centroid = FindCentroid(data,select,cnt); - if (cnt > 0) { - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,6); - ImPlot::PlotScatter("Centroid", ¢roid.x, ¢roid.y, 1); - } - if (ImGui::IsMouseClicked(ImPlot::GetInputMap().SelectCancel)) { - CancelPlotSelection(); - rects.push_back(select); - } - } - for (int i = 0; i < rects.size(); ++i) { - int cnt; - ImPlotPoint centroid = FindCentroid(data,rects[i],cnt); - if (cnt > 0) { - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,6); - ImPlot::PlotScatter("Centroid", ¢roid.x, ¢roid.y, 1); - } - ImPlot::DragRect(i,&rects[i].X.Min,&rects[i].Y.Min,&rects[i].X.Max,&rects[i].Y.Max,ImVec4(1,0,1,1)); - } - limits = ImPlot::GetPlotLimits(); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_Annotations() { - static bool clamp = false; - ImGui::Checkbox("Clamp",&clamp); - if (ImPlot::BeginPlot("##Annotations")) { - ImPlot::SetupAxesLimits(0,2,0,1); - static float p[] = {0.25f, 0.25f, 0.75f, 0.75f, 0.25f}; - ImPlot::PlotScatter("##Points",&p[0],&p[1],4); - ImVec4 col = GetLastItemColor(); - ImPlot::Annotation(0.25,0.25,col,ImVec2(-15,15),clamp,"BL"); - ImPlot::Annotation(0.75,0.25,col,ImVec2(15,15),clamp,"BR"); - ImPlot::Annotation(0.75,0.75,col,ImVec2(15,-15),clamp,"TR"); - ImPlot::Annotation(0.25,0.75,col,ImVec2(-15,-15),clamp,"TL"); - ImPlot::Annotation(0.5,0.5,col,ImVec2(0,0),clamp,"Center"); - - ImPlot::Annotation(1.25,0.75,ImVec4(0,1,0,1),ImVec2(0,0),clamp); - - float bx[] = {1.2f,1.5f,1.8f}; - float by[] = {0.25f, 0.5f, 0.75f}; - ImPlot::PlotBars("##Bars",bx,by,3,0.2); - for (int i = 0; i < 3; ++i) - ImPlot::Annotation(bx[i],by[i],ImVec4(0,0,0,0),ImVec2(0,-5),clamp,"B[%d]=%.2f",i,by[i]); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_Tags() { - static bool show = true; - ImGui::Checkbox("Show Tags",&show); - if (ImPlot::BeginPlot("##Tags")) { - ImPlot::SetupAxis(ImAxis_X2); - ImPlot::SetupAxis(ImAxis_Y2); - if (show) { - ImPlot::TagX(0.25, ImVec4(1,1,0,1)); - ImPlot::TagY(0.75, ImVec4(1,1,0,1)); - static double drag_tag = 0.25; - ImPlot::DragLineY(0,&drag_tag,ImVec4(1,0,0,1),1,ImPlotDragToolFlags_NoFit); - ImPlot::TagY(drag_tag, ImVec4(1,0,0,1), "Drag"); - SetAxes(ImAxis_X2, ImAxis_Y2); - ImPlot::TagX(0.5, ImVec4(0,1,1,1), "%s", "MyTag"); - ImPlot::TagY(0.5, ImVec4(0,1,1,1), "Tag: %d", 42); - } - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_DragAndDrop() { - ImGui::BulletText("Drag/drop items from the left column."); - ImGui::BulletText("Drag/drop items between plots."); - ImGui::Indent(); - ImGui::BulletText("Plot 1 Targets: Plot, Y-Axes, Legend"); - ImGui::BulletText("Plot 1 Sources: Legend Item Labels"); - ImGui::BulletText("Plot 2 Targets: Plot, X-Axis, Y-Axis"); - ImGui::BulletText("Plot 2 Sources: Plot, X-Axis, Y-Axis (hold Ctrl)"); - ImGui::Unindent(); - - // convenience struct to manage DND items; do this however you like - struct MyDndItem { - int Idx; - int Plt; - ImAxis Yax; - char Label[16]; - ImVector Data; - ImVec4 Color; - MyDndItem() { - static int i = 0; - Idx = i++; - Plt = 0; - Yax = ImAxis_Y1; - sprintf(Label, "%02d Hz", Idx+1); - Color = RandomColor(); - Data.reserve(1001); - for (int k = 0; k < 1001; ++k) { - float t = k * 1.0f / 999; - Data.push_back(ImVec2(t, 0.5f + 0.5f * sinf(2*3.14f*t*(Idx+1)))); - } - } - void Reset() { Plt = 0; Yax = ImAxis_Y1; } - }; - - const int k_dnd = 20; - static MyDndItem dnd[k_dnd]; - static MyDndItem* dndx = NULL; // for plot 2 - static MyDndItem* dndy = NULL; // for plot 2 - - // child window to serve as initial source for our DND items - ImGui::BeginChild("DND_LEFT",ImVec2(100,400)); - if (ImGui::Button("Reset Data")) { - for (int k = 0; k < k_dnd; ++k) - dnd[k].Reset(); - dndx = dndy = NULL; - } - for (int k = 0; k < k_dnd; ++k) { - if (dnd[k].Plt > 0) - continue; - ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); - ImGui::Selectable(dnd[k].Label, false, 0, ImVec2(100, 0)); - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); - ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); - ImGui::TextUnformatted(dnd[k].Label); - ImGui::EndDragDropSource(); - } - } - ImGui::EndChild(); - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dnd[i].Reset(); - } - ImGui::EndDragDropTarget(); - } - - ImGui::SameLine(); - ImGui::BeginChild("DND_RIGHT",ImVec2(-1,400)); - // plot 1 (time series) - ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoHighlight; - if (ImPlot::BeginPlot("##DND1", ImVec2(-1,195))) { - ImPlot::SetupAxis(ImAxis_X1, NULL, flags|ImPlotAxisFlags_Lock); - ImPlot::SetupAxis(ImAxis_Y1, "[drop here]", flags); - ImPlot::SetupAxis(ImAxis_Y2, "[drop here]", flags|ImPlotAxisFlags_Opposite); - ImPlot::SetupAxis(ImAxis_Y3, "[drop here]", flags|ImPlotAxisFlags_Opposite); - - for (int k = 0; k < k_dnd; ++k) { - if (dnd[k].Plt == 1 && dnd[k].Data.size() > 0) { - ImPlot::SetAxis(dnd[k].Yax); - ImPlot::SetNextLineStyle(dnd[k].Color); - ImPlot::PlotLine(dnd[k].Label, &dnd[k].Data[0].x, &dnd[k].Data[0].y, dnd[k].Data.size(), 0, 0, 2 * sizeof(float)); - // allow legend item labels to be DND sources - if (ImPlot::BeginDragDropSourceItem(dnd[k].Label)) { - ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); - ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); - ImGui::TextUnformatted(dnd[k].Label); - ImPlot::EndDragDropSource(); - } - } - } - // allow the main plot area to be a DND target - if (ImPlot::BeginDragDropTargetPlot()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = ImAxis_Y1; - } - ImPlot::EndDragDropTarget(); - } - // allow each y-axis to be a DND target - for (int y = ImAxis_Y1; y <= ImAxis_Y3; ++y) { - if (ImPlot::BeginDragDropTargetAxis(y)) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = y; - } - ImPlot::EndDragDropTarget(); - } - } - // allow the legend to be a DND target - if (ImPlot::BeginDragDropTargetLegend()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = ImAxis_Y1; - } - ImPlot::EndDragDropTarget(); - } - ImPlot::EndPlot(); - } - // plot 2 (Lissajous) - if (ImPlot::BeginPlot("##DND2", ImVec2(-1,195))) { - ImPlot::PushStyleColor(ImPlotCol_AxisBg, dndx != NULL ? dndx->Color : ImPlot::GetStyle().Colors[ImPlotCol_AxisBg]); - ImPlot::SetupAxis(ImAxis_X1, dndx == NULL ? "[drop here]" : dndx->Label, flags); - ImPlot::PushStyleColor(ImPlotCol_AxisBg, dndy != NULL ? dndy->Color : ImPlot::GetStyle().Colors[ImPlotCol_AxisBg]); - ImPlot::SetupAxis(ImAxis_Y1, dndy == NULL ? "[drop here]" : dndy->Label, flags); - ImPlot::PopStyleColor(2); - if (dndx != NULL && dndy != NULL) { - ImVec4 mixed((dndx->Color.x + dndy->Color.x)/2,(dndx->Color.y + dndy->Color.y)/2,(dndx->Color.z + dndy->Color.z)/2,(dndx->Color.w + dndy->Color.w)/2); - ImPlot::SetNextLineStyle(mixed); - ImPlot::PlotLine("##dndxy", &dndx->Data[0].y, &dndy->Data[0].y, dndx->Data.size(), 0, 0, 2 * sizeof(float)); - } - // allow the x-axis to be a DND target - if (ImPlot::BeginDragDropTargetAxis(ImAxis_X1)) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dndx = &dnd[i]; - } - ImPlot::EndDragDropTarget(); - } - // allow the x-axis to be a DND source - if (dndx != NULL && ImPlot::BeginDragDropSourceAxis(ImAxis_X1)) { - ImGui::SetDragDropPayload("MY_DND", &dndx->Idx, sizeof(int)); - ImPlot::ItemIcon(dndx->Color); ImGui::SameLine(); - ImGui::TextUnformatted(dndx->Label); - ImPlot::EndDragDropSource(); - } - // allow the y-axis to be a DND target - if (ImPlot::BeginDragDropTargetAxis(ImAxis_Y1)) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dndy = &dnd[i]; - } - ImPlot::EndDragDropTarget(); - } - // allow the y-axis to be a DND source - if (dndy != NULL && ImPlot::BeginDragDropSourceAxis(ImAxis_Y1)) { - ImGui::SetDragDropPayload("MY_DND", &dndy->Idx, sizeof(int)); - ImPlot::ItemIcon(dndy->Color); ImGui::SameLine(); - ImGui::TextUnformatted(dndy->Label); - ImPlot::EndDragDropSource(); - } - // allow the plot area to be a DND target - if (ImPlot::BeginDragDropTargetPlot()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dndx = dndy = &dnd[i]; - } - } - // allow the plot area to be a DND source - if (ImPlot::BeginDragDropSourcePlot()) { - ImGui::TextUnformatted("Yes, you can\ndrag this!"); - ImPlot::EndDragDropSource(); - } - ImPlot::EndPlot(); - } - ImGui::EndChild(); -} - -//----------------------------------------------------------------------------- - -void Demo_Tables() { -#ifdef IMGUI_HAS_TABLE - static ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | - ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable; - static bool anim = true; - static int offset = 0; - ImGui::BulletText("Plots can be used inside of ImGui tables as another means of creating subplots."); - ImGui::Checkbox("Animate",&anim); - if (anim) - offset = (offset + 1) % 100; - if (ImGui::BeginTable("##table", 3, flags, ImVec2(-1,0))) { - ImGui::TableSetupColumn("Electrode", ImGuiTableColumnFlags_WidthFixed, 75.0f); - ImGui::TableSetupColumn("Voltage", ImGuiTableColumnFlags_WidthFixed, 75.0f); - ImGui::TableSetupColumn("EMG Signal"); - ImGui::TableHeadersRow(); - ImPlot::PushColormap(ImPlotColormap_Cool); - for (int row = 0; row < 10; row++) { - ImGui::TableNextRow(); - static float data[100]; - srand(row); - for (int i = 0; i < 100; ++i) - data[i] = RandomRange(0.0f,10.0f); - ImGui::TableSetColumnIndex(0); - ImGui::Text("EMG %d", row); - ImGui::TableSetColumnIndex(1); - ImGui::Text("%.3f V", data[offset]); - ImGui::TableSetColumnIndex(2); - ImGui::PushID(row); - MyImPlot::Sparkline("##spark",data,100,0,11.0f,offset,ImPlot::GetColormapColor(row),ImVec2(-1, 35)); - ImGui::PopID(); - } - ImPlot::PopColormap(); - ImGui::EndTable(); - } -#else - ImGui::BulletText("You need to merge the ImGui 'tables' branch for this section."); -#endif -} - -//----------------------------------------------------------------------------- - -void Demo_OffsetAndStride() { - static const int k_circles = 11; - static const int k_points_per = 50; - static const int k_size = 2 * k_points_per * k_circles; - static double interleaved_data[k_size]; - for (int p = 0; p < k_points_per; ++p) { - for (int c = 0; c < k_circles; ++c) { - double r = (double)c / (k_circles - 1) * 0.2 + 0.2; - interleaved_data[p*2*k_circles + 2*c + 0] = 0.5 + r * cos((double)p/k_points_per * 6.28); - interleaved_data[p*2*k_circles + 2*c + 1] = 0.5 + r * sin((double)p/k_points_per * 6.28); - } - } - static int offset = 0; - ImGui::BulletText("Offsetting is useful for realtime plots (see above) and circular buffers."); - ImGui::BulletText("Striding is useful for interleaved data (e.g. audio) or plotting structs."); - ImGui::BulletText("Here, all circle data is stored in a single interleaved buffer:"); - ImGui::BulletText("[c0.x0 c0.y0 ... cn.x0 cn.y0 c0.x1 c0.y1 ... cn.x1 cn.y1 ... cn.xm cn.ym]"); - ImGui::BulletText("The offset value indicates which circle point index is considered the first."); - ImGui::BulletText("Offsets can be negative and/or larger than the actual data count."); - ImGui::SliderInt("Offset", &offset, -2*k_points_per, 2*k_points_per); - if (ImPlot::BeginPlot("##strideoffset",ImVec2(-1,0),ImPlotFlags_Equal)) { - ImPlot::PushColormap(ImPlotColormap_Jet); - char buff[16]; - for (int c = 0; c < k_circles; ++c) { - sprintf(buff, "Circle %d", c); - ImPlot::PlotLine(buff, &interleaved_data[c*2 + 0], &interleaved_data[c*2 + 1], k_points_per, 0, offset, 2*k_circles*sizeof(double)); - } - ImPlot::EndPlot(); - ImPlot::PopColormap(); - } - // offset++; uncomment for animation! -} - -//----------------------------------------------------------------------------- - -void Demo_CustomDataAndGetters() { - ImGui::BulletText("You can plot custom structs using the stride feature."); - ImGui::BulletText("Most plotters can also be passed a function pointer for getting data."); - ImGui::Indent(); - ImGui::BulletText("You can optionally pass user data to be given to your getter function."); - ImGui::BulletText("C++ lambdas can be passed as function pointers as well!"); - ImGui::Unindent(); - - MyImPlot::Vector2f vec2_data[2] = { MyImPlot::Vector2f(0,0), MyImPlot::Vector2f(1,1) }; - - if (ImPlot::BeginPlot("##Custom Data")) { - - // custom structs using stride example: - ImPlot::PlotLine("Vector2f", &vec2_data[0].x, &vec2_data[0].y, 2, 0, 0, sizeof(MyImPlot::Vector2f) /* or sizeof(float) * 2 */); - - // custom getter example 1: - ImPlot::PlotLineG("Spiral", MyImPlot::Spiral, NULL, 1000); - - // custom getter example 2: - static MyImPlot::WaveData data1(0.001, 0.2, 2, 0.75); - static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.25); - ImPlot::PlotLineG("Waves", MyImPlot::SineWave, &data1, 1000); - ImPlot::PlotLineG("Waves", MyImPlot::SawWave, &data2, 1000); - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); - ImPlot::PlotShadedG("Waves", MyImPlot::SineWave, &data1, MyImPlot::SawWave, &data2, 1000); - ImPlot::PopStyleVar(); - - // you can also pass C++ lambdas: - // auto lamda = [](void* data, int idx) { ... return ImPlotPoint(x,y); }; - // ImPlot::PlotLine("My Lambda", lambda, data, 1000); - - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -int MetricFormatter(double value, char* buff, int size, void* data) { - const char* unit = (const char*)data; - static double v[] = {1000000000,1000000,1000,1,0.001,0.000001,0.000000001}; - static const char* p[] = {"G","M","k","","m","u","n"}; - if (value == 0) { - return snprintf(buff,size,"0 %s", unit); - } - for (int i = 0; i < 7; ++i) { - if (fabs(value) >= v[i]) { - return snprintf(buff,size,"%g %s%s",value/v[i],p[i],unit); - } - } - return snprintf(buff,size,"%g %s%s",value/v[6],p[6],unit); -} - -void Demo_TickLabels() { - static bool custom_fmt = true; - static bool custom_ticks = false; - static bool custom_labels = true; - ImGui::Checkbox("Show Custom Format", &custom_fmt); - ImGui::SameLine(); - ImGui::Checkbox("Show Custom Ticks", &custom_ticks); - if (custom_ticks) { - ImGui::SameLine(); - ImGui::Checkbox("Show Custom Labels", &custom_labels); - } - const double pi = 3.14; - const char* pi_str[] = {"PI"}; - static double yticks[] = {100,300,700,900}; - static const char* ylabels[] = {"One","Three","Seven","Nine"}; - static double yticks_aux[] = {0.2,0.4,0.6}; - static const char* ylabels_aux[] = {"A","B","C","D","E","F"}; - if (ImPlot::BeginPlot("##Ticks")) { - ImPlot::SetupAxesLimits(2.5,5,0,1000); - ImPlot::SetupAxis(ImAxis_Y2, NULL, ImPlotAxisFlags_AuxDefault); - ImPlot::SetupAxis(ImAxis_Y3, NULL, ImPlotAxisFlags_AuxDefault); - if (custom_fmt) { - ImPlot::SetupAxisFormat(ImAxis_X1, "%g ms"); - ImPlot::SetupAxisFormat(ImAxis_Y1, MetricFormatter, (void*)"Hz"); - ImPlot::SetupAxisFormat(ImAxis_Y2, "%g dB"); - ImPlot::SetupAxisFormat(ImAxis_Y3, MetricFormatter, (void*)"m"); - } - if (custom_ticks) { - ImPlot::SetupAxisTicks(ImAxis_X1, &pi,1,custom_labels ? pi_str : NULL, true); - ImPlot::SetupAxisTicks(ImAxis_Y1, yticks, 4, custom_labels ? ylabels : NULL, false); - ImPlot::SetupAxisTicks(ImAxis_Y2, yticks_aux, 3, custom_labels ? ylabels_aux : NULL, false); - ImPlot::SetupAxisTicks(ImAxis_Y3, 0, 1, 6, custom_labels ? ylabels_aux : NULL, false); - } - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_CustomStyles() { - ImPlot::PushColormap(ImPlotColormap_Deep); - // normally you wouldn't change the entire style each frame - ImPlotStyle backup = ImPlot::GetStyle(); - MyImPlot::StyleSeaborn(); - if (ImPlot::BeginPlot("seaborn style")) { - ImPlot::SetupAxes( "x-axis", "y-axis"); - ImPlot::SetupAxesLimits(-0.5f, 9.5f, 0, 10); - unsigned int lin[10] = {8,8,9,7,8,8,8,9,7,8}; - unsigned int bar[10] = {1,2,5,3,4,1,2,5,3,4}; - unsigned int dot[10] = {7,6,6,7,8,5,6,5,8,7}; - ImPlot::PlotBars("Bars", bar, 10, 0.5f); - ImPlot::PlotLine("Line", lin, 10); - ImPlot::NextColormapColor(); // skip green - ImPlot::PlotScatter("Scatter", dot, 10); - ImPlot::EndPlot(); - } - ImPlot::GetStyle() = backup; - ImPlot::PopColormap(); -} - -//----------------------------------------------------------------------------- - -void Demo_CustomRendering() { - if (ImPlot::BeginPlot("##CustomRend")) { - ImVec2 cntr = ImPlot::PlotToPixels(ImPlotPoint(0.5f, 0.5f)); - ImVec2 rmin = ImPlot::PlotToPixels(ImPlotPoint(0.25f, 0.75f)); - ImVec2 rmax = ImPlot::PlotToPixels(ImPlotPoint(0.75f, 0.25f)); - ImPlot::PushPlotClipRect(); - ImPlot::GetPlotDrawList()->AddCircleFilled(cntr,20,IM_COL32(255,255,0,255),20); - ImPlot::GetPlotDrawList()->AddRect(rmin, rmax, IM_COL32(128,0,255,255)); - ImPlot::PopPlotClipRect(); - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_LegendPopups() { - ImGui::BulletText("You can implement legend context menus to inject per-item controls and widgets."); - ImGui::BulletText("Right click the legend label/icon to edit custom item attributes."); - - static float frequency = 0.1f; - static float amplitude = 0.5f; - static ImVec4 color = ImVec4(1,1,0,1); - static float alpha = 1.0f; - static bool line = false; - static float thickness = 1; - static bool markers = false; - static bool shaded = false; - - static float vals[101]; - for (int i = 0; i < 101; ++i) - vals[i] = amplitude * sinf(frequency * i); - - if (ImPlot::BeginPlot("Right Click the Legend")) { - ImPlot::SetupAxesLimits(0,100,-1,1); - // rendering logic - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); - if (!line) { - ImPlot::SetNextFillStyle(color); - ImPlot::PlotBars("Right Click Me", vals, 101); - } - else { - if (markers) ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); - ImPlot::SetNextLineStyle(color, thickness); - ImPlot::PlotLine("Right Click Me", vals, 101); - if (shaded) ImPlot::PlotShaded("Right Click Me",vals,101); - } - ImPlot::PopStyleVar(); - // custom legend context menu - if (ImPlot::BeginLegendPopup("Right Click Me")) { - ImGui::SliderFloat("Frequency",&frequency,0,1,"%0.2f"); - ImGui::SliderFloat("Amplitude",&litude,0,1,"%0.2f"); - ImGui::Separator(); - ImGui::ColorEdit3("Color",&color.x); - ImGui::SliderFloat("Transparency",&alpha,0,1,"%.2f"); - ImGui::Checkbox("Line Plot", &line); - if (line) { - ImGui::SliderFloat("Thickness", &thickness, 0, 5); - ImGui::Checkbox("Markers", &markers); - ImGui::Checkbox("Shaded",&shaded); - } - ImPlot::EndLegendPopup(); - } - ImPlot::EndPlot(); - } -} - -//----------------------------------------------------------------------------- - -void Demo_ColormapWidgets() { - static int cmap = ImPlotColormap_Viridis; - - if (ImPlot::ColormapButton("Button",ImVec2(0,0),cmap)) { - cmap = (cmap + 1) % ImPlot::GetColormapCount(); - } - - static float t = 0.5f; - static ImVec4 col; - ImGui::ColorButton("##Display",col,ImGuiColorEditFlags_NoInputs); - ImGui::SameLine(); - ImPlot::ColormapSlider("Slider", &t, &col, "%.3f", cmap); - - ImPlot::ColormapIcon(cmap); ImGui::SameLine(); ImGui::Text("Icon"); - - static ImPlotColormapScaleFlags flags = 0; - static float scale[2] = {0, 100}; - ImPlot::ColormapScale("Scale",scale[0],scale[1],ImVec2(0,0),"%g dB",flags,cmap); - ImGui::InputFloat2("Scale",scale); - CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_NoLabel); - CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_Opposite); - CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_Invert); -} - -//----------------------------------------------------------------------------- - -void Demo_CustomPlottersAndTooltips() { - ImGui::BulletText("You can create custom plotters or extend ImPlot using implot_internal.h."); - double dates[] = {1546300800,1546387200,1546473600,1546560000,1546819200,1546905600,1546992000,1547078400,1547164800,1547424000,1547510400,1547596800,1547683200,1547769600,1547942400,1548028800,1548115200,1548201600,1548288000,1548374400,1548633600,1548720000,1548806400,1548892800,1548979200,1549238400,1549324800,1549411200,1549497600,1549584000,1549843200,1549929600,1550016000,1550102400,1550188800,1550361600,1550448000,1550534400,1550620800,1550707200,1550793600,1551052800,1551139200,1551225600,1551312000,1551398400,1551657600,1551744000,1551830400,1551916800,1552003200,1552262400,1552348800,1552435200,1552521600,1552608000,1552867200,1552953600,1553040000,1553126400,1553212800,1553472000,1553558400,1553644800,1553731200,1553817600,1554076800,1554163200,1554249600,1554336000,1554422400,1554681600,1554768000,1554854400,1554940800,1555027200,1555286400,1555372800,1555459200,1555545600,1555632000,1555891200,1555977600,1556064000,1556150400,1556236800,1556496000,1556582400,1556668800,1556755200,1556841600,1557100800,1557187200,1557273600,1557360000,1557446400,1557705600,1557792000,1557878400,1557964800,1558051200,1558310400,1558396800,1558483200,1558569600,1558656000,1558828800,1558915200,1559001600,1559088000,1559174400,1559260800,1559520000,1559606400,1559692800,1559779200,1559865600,1560124800,1560211200,1560297600,1560384000,1560470400,1560729600,1560816000,1560902400,1560988800,1561075200,1561334400,1561420800,1561507200,1561593600,1561680000,1561939200,1562025600,1562112000,1562198400,1562284800,1562544000,1562630400,1562716800,1562803200,1562889600,1563148800,1563235200,1563321600,1563408000,1563494400,1563753600,1563840000,1563926400,1564012800,1564099200,1564358400,1564444800,1564531200,1564617600,1564704000,1564963200,1565049600,1565136000,1565222400,1565308800,1565568000,1565654400,1565740800,1565827200,1565913600,1566172800,1566259200,1566345600,1566432000,1566518400,1566777600,1566864000,1566950400,1567036800,1567123200,1567296000,1567382400,1567468800,1567555200,1567641600,1567728000,1567987200,1568073600,1568160000,1568246400,1568332800,1568592000,1568678400,1568764800,1568851200,1568937600,1569196800,1569283200,1569369600,1569456000,1569542400,1569801600,1569888000,1569974400,1570060800,1570147200,1570406400,1570492800,1570579200,1570665600,1570752000,1571011200,1571097600,1571184000,1571270400,1571356800,1571616000,1571702400,1571788800,1571875200,1571961600}; - double opens[] = {1284.7,1319.9,1318.7,1328,1317.6,1321.6,1314.3,1325,1319.3,1323.1,1324.7,1321.3,1323.5,1322,1281.3,1281.95,1311.1,1315,1314,1313.1,1331.9,1334.2,1341.3,1350.6,1349.8,1346.4,1343.4,1344.9,1335.6,1337.9,1342.5,1337,1338.6,1337,1340.4,1324.65,1324.35,1349.5,1371.3,1367.9,1351.3,1357.8,1356.1,1356,1347.6,1339.1,1320.6,1311.8,1314,1312.4,1312.3,1323.5,1319.1,1327.2,1332.1,1320.3,1323.1,1328,1330.9,1338,1333,1335.3,1345.2,1341.1,1332.5,1314,1314.4,1310.7,1314,1313.1,1315,1313.7,1320,1326.5,1329.2,1314.2,1312.3,1309.5,1297.4,1293.7,1277.9,1295.8,1295.2,1290.3,1294.2,1298,1306.4,1299.8,1302.3,1297,1289.6,1302,1300.7,1303.5,1300.5,1303.2,1306,1318.7,1315,1314.5,1304.1,1294.7,1293.7,1291.2,1290.2,1300.4,1284.2,1284.25,1301.8,1295.9,1296.2,1304.4,1323.1,1340.9,1341,1348,1351.4,1351.4,1343.5,1342.3,1349,1357.6,1357.1,1354.7,1361.4,1375.2,1403.5,1414.7,1433.2,1438,1423.6,1424.4,1418,1399.5,1435.5,1421.25,1434.1,1412.4,1409.8,1412.2,1433.4,1418.4,1429,1428.8,1420.6,1441,1460.4,1441.7,1438.4,1431,1439.3,1427.4,1431.9,1439.5,1443.7,1425.6,1457.5,1451.2,1481.1,1486.7,1512.1,1515.9,1509.2,1522.3,1513,1526.6,1533.9,1523,1506.3,1518.4,1512.4,1508.8,1545.4,1537.3,1551.8,1549.4,1536.9,1535.25,1537.95,1535.2,1556,1561.4,1525.6,1516.4,1507,1493.9,1504.9,1506.5,1513.1,1506.5,1509.7,1502,1506.8,1521.5,1529.8,1539.8,1510.9,1511.8,1501.7,1478,1485.4,1505.6,1511.6,1518.6,1498.7,1510.9,1510.8,1498.3,1492,1497.7,1484.8,1494.2,1495.6,1495.6,1487.5,1491.1,1495.1,1506.4}; - double highs[] = {1284.75,1320.6,1327,1330.8,1326.8,1321.6,1326,1328,1325.8,1327.1,1326,1326,1323.5,1322.1,1282.7,1282.95,1315.8,1316.3,1314,1333.2,1334.7,1341.7,1353.2,1354.6,1352.2,1346.4,1345.7,1344.9,1340.7,1344.2,1342.7,1342.1,1345.2,1342,1350,1324.95,1330.75,1369.6,1374.3,1368.4,1359.8,1359,1357,1356,1353.4,1340.6,1322.3,1314.1,1316.1,1312.9,1325.7,1323.5,1326.3,1336,1332.1,1330.1,1330.4,1334.7,1341.1,1344.2,1338.8,1348.4,1345.6,1342.8,1334.7,1322.3,1319.3,1314.7,1316.6,1316.4,1315,1325.4,1328.3,1332.2,1329.2,1316.9,1312.3,1309.5,1299.6,1296.9,1277.9,1299.5,1296.2,1298.4,1302.5,1308.7,1306.4,1305.9,1307,1297.2,1301.7,1305,1305.3,1310.2,1307,1308,1319.8,1321.7,1318.7,1316.2,1305.9,1295.8,1293.8,1293.7,1304.2,1302,1285.15,1286.85,1304,1302,1305.2,1323,1344.1,1345.2,1360.1,1355.3,1363.8,1353,1344.7,1353.6,1358,1373.6,1358.2,1369.6,1377.6,1408.9,1425.5,1435.9,1453.7,1438,1426,1439.1,1418,1435,1452.6,1426.65,1437.5,1421.5,1414.1,1433.3,1441.3,1431.4,1433.9,1432.4,1440.8,1462.3,1467,1443.5,1444,1442.9,1447,1437.6,1440.8,1445.7,1447.8,1458.2,1461.9,1481.8,1486.8,1522.7,1521.3,1521.1,1531.5,1546.1,1534.9,1537.7,1538.6,1523.6,1518.8,1518.4,1514.6,1540.3,1565,1554.5,1556.6,1559.8,1541.9,1542.9,1540.05,1558.9,1566.2,1561.9,1536.2,1523.8,1509.1,1506.2,1532.2,1516.6,1519.7,1515,1519.5,1512.1,1524.5,1534.4,1543.3,1543.3,1542.8,1519.5,1507.2,1493.5,1511.4,1525.8,1522.2,1518.8,1515.3,1518,1522.3,1508,1501.5,1503,1495.5,1501.1,1497.9,1498.7,1492.1,1499.4,1506.9,1520.9}; - double lows[] = {1282.85,1315,1318.7,1309.6,1317.6,1312.9,1312.4,1319.1,1319,1321,1318.1,1321.3,1319.9,1312,1280.5,1276.15,1308,1309.9,1308.5,1312.3,1329.3,1333.1,1340.2,1347,1345.9,1338,1340.8,1335,1332,1337.9,1333,1336.8,1333.2,1329.9,1340.4,1323.85,1324.05,1349,1366.3,1351.2,1349.1,1352.4,1350.7,1344.3,1338.9,1316.3,1308.4,1306.9,1309.6,1306.7,1312.3,1315.4,1319,1327.2,1317.2,1320,1323,1328,1323,1327.8,1331.7,1335.3,1336.6,1331.8,1311.4,1310,1309.5,1308,1310.6,1302.8,1306.6,1313.7,1320,1322.8,1311,1312.1,1303.6,1293.9,1293.5,1291,1277.9,1294.1,1286,1289.1,1293.5,1296.9,1298,1299.6,1292.9,1285.1,1288.5,1296.3,1297.2,1298.4,1298.6,1302,1300.3,1312,1310.8,1301.9,1292,1291.1,1286.3,1289.2,1289.9,1297.4,1283.65,1283.25,1292.9,1295.9,1290.8,1304.2,1322.7,1336.1,1341,1343.5,1345.8,1340.3,1335.1,1341.5,1347.6,1352.8,1348.2,1353.7,1356.5,1373.3,1398,1414.7,1427,1416.4,1412.7,1420.1,1396.4,1398.8,1426.6,1412.85,1400.7,1406,1399.8,1404.4,1415.5,1417.2,1421.9,1415,1413.7,1428.1,1434,1435.7,1427.5,1429.4,1423.9,1425.6,1427.5,1434.8,1422.3,1412.1,1442.5,1448.8,1468.2,1484.3,1501.6,1506.2,1498.6,1488.9,1504.5,1518.3,1513.9,1503.3,1503,1506.5,1502.1,1503,1534.8,1535.3,1541.4,1528.6,1525.6,1535.25,1528.15,1528,1542.6,1514.3,1510.7,1505.5,1492.1,1492.9,1496.8,1493.1,1503.4,1500.9,1490.7,1496.3,1505.3,1505.3,1517.9,1507.4,1507.1,1493.3,1470.5,1465,1480.5,1501.7,1501.4,1493.3,1492.1,1505.1,1495.7,1478,1487.1,1480.8,1480.6,1487,1488.3,1484.8,1484,1490.7,1490.4,1503.1}; - double closes[] = {1283.35,1315.3,1326.1,1317.4,1321.5,1317.4,1323.5,1319.2,1321.3,1323.3,1319.7,1325.1,1323.6,1313.8,1282.05,1279.05,1314.2,1315.2,1310.8,1329.1,1334.5,1340.2,1340.5,1350,1347.1,1344.3,1344.6,1339.7,1339.4,1343.7,1337,1338.9,1340.1,1338.7,1346.8,1324.25,1329.55,1369.6,1372.5,1352.4,1357.6,1354.2,1353.4,1346,1341,1323.8,1311.9,1309.1,1312.2,1310.7,1324.3,1315.7,1322.4,1333.8,1319.4,1327.1,1325.8,1330.9,1325.8,1331.6,1336.5,1346.7,1339.2,1334.7,1313.3,1316.5,1312.4,1313.4,1313.3,1312.2,1313.7,1319.9,1326.3,1331.9,1311.3,1313.4,1309.4,1295.2,1294.7,1294.1,1277.9,1295.8,1291.2,1297.4,1297.7,1306.8,1299.4,1303.6,1302.2,1289.9,1299.2,1301.8,1303.6,1299.5,1303.2,1305.3,1319.5,1313.6,1315.1,1303.5,1293,1294.6,1290.4,1291.4,1302.7,1301,1284.15,1284.95,1294.3,1297.9,1304.1,1322.6,1339.3,1340.1,1344.9,1354,1357.4,1340.7,1342.7,1348.2,1355.1,1355.9,1354.2,1362.1,1360.1,1408.3,1411.2,1429.5,1430.1,1426.8,1423.4,1425.1,1400.8,1419.8,1432.9,1423.55,1412.1,1412.2,1412.8,1424.9,1419.3,1424.8,1426.1,1423.6,1435.9,1440.8,1439.4,1439.7,1434.5,1436.5,1427.5,1432.2,1433.3,1441.8,1437.8,1432.4,1457.5,1476.5,1484.2,1519.6,1509.5,1508.5,1517.2,1514.1,1527.8,1531.2,1523.6,1511.6,1515.7,1515.7,1508.5,1537.6,1537.2,1551.8,1549.1,1536.9,1529.4,1538.05,1535.15,1555.9,1560.4,1525.5,1515.5,1511.1,1499.2,1503.2,1507.4,1499.5,1511.5,1513.4,1515.8,1506.2,1515.1,1531.5,1540.2,1512.3,1515.2,1506.4,1472.9,1489,1507.9,1513.8,1512.9,1504.4,1503.9,1512.8,1500.9,1488.7,1497.6,1483.5,1494,1498.3,1494.1,1488.1,1487.5,1495.7,1504.7,1505.3}; - static bool tooltip = true; - ImGui::Checkbox("Show Tooltip", &tooltip); - ImGui::SameLine(); - static ImVec4 bullCol = ImVec4(0.000f, 1.000f, 0.441f, 1.000f); - static ImVec4 bearCol = ImVec4(0.853f, 0.050f, 0.310f, 1.000f); - ImGui::SameLine(); ImGui::ColorEdit4("##Bull", &bullCol.x, ImGuiColorEditFlags_NoInputs); - ImGui::SameLine(); ImGui::ColorEdit4("##Bear", &bearCol.x, ImGuiColorEditFlags_NoInputs); - ImPlot::GetStyle().UseLocalTime = false; - - if (ImPlot::BeginPlot("Candlestick Chart",ImVec2(-1,0))) { - ImPlot::SetupAxes(NULL,NULL,0,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit); - ImPlot::SetupAxesLimits(1546300800, 1571961600, 1250, 1600); - ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); - ImPlot::SetupAxisLimitsConstraints(ImAxis_X1, 1546300800, 1571961600); - ImPlot::SetupAxisZoomConstraints(ImAxis_X1, 60*60*24*14, 1571961600-1546300800); - ImPlot::SetupAxisFormat(ImAxis_Y1, "$%.0f"); - MyImPlot::PlotCandlestick("GOOGL",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol); - ImPlot::EndPlot(); - } - } - -//----------------------------------------------------------------------------- -// DEMO WINDOW -//----------------------------------------------------------------------------- - -void DemoHeader(const char* label, void(*demo)()) { - if (ImGui::TreeNodeEx(label)) { - demo(); - ImGui::TreePop(); - } -} - -void ShowDemoWindow(bool* p_open) { - static bool show_implot_metrics = false; - static bool show_implot_style_editor = false; - static bool show_imgui_metrics = false; - static bool show_imgui_style_editor = false; - static bool show_imgui_demo = false; - - if (show_implot_metrics) { - ImPlot::ShowMetricsWindow(&show_implot_metrics); - } - if (show_implot_style_editor) { - ImGui::SetNextWindowSize(ImVec2(415,762), ImGuiCond_Appearing); - ImGui::Begin("Style Editor (ImPlot)", &show_implot_style_editor); - ImPlot::ShowStyleEditor(); - ImGui::End(); - } - if (show_imgui_style_editor) { - ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor); - ImGui::ShowStyleEditor(); - ImGui::End(); - } - if (show_imgui_metrics) { - ImGui::ShowMetricsWindow(&show_imgui_metrics); - } - if (show_imgui_demo) { - ImGui::ShowDemoWindow(&show_imgui_demo); - } - ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(600, 750), ImGuiCond_FirstUseEver); - ImGui::Begin("ImPlot Demo", p_open, ImGuiWindowFlags_MenuBar); - if (ImGui::BeginMenuBar()) { - if (ImGui::BeginMenu("Tools")) { - ImGui::MenuItem("Metrics", NULL, &show_implot_metrics); - ImGui::MenuItem("Style Editor", NULL, &show_implot_style_editor); - ImGui::Separator(); - ImGui::MenuItem("ImGui Metrics", NULL, &show_imgui_metrics); - ImGui::MenuItem("ImGui Style Editor", NULL, &show_imgui_style_editor); - ImGui::MenuItem("ImGui Demo", NULL, &show_imgui_demo); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - //------------------------------------------------------------------------- - ImGui::Text("ImPlot says hello. (%s)", IMPLOT_VERSION); - // display warning about 16-bit indices - static bool showWarning = sizeof(ImDrawIdx)*8 == 16 && (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) == false; - if (showWarning) { - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); - ImGui::TextWrapped("WARNING: ImDrawIdx is 16-bit and ImGuiBackendFlags_RendererHasVtxOffset is false. Expect visual glitches and artifacts! See README for more information."); - ImGui::PopStyleColor(); - } - - ImGui::Spacing(); - - if (ImGui::BeginTabBar("ImPlotDemoTabs")) { - if (ImGui::BeginTabItem("Plots")) { - DemoHeader("Line Plots", Demo_LinePlots); - DemoHeader("Filled Line Plots", Demo_FilledLinePlots); - DemoHeader("Shaded Plots##", Demo_ShadedPlots); - DemoHeader("Scatter Plots", Demo_ScatterPlots); - DemoHeader("Realtime Plots", Demo_RealtimePlots); - DemoHeader("Stairstep Plots", Demo_StairstepPlots); - DemoHeader("Bar Plots", Demo_BarPlots); - DemoHeader("Bar Groups", Demo_BarGroups); - DemoHeader("Bar Stacks", Demo_BarStacks); - DemoHeader("Error Bars", Demo_ErrorBars); - DemoHeader("Stem Plots##", Demo_StemPlots); - DemoHeader("Infinite Lines", Demo_InfiniteLines); - DemoHeader("Pie Charts", Demo_PieCharts); - DemoHeader("Heatmaps", Demo_Heatmaps); - DemoHeader("Histogram", Demo_Histogram); - DemoHeader("Histogram 2D", Demo_Histogram2D); - DemoHeader("Digital Plots", Demo_DigitalPlots); - DemoHeader("Images", Demo_Images); - DemoHeader("Markers and Text", Demo_MarkersAndText); - DemoHeader("NaN Values", Demo_NaNValues); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Subplots")) { - DemoHeader("Sizing", Demo_SubplotsSizing); - DemoHeader("Item Sharing", Demo_SubplotItemSharing); - DemoHeader("Axis Linking", Demo_SubplotAxisLinking); - DemoHeader("Tables", Demo_Tables); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Axes")) { - DemoHeader("Log Scale", Demo_LogScale); - DemoHeader("Symmetric Log Scale", Demo_SymmetricLogScale); - DemoHeader("Time Scale", Demo_TimeScale); - DemoHeader("Custom Scale", Demo_CustomScale); - DemoHeader("Multiple Axes", Demo_MultipleAxes); - DemoHeader("Tick Labels", Demo_TickLabels); - DemoHeader("Linked Axes", Demo_LinkedAxes); - DemoHeader("Axis Constraints", Demo_AxisConstraints); - DemoHeader("Equal Axes", Demo_EqualAxes); - DemoHeader("Auto-Fitting Data", Demo_AutoFittingData); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Tools")) { - DemoHeader("Offset and Stride", Demo_OffsetAndStride); - DemoHeader("Drag Points", Demo_DragPoints); - DemoHeader("Drag Lines", Demo_DragLines); - DemoHeader("Drag Rects", Demo_DragRects); - DemoHeader("Querying", Demo_Querying); - DemoHeader("Annotations", Demo_Annotations); - DemoHeader("Tags", Demo_Tags); - DemoHeader("Drag and Drop", Demo_DragAndDrop); - DemoHeader("Legend Options", Demo_LegendOptions); - DemoHeader("Legend Popups", Demo_LegendPopups); - DemoHeader("Colormap Widgets", Demo_ColormapWidgets); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Custom")) { - DemoHeader("Custom Styles", Demo_CustomStyles); - DemoHeader("Custom Data and Getters", Demo_CustomDataAndGetters); - DemoHeader("Custom Rendering", Demo_CustomRendering); - DemoHeader("Custom Plotters and Tooltips", Demo_CustomPlottersAndTooltips); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Config")) { - Demo_Config(); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Help")) { - Demo_Help(); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } - ImGui::End(); -} - -} // namespace ImPlot - -namespace MyImPlot { - -ImPlotPoint SineWave(int idx, void* data) { - WaveData* wd = (WaveData*)data; - double x = idx * wd->X; - return ImPlotPoint(x, wd->Offset + wd->Amp * sin(2 * 3.14 * wd->Freq * x)); -} - -ImPlotPoint SawWave(int idx, void* data) { - WaveData* wd = (WaveData*)data; - double x = idx * wd->X; - return ImPlotPoint(x, wd->Offset + wd->Amp * (-2 / 3.14 * atan(cos(3.14 * wd->Freq * x) / sin(3.14 * wd->Freq * x)))); -} - -ImPlotPoint Spiral(int idx, void*) { - float r = 0.9f; // outer radius - float a = 0; // inner radius - float b = 0.05f; // increment per rev - float n = (r - a) / b; // number of revolutions - double th = 2 * n * 3.14; // angle - float Th = float(th * idx / (1000 - 1)); - return ImPlotPoint(0.5f+(a + b*Th / (2.0f * (float) 3.14))*cos(Th), - 0.5f + (a + b*Th / (2.0f * (float)3.14))*sin(Th)); -} - -void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size) { - ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0)); - if (ImPlot::BeginPlot(id,size,ImPlotFlags_CanvasOnly|ImPlotFlags_NoChild)) { - ImPlot::SetupAxes(0,0,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); - ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always); - ImPlot::SetNextLineStyle(col); - ImPlot::SetNextFillStyle(col, 0.25); - ImPlot::PlotLine(id, values, count, 1, 0, ImPlotLineFlags_Shaded, offset); - ImPlot::EndPlot(); - } - ImPlot::PopStyleVar(); -} - -void StyleSeaborn() { - - ImPlotStyle& style = ImPlot::GetStyle(); - - ImVec4* colors = style.Colors; - colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; - colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; - colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; - colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; - colors[ImPlotCol_ErrorBar] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_PlotBg] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f); - colors[ImPlotCol_PlotBorder] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImPlotCol_LegendBg] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f); - colors[ImPlotCol_LegendBorder] = ImVec4(0.80f, 0.81f, 0.85f, 1.00f); - colors[ImPlotCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_AxisBgHovered] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f); - colors[ImPlotCol_AxisBgActive] = ImVec4(0.92f, 0.92f, 0.95f, 0.75f); - colors[ImPlotCol_Selection] = ImVec4(1.00f, 0.65f, 0.00f, 1.00f); - colors[ImPlotCol_Crosshairs] = ImVec4(0.23f, 0.10f, 0.64f, 0.50f); - - style.LineWeight = 1.5; - style.Marker = ImPlotMarker_None; - style.MarkerSize = 4; - style.MarkerWeight = 1; - style.FillAlpha = 1.0f; - style.ErrorBarSize = 5; - style.ErrorBarWeight = 1.5f; - style.DigitalBitHeight = 8; - style.DigitalBitGap = 4; - style.PlotBorderSize = 0; - style.MinorAlpha = 1.0f; - style.MajorTickLen = ImVec2(0,0); - style.MinorTickLen = ImVec2(0,0); - style.MajorTickSize = ImVec2(0,0); - style.MinorTickSize = ImVec2(0,0); - style.MajorGridSize = ImVec2(1.2f,1.2f); - style.MinorGridSize = ImVec2(1.2f,1.2f); - style.PlotPadding = ImVec2(12,12); - style.LabelPadding = ImVec2(5,5); - style.LegendPadding = ImVec2(5,5); - style.MousePosPadding = ImVec2(5,5); - style.PlotMinSize = ImVec2(300,225); -} - -} // namespaece MyImPlot - -// WARNING: -// -// You can use "implot_internal.h" to build custom plotting fuctions or extend ImPlot. -// However, note that forward compatibility of this file is not guaranteed and the -// internal API is subject to change. At some point we hope to bring more of this -// into the public API and expose the necessary building blocks to fully support -// custom plotters. For now, proceed at your own risk! - -#include "implot_internal.h" - -namespace MyImPlot { - -template -int BinarySearch(const T* arr, int l, int r, T x) { - if (r >= l) { - int mid = l + (r - l) / 2; - if (arr[mid] == x) - return mid; - if (arr[mid] > x) - return BinarySearch(arr, l, mid - 1, x); - return BinarySearch(arr, mid + 1, r, x); - } - return -1; -} - -void PlotCandlestick(const char* label_id, const double* xs, const double* opens, const double* closes, const double* lows, const double* highs, int count, bool tooltip, float width_percent, ImVec4 bullCol, ImVec4 bearCol) { - - // get ImGui window DrawList - ImDrawList* draw_list = ImPlot::GetPlotDrawList(); - // calc real value width - double half_width = count > 1 ? (xs[1] - xs[0]) * width_percent : width_percent; - - // custom tool - if (ImPlot::IsPlotHovered() && tooltip) { - ImPlotPoint mouse = ImPlot::GetPlotMousePos(); - mouse.x = ImPlot::RoundTime(ImPlotTime::FromDouble(mouse.x), ImPlotTimeUnit_Day).ToDouble(); - float tool_l = ImPlot::PlotToPixels(mouse.x - half_width * 1.5, mouse.y).x; - float tool_r = ImPlot::PlotToPixels(mouse.x + half_width * 1.5, mouse.y).x; - float tool_t = ImPlot::GetPlotPos().y; - float tool_b = tool_t + ImPlot::GetPlotSize().y; - ImPlot::PushPlotClipRect(); - draw_list->AddRectFilled(ImVec2(tool_l, tool_t), ImVec2(tool_r, tool_b), IM_COL32(128,128,128,64)); - ImPlot::PopPlotClipRect(); - // find mouse location index - int idx = BinarySearch(xs, 0, count - 1, mouse.x); - // render tool tip (won't be affected by plot clip rect) - if (idx != -1) { - ImGui::BeginTooltip(); - char buff[32]; - ImPlot::FormatDate(ImPlotTime::FromDouble(xs[idx]),buff,32,ImPlotDateFmt_DayMoYr,ImPlot::GetStyle().UseISO8601); - ImGui::Text("Day: %s", buff); - ImGui::Text("Open: $%.2f", opens[idx]); - ImGui::Text("Close: $%.2f", closes[idx]); - ImGui::Text("Low: $%.2f", lows[idx]); - ImGui::Text("High: $%.2f", highs[idx]); - ImGui::EndTooltip(); - } - } - - // begin plot item - if (ImPlot::BeginItem(label_id)) { - // override legend icon color - ImPlot::GetCurrentItem()->Color = IM_COL32(64,64,64,255); - // fit data if requested - if (ImPlot::FitThisFrame()) { - for (int i = 0; i < count; ++i) { - ImPlot::FitPoint(ImPlotPoint(xs[i], lows[i])); - ImPlot::FitPoint(ImPlotPoint(xs[i], highs[i])); - } - } - // render data - for (int i = 0; i < count; ++i) { - ImVec2 open_pos = ImPlot::PlotToPixels(xs[i] - half_width, opens[i]); - ImVec2 close_pos = ImPlot::PlotToPixels(xs[i] + half_width, closes[i]); - ImVec2 low_pos = ImPlot::PlotToPixels(xs[i], lows[i]); - ImVec2 high_pos = ImPlot::PlotToPixels(xs[i], highs[i]); - ImU32 color = ImGui::GetColorU32(opens[i] > closes[i] ? bearCol : bullCol); - draw_list->AddLine(low_pos, high_pos, color); - draw_list->AddRectFilled(open_pos, close_pos, color); - } - - // end plot item - ImPlot::EndItem(); - } -} - -} // namespace MyImplot diff --git a/libs/zgui/libs/imgui/implot_internal.h b/libs/zgui/libs/imgui/implot_internal.h deleted file mode 100644 index 8c73d4a04..000000000 --- a/libs/zgui/libs/imgui/implot_internal.h +++ /dev/null @@ -1,1656 +0,0 @@ -// MIT License - -// Copyright (c) 2022 Evan Pezent - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// ImPlot v0.14 - -// You may use this file to debug, understand or extend ImPlot features but we -// don't provide any guarantee of forward compatibility! - -//----------------------------------------------------------------------------- -// [SECTION] Header Mess -//----------------------------------------------------------------------------- - -#pragma once - -#ifndef IMGUI_DEFINE_MATH_OPERATORS -#define IMGUI_DEFINE_MATH_OPERATORS -#endif - -#include -#include "imgui_internal.h" - -#ifndef IMPLOT_VERSION -#error Must include implot.h before implot_internal.h -#endif - - -// Support for pre-1.84 versions. ImPool's GetSize() -> GetBufSize() -#if (IMGUI_VERSION_NUM < 18303) -#define GetBufSize GetSize -#endif - -//----------------------------------------------------------------------------- -// [SECTION] Constants -//----------------------------------------------------------------------------- - -// Constants can be changed unless stated otherwise. We may move some of these -// to ImPlotStyleVar_ over time. - -// Mimimum allowable timestamp value 01/01/1970 @ 12:00am (UTC) (DO NOT DECREASE THIS) -#define IMPLOT_MIN_TIME 0 -// Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS) -#define IMPLOT_MAX_TIME 32503680000 -// Default label format for axis labels -#define IMPLOT_LABEL_FORMAT "%g" -// Max character size for tick labels -#define IMPLOT_LABEL_MAX_SIZE 32 - -//----------------------------------------------------------------------------- -// [SECTION] Macros -//----------------------------------------------------------------------------- - -#define IMPLOT_NUM_X_AXES ImAxis_Y1 -#define IMPLOT_NUM_Y_AXES (ImAxis_COUNT - IMPLOT_NUM_X_AXES) - -// Split ImU32 color into RGB components [0 255] -#define IM_COL32_SPLIT_RGB(col,r,g,b) \ - ImU32 r = ((col >> IM_COL32_R_SHIFT) & 0xFF); \ - ImU32 g = ((col >> IM_COL32_G_SHIFT) & 0xFF); \ - ImU32 b = ((col >> IM_COL32_B_SHIFT) & 0xFF); - -//----------------------------------------------------------------------------- -// [SECTION] Forward Declarations -//----------------------------------------------------------------------------- - -struct ImPlotTick; -struct ImPlotAxis; -struct ImPlotAxisColor; -struct ImPlotItem; -struct ImPlotLegend; -struct ImPlotPlot; -struct ImPlotNextPlotData; -struct ImPlotTicker; - -//----------------------------------------------------------------------------- -// [SECTION] Context Pointer -//----------------------------------------------------------------------------- - -#ifndef GImPlot -extern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer -#endif - -//----------------------------------------------------------------------------- -// [SECTION] Generic Helpers -//----------------------------------------------------------------------------- - -// Computes the common (base-10) logarithm -static inline float ImLog10(float x) { return log10f(x); } -static inline double ImLog10(double x) { return log10(x); } -static inline float ImSinh(float x) { return sinhf(x); } -static inline double ImSinh(double x) { return sinh(x); } -static inline float ImAsinh(float x) { return asinhf(x); } -static inline double ImAsinh(double x) { return asinh(x); } -// Returns true if a flag is set -template -static inline bool ImHasFlag(TSet set, TFlag flag) { return (set & flag) == flag; } -// Flips a flag in a flagset -template -static inline void ImFlipFlag(TSet& set, TFlag flag) { ImHasFlag(set, flag) ? set &= ~flag : set |= flag; } -// Linearly remaps x from [x0 x1] to [y0 y1]. -template -static inline T ImRemap(T x, T x0, T x1, T y0, T y1) { return y0 + (x - x0) * (y1 - y0) / (x1 - x0); } -// Linear rempas x from [x0 x1] to [0 1] -template -static inline T ImRemap01(T x, T x0, T x1) { return (x - x0) / (x1 - x0); } -// Returns always positive modulo (assumes r != 0) -static inline int ImPosMod(int l, int r) { return (l % r + r) % r; } -// Returns true if val is NAN -static inline bool ImNan(double val) { return isnan(val); } -// Returns true if val is NAN or INFINITY -static inline bool ImNanOrInf(double val) { return !(val >= -DBL_MAX && val <= DBL_MAX) || ImNan(val); } -// Turns NANs to 0s -static inline double ImConstrainNan(double val) { return ImNan(val) ? 0 : val; } -// Turns infinity to floating point maximums -static inline double ImConstrainInf(double val) { return val >= DBL_MAX ? DBL_MAX : val <= -DBL_MAX ? - DBL_MAX : val; } -// Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?) -static inline double ImConstrainLog(double val) { return val <= 0 ? 0.001f : val; } -// Turns numbers less than 0 to zero -static inline double ImConstrainTime(double val) { return val < IMPLOT_MIN_TIME ? IMPLOT_MIN_TIME : (val > IMPLOT_MAX_TIME ? IMPLOT_MAX_TIME : val); } -// True if two numbers are approximately equal using units in the last place. -static inline bool ImAlmostEqual(double v1, double v2, int ulp = 2) { return ImAbs(v1-v2) < DBL_EPSILON * ImAbs(v1+v2) * ulp || ImAbs(v1-v2) < DBL_MIN; } -// Finds min value in an unsorted array -template -static inline T ImMinArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] < m) { m = values[i]; } } return m; } -// Finds the max value in an unsorted array -template -static inline T ImMaxArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] > m) { m = values[i]; } } return m; } -// Finds the min and max value in an unsorted array -template -static inline void ImMinMaxArray(const T* values, int count, T* min_out, T* max_out) { - T Min = values[0]; T Max = values[0]; - for (int i = 1; i < count; ++i) { - if (values[i] < Min) { Min = values[i]; } - if (values[i] > Max) { Max = values[i]; } - } - *min_out = Min; *max_out = Max; -} -// Finds the sim of an array -template -static inline T ImSum(const T* values, int count) { - T sum = 0; - for (int i = 0; i < count; ++i) - sum += values[i]; - return sum; -} -// Finds the mean of an array -template -static inline double ImMean(const T* values, int count) { - double den = 1.0 / count; - double mu = 0; - for (int i = 0; i < count; ++i) - mu += values[i] * den; - return mu; -} -// Finds the sample standard deviation of an array -template -static inline double ImStdDev(const T* values, int count) { - double den = 1.0 / (count - 1.0); - double mu = ImMean(values, count); - double x = 0; - for (int i = 0; i < count; ++i) - x += (values[i] - mu) * (values[i] - mu) * den; - return sqrt(x); -} -// Mix color a and b by factor s in [0 256] -static inline ImU32 ImMixU32(ImU32 a, ImU32 b, ImU32 s) { -#ifdef IMPLOT_MIX64 - const ImU32 af = 256-s; - const ImU32 bf = s; - const ImU64 al = (a & 0x00ff00ff) | (((ImU64)(a & 0xff00ff00)) << 24); - const ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24); - const ImU64 mix = (al * af + bl * bf); - return ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8); -#else - const ImU32 af = 256-s; - const ImU32 bf = s; - const ImU32 al = (a & 0x00ff00ff); - const ImU32 ah = (a & 0xff00ff00) >> 8; - const ImU32 bl = (b & 0x00ff00ff); - const ImU32 bh = (b & 0xff00ff00) >> 8; - const ImU32 ml = (al * af + bl * bf); - const ImU32 mh = (ah * af + bh * bf); - return (mh & 0xff00ff00) | ((ml & 0xff00ff00) >> 8); -#endif -} - -// Lerp across an array of 32-bit collors given t in [0.0 1.0] -static inline ImU32 ImLerpU32(const ImU32* colors, int size, float t) { - int i1 = (int)((size - 1 ) * t); - int i2 = i1 + 1; - if (i2 == size || size == 1) - return colors[i1]; - float den = 1.0f / (size - 1); - float t1 = i1 * den; - float t2 = i2 * den; - float tr = ImRemap01(t, t1, t2); - return ImMixU32(colors[i1], colors[i2], (ImU32)(tr*256)); -} - -// Set alpha channel of 32-bit color from float in range [0.0 1.0] -static inline ImU32 ImAlphaU32(ImU32 col, float alpha) { - return col & ~((ImU32)((1.0f-alpha)*255)< -static inline bool ImOverlaps(T min_a, T max_a, T min_b, T max_b) { - return min_a <= max_b && min_b <= max_a; -} - -//----------------------------------------------------------------------------- -// [SECTION] ImPlot Enums -//----------------------------------------------------------------------------- - -typedef int ImPlotTimeUnit; // -> enum ImPlotTimeUnit_ -typedef int ImPlotDateFmt; // -> enum ImPlotDateFmt_ -typedef int ImPlotTimeFmt; // -> enum ImPlotTimeFmt_ - -enum ImPlotTimeUnit_ { - ImPlotTimeUnit_Us, // microsecond - ImPlotTimeUnit_Ms, // millisecond - ImPlotTimeUnit_S, // second - ImPlotTimeUnit_Min, // minute - ImPlotTimeUnit_Hr, // hour - ImPlotTimeUnit_Day, // day - ImPlotTimeUnit_Mo, // month - ImPlotTimeUnit_Yr, // year - ImPlotTimeUnit_COUNT -}; - -enum ImPlotDateFmt_ { // default [ ISO 8601 ] - ImPlotDateFmt_None = 0, - ImPlotDateFmt_DayMo, // 10/3 [ --10-03 ] - ImPlotDateFmt_DayMoYr, // 10/3/91 [ 1991-10-03 ] - ImPlotDateFmt_MoYr, // Oct 1991 [ 1991-10 ] - ImPlotDateFmt_Mo, // Oct [ --10 ] - ImPlotDateFmt_Yr // 1991 [ 1991 ] -}; - -enum ImPlotTimeFmt_ { // default [ 24 Hour Clock ] - ImPlotTimeFmt_None = 0, - ImPlotTimeFmt_Us, // .428 552 [ .428 552 ] - ImPlotTimeFmt_SUs, // :29.428 552 [ :29.428 552 ] - ImPlotTimeFmt_SMs, // :29.428 [ :29.428 ] - ImPlotTimeFmt_S, // :29 [ :29 ] - ImPlotTimeFmt_HrMinSMs, // 7:21:29.428pm [ 19:21:29.428 ] - ImPlotTimeFmt_HrMinS, // 7:21:29pm [ 19:21:29 ] - ImPlotTimeFmt_HrMin, // 7:21pm [ 19:21 ] - ImPlotTimeFmt_Hr // 7pm [ 19:00 ] -}; - -//----------------------------------------------------------------------------- -// [SECTION] Callbacks -//----------------------------------------------------------------------------- - -typedef void (*ImPlotLocator)(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); - -//----------------------------------------------------------------------------- -// [SECTION] Structs -//----------------------------------------------------------------------------- - -// Combined date/time format spec -struct ImPlotDateTimeSpec { - ImPlotDateTimeSpec() {} - ImPlotDateTimeSpec(ImPlotDateFmt date_fmt, ImPlotTimeFmt time_fmt, bool use_24_hr_clk = false, bool use_iso_8601 = false) { - Date = date_fmt; - Time = time_fmt; - UseISO8601 = use_iso_8601; - Use24HourClock = use_24_hr_clk; - } - ImPlotDateFmt Date; - ImPlotTimeFmt Time; - bool UseISO8601; - bool Use24HourClock; -}; - -// Two part timestamp struct. -struct ImPlotTime { - time_t S; // second part - int Us; // microsecond part - ImPlotTime() { S = 0; Us = 0; } - ImPlotTime(time_t s, int us = 0) { S = s + us / 1000000; Us = us % 1000000; } - void RollOver() { S = S + Us / 1000000; Us = Us % 1000000; } - double ToDouble() const { return (double)S + (double)Us / 1000000.0; } - static ImPlotTime FromDouble(double t) { return ImPlotTime((time_t)t, (int)(t * 1000000 - floor(t) * 1000000)); } -}; - -static inline ImPlotTime operator+(const ImPlotTime& lhs, const ImPlotTime& rhs) -{ return ImPlotTime(lhs.S + rhs.S, lhs.Us + rhs.Us); } -static inline ImPlotTime operator-(const ImPlotTime& lhs, const ImPlotTime& rhs) -{ return ImPlotTime(lhs.S - rhs.S, lhs.Us - rhs.Us); } -static inline bool operator==(const ImPlotTime& lhs, const ImPlotTime& rhs) -{ return lhs.S == rhs.S && lhs.Us == rhs.Us; } -static inline bool operator<(const ImPlotTime& lhs, const ImPlotTime& rhs) -{ return lhs.S == rhs.S ? lhs.Us < rhs.Us : lhs.S < rhs.S; } -static inline bool operator>(const ImPlotTime& lhs, const ImPlotTime& rhs) -{ return rhs < lhs; } -static inline bool operator<=(const ImPlotTime& lhs, const ImPlotTime& rhs) -{ return lhs < rhs || lhs == rhs; } -static inline bool operator>=(const ImPlotTime& lhs, const ImPlotTime& rhs) -{ return lhs > rhs || lhs == rhs; } - -// Colormap data storage -struct ImPlotColormapData { - ImVector Keys; - ImVector KeyCounts; - ImVector KeyOffsets; - ImVector Tables; - ImVector TableSizes; - ImVector TableOffsets; - ImGuiTextBuffer Text; - ImVector TextOffsets; - ImVector Quals; - ImGuiStorage Map; - int Count; - - ImPlotColormapData() { Count = 0; } - - int Append(const char* name, const ImU32* keys, int count, bool qual) { - if (GetIndex(name) != -1) - return -1; - KeyOffsets.push_back(Keys.size()); - KeyCounts.push_back(count); - Keys.reserve(Keys.size()+count); - for (int i = 0; i < count; ++i) - Keys.push_back(keys[i]); - TextOffsets.push_back(Text.size()); - Text.append(name, name + strlen(name) + 1); - Quals.push_back(qual); - ImGuiID id = ImHashStr(name); - int idx = Count++; - Map.SetInt(id,idx); - _AppendTable(idx); - return idx; - } - - void _AppendTable(ImPlotColormap cmap) { - int key_count = GetKeyCount(cmap); - const ImU32* keys = GetKeys(cmap); - int off = Tables.size(); - TableOffsets.push_back(off); - if (IsQual(cmap)) { - Tables.reserve(key_count); - for (int i = 0; i < key_count; ++i) - Tables.push_back(keys[i]); - TableSizes.push_back(key_count); - } - else { - int max_size = 255 * (key_count-1) + 1; - Tables.reserve(off + max_size); - // ImU32 last = keys[0]; - // Tables.push_back(last); - // int n = 1; - for (int i = 0; i < key_count-1; ++i) { - for (int s = 0; s < 255; ++s) { - ImU32 a = keys[i]; - ImU32 b = keys[i+1]; - ImU32 c = ImMixU32(a,b,s); - // if (c != last) { - Tables.push_back(c); - // last = c; - // n++; - // } - } - } - ImU32 c = keys[key_count-1]; - // if (c != last) { - Tables.push_back(c); - // n++; - // } - // TableSizes.push_back(n); - TableSizes.push_back(max_size); - } - } - - void RebuildTables() { - Tables.resize(0); - TableSizes.resize(0); - TableOffsets.resize(0); - for (int i = 0; i < Count; ++i) - _AppendTable(i); - } - - inline bool IsQual(ImPlotColormap cmap) const { return Quals[cmap]; } - inline const char* GetName(ImPlotColormap cmap) const { return cmap < Count ? Text.Buf.Data + TextOffsets[cmap] : NULL; } - inline ImPlotColormap GetIndex(const char* name) const { ImGuiID key = ImHashStr(name); return Map.GetInt(key,-1); } - - inline const ImU32* GetKeys(ImPlotColormap cmap) const { return &Keys[KeyOffsets[cmap]]; } - inline int GetKeyCount(ImPlotColormap cmap) const { return KeyCounts[cmap]; } - inline ImU32 GetKeyColor(ImPlotColormap cmap, int idx) const { return Keys[KeyOffsets[cmap]+idx]; } - inline void SetKeyColor(ImPlotColormap cmap, int idx, ImU32 value) { Keys[KeyOffsets[cmap]+idx] = value; RebuildTables(); } - - inline const ImU32* GetTable(ImPlotColormap cmap) const { return &Tables[TableOffsets[cmap]]; } - inline int GetTableSize(ImPlotColormap cmap) const { return TableSizes[cmap]; } - inline ImU32 GetTableColor(ImPlotColormap cmap, int idx) const { return Tables[TableOffsets[cmap]+idx]; } - - inline ImU32 LerpTable(ImPlotColormap cmap, float t) const { - int off = TableOffsets[cmap]; - int siz = TableSizes[cmap]; - int idx = Quals[cmap] ? ImClamp((int)(siz*t),0,siz-1) : (int)((siz - 1) * t + 0.5f); - return Tables[off + idx]; - } -}; - -// ImPlotPoint with positive/negative error values -struct ImPlotPointError { - double X, Y, Neg, Pos; - ImPlotPointError(double x, double y, double neg, double pos) { - X = x; Y = y; Neg = neg; Pos = pos; - } -}; - -// Interior plot label/annotation -struct ImPlotAnnotation { - ImVec2 Pos; - ImVec2 Offset; - ImU32 ColorBg; - ImU32 ColorFg; - int TextOffset; - bool Clamp; -}; - -// Collection of plot labels -struct ImPlotAnnotationCollection { - - ImVector Annotations; - ImGuiTextBuffer TextBuffer; - int Size; - - ImPlotAnnotationCollection() { Reset(); } - - void AppendV(const ImVec2& pos, const ImVec2& off, ImU32 bg, ImU32 fg, bool clamp, const char* fmt, va_list args) IM_FMTLIST(7) { - ImPlotAnnotation an; - an.Pos = pos; an.Offset = off; - an.ColorBg = bg; an.ColorFg = fg; - an.TextOffset = TextBuffer.size(); - an.Clamp = clamp; - Annotations.push_back(an); - TextBuffer.appendfv(fmt, args); - const char nul[] = ""; - TextBuffer.append(nul,nul+1); - Size++; - } - - void Append(const ImVec2& pos, const ImVec2& off, ImU32 bg, ImU32 fg, bool clamp, const char* fmt, ...) IM_FMTARGS(7) { - va_list args; - va_start(args, fmt); - AppendV(pos, off, bg, fg, clamp, fmt, args); - va_end(args); - } - - const char* GetText(int idx) { - return TextBuffer.Buf.Data + Annotations[idx].TextOffset; - } - - void Reset() { - Annotations.shrink(0); - TextBuffer.Buf.shrink(0); - Size = 0; - } -}; - -struct ImPlotTag { - ImAxis Axis; - double Value; - ImU32 ColorBg; - ImU32 ColorFg; - int TextOffset; -}; - -struct ImPlotTagCollection { - - ImVector Tags; - ImGuiTextBuffer TextBuffer; - int Size; - - ImPlotTagCollection() { Reset(); } - - void AppendV(ImAxis axis, double value, ImU32 bg, ImU32 fg, const char* fmt, va_list args) IM_FMTLIST(6) { - ImPlotTag tag; - tag.Axis = axis; - tag.Value = value; - tag.ColorBg = bg; - tag.ColorFg = fg; - tag.TextOffset = TextBuffer.size(); - Tags.push_back(tag); - TextBuffer.appendfv(fmt, args); - const char nul[] = ""; - TextBuffer.append(nul,nul+1); - Size++; - } - - void Append(ImAxis axis, double value, ImU32 bg, ImU32 fg, const char* fmt, ...) IM_FMTARGS(6) { - va_list args; - va_start(args, fmt); - AppendV(axis, value, bg, fg, fmt, args); - va_end(args); - } - - const char* GetText(int idx) { - return TextBuffer.Buf.Data + Tags[idx].TextOffset; - } - - void Reset() { - Tags.shrink(0); - TextBuffer.Buf.shrink(0); - Size = 0; - } -}; - -// Tick mark info -struct ImPlotTick -{ - double PlotPos; - float PixelPos; - ImVec2 LabelSize; - int TextOffset; - bool Major; - bool ShowLabel; - int Level; - int Idx; - - ImPlotTick(double value, bool major, int level, bool show_label) { - PlotPos = value; - Major = major; - ShowLabel = show_label; - Level = level; - TextOffset = -1; - } -}; - -// Collection of ticks -struct ImPlotTicker { - ImVector Ticks; - ImGuiTextBuffer TextBuffer; - ImVec2 MaxSize; - ImVec2 LateSize; - int Levels; - - ImPlotTicker() { - Reset(); - } - - ImPlotTick& AddTick(double value, bool major, int level, bool show_label, const char* label) { - ImPlotTick tick(value, major, level, show_label); - if (show_label && label != NULL) { - tick.TextOffset = TextBuffer.size(); - TextBuffer.append(label, label + strlen(label) + 1); - tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset); - } - return AddTick(tick); - } - - ImPlotTick& AddTick(double value, bool major, int level, bool show_label, ImPlotFormatter formatter, void* data) { - ImPlotTick tick(value, major, level, show_label); - if (show_label && formatter != NULL) { - char buff[IMPLOT_LABEL_MAX_SIZE]; - tick.TextOffset = TextBuffer.size(); - formatter(tick.PlotPos, buff, sizeof(buff), data); - TextBuffer.append(buff, buff + strlen(buff) + 1); - tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset); - } - return AddTick(tick); - } - - inline ImPlotTick& AddTick(ImPlotTick tick) { - if (tick.ShowLabel) { - MaxSize.x = tick.LabelSize.x > MaxSize.x ? tick.LabelSize.x : MaxSize.x; - MaxSize.y = tick.LabelSize.y > MaxSize.y ? tick.LabelSize.y : MaxSize.y; - } - tick.Idx = Ticks.size(); - Ticks.push_back(tick); - return Ticks.back(); - } - - const char* GetText(int idx) const { - return TextBuffer.Buf.Data + Ticks[idx].TextOffset; - } - - const char* GetText(const ImPlotTick& tick) { - return GetText(tick.Idx); - } - - void OverrideSizeLate(const ImVec2& size) { - LateSize.x = size.x > LateSize.x ? size.x : LateSize.x; - LateSize.y = size.y > LateSize.y ? size.y : LateSize.y; - } - - void Reset() { - Ticks.shrink(0); - TextBuffer.Buf.shrink(0); - MaxSize = LateSize; - LateSize = ImVec2(0,0); - Levels = 1; - } - - int TickCount() const { - return Ticks.Size; - } -}; - -// Axis state information that must persist after EndPlot -struct ImPlotAxis -{ - ImGuiID ID; - ImPlotAxisFlags Flags; - ImPlotAxisFlags PreviousFlags; - ImPlotRange Range; - ImPlotCond RangeCond; - ImPlotScale Scale; - ImPlotRange FitExtents; - ImPlotAxis* OrthoAxis; - ImPlotRange ConstraintRange; - ImPlotRange ConstraintZoom; - - ImPlotTicker Ticker; - ImPlotFormatter Formatter; - void* FormatterData; - char FormatSpec[16]; - ImPlotLocator Locator; - - double* LinkedMin; - double* LinkedMax; - - int PickerLevel; - ImPlotTime PickerTimeMin, PickerTimeMax; - - ImPlotTransform TransformForward; - ImPlotTransform TransformInverse; - void* TransformData; - float PixelMin, PixelMax; - double ScaleMin, ScaleMax; - double ScaleToPixel; - float Datum1, Datum2; - - ImRect HoverRect; - int LabelOffset; - ImU32 ColorMaj, ColorMin, ColorTick, ColorTxt, ColorBg, ColorHov, ColorAct, ColorHiLi; - - bool Enabled; - bool Vertical; - bool FitThisFrame; - bool HasRange; - bool HasFormatSpec; - bool ShowDefaultTicks; - bool Hovered; - bool Held; - - ImPlotAxis() { - Flags = PreviousFlags = ImPlotAxisFlags_None; - Range.Min = 0; - Range.Max = 1; - Scale = ImPlotScale_Linear; - TransformForward = TransformInverse = NULL; - TransformData = NULL; - FitExtents.Min = HUGE_VAL; - FitExtents.Max = -HUGE_VAL; - OrthoAxis = NULL; - ConstraintRange = ImPlotRange(-INFINITY,INFINITY); - ConstraintZoom = ImPlotRange(DBL_MIN,INFINITY); - LinkedMin = LinkedMax = NULL; - PickerLevel = 0; - Datum1 = Datum2 = 0; - PixelMin = PixelMax = 0; - LabelOffset = -1; - ColorMaj = ColorMin = ColorTick = ColorTxt = ColorBg = ColorHov = ColorAct = 0; - ColorHiLi = IM_COL32_BLACK_TRANS; - Formatter = NULL; - FormatterData = NULL; - Locator = NULL; - Enabled = Hovered = Held = FitThisFrame = HasRange = HasFormatSpec = false; - ShowDefaultTicks = true; - } - - inline void Reset() { - Enabled = false; - Scale = ImPlotScale_Linear; - TransformForward = TransformInverse = NULL; - TransformData = NULL; - LabelOffset = -1; - HasFormatSpec = false; - Formatter = NULL; - FormatterData = NULL; - Locator = NULL; - ShowDefaultTicks = true; - FitThisFrame = false; - FitExtents.Min = HUGE_VAL; - FitExtents.Max = -HUGE_VAL; - OrthoAxis = NULL; - ConstraintRange = ImPlotRange(-INFINITY,INFINITY); - ConstraintZoom = ImPlotRange(DBL_MIN,INFINITY); - Ticker.Reset(); - } - - inline bool SetMin(double _min, bool force=false) { - if (!force && IsLockedMin()) - return false; - _min = ImConstrainNan(ImConstrainInf(_min)); - if (_min < ConstraintRange.Min) - _min = ConstraintRange.Min; - double z = Range.Max - _min; - if (z < ConstraintZoom.Min) - _min = Range.Max - ConstraintZoom.Min; - if (z > ConstraintZoom.Max) - _min = Range.Max - ConstraintZoom.Max; - if (_min >= Range.Max) - return false; - Range.Min = _min; - PickerTimeMin = ImPlotTime::FromDouble(Range.Min); - UpdateTransformCache(); - return true; - }; - - inline bool SetMax(double _max, bool force=false) { - if (!force && IsLockedMax()) - return false; - _max = ImConstrainNan(ImConstrainInf(_max)); - if (_max > ConstraintRange.Max) - _max = ConstraintRange.Max; - double z = _max - Range.Min; - if (z < ConstraintZoom.Min) - _max = Range.Min + ConstraintZoom.Min; - if (z > ConstraintZoom.Max) - _max = Range.Min + ConstraintZoom.Max; - if (_max <= Range.Min) - return false; - Range.Max = _max; - PickerTimeMax = ImPlotTime::FromDouble(Range.Max); - UpdateTransformCache(); - return true; - }; - - inline void SetRange(double v1, double v2) { - Range.Min = ImMin(v1,v2); - Range.Max = ImMax(v1,v2); - Constrain(); - PickerTimeMin = ImPlotTime::FromDouble(Range.Min); - PickerTimeMax = ImPlotTime::FromDouble(Range.Max); - UpdateTransformCache(); - } - - inline void SetRange(const ImPlotRange& range) { - SetRange(range.Min, range.Max); - } - - inline void SetAspect(double unit_per_pix) { - double new_size = unit_per_pix * PixelSize(); - double delta = (new_size - Range.Size()) * 0.5f; - if (IsLocked()) - return; - else if (IsLockedMin() && !IsLockedMax()) - SetRange(Range.Min, Range.Max + 2*delta); - else if (!IsLockedMin() && IsLockedMax()) - SetRange(Range.Min - 2*delta, Range.Max); - else - SetRange(Range.Min - delta, Range.Max + delta); - } - - inline float PixelSize() const { return ImAbs(PixelMax - PixelMin); } - - inline double GetAspect() const { return Range.Size() / PixelSize(); } - - inline void Constrain() { - Range.Min = ImConstrainNan(ImConstrainInf(Range.Min)); - Range.Max = ImConstrainNan(ImConstrainInf(Range.Max)); - if (Range.Min < ConstraintRange.Min) - Range.Min = ConstraintRange.Min; - if (Range.Max > ConstraintRange.Max) - Range.Max = ConstraintRange.Max; - double z = Range.Size(); - if (z < ConstraintZoom.Min) { - double delta = (ConstraintZoom.Min - z) * 0.5; - Range.Min -= delta; - Range.Max += delta; - } - if (z > ConstraintZoom.Max) { - double delta = (z - ConstraintZoom.Max) * 0.5f; - Range.Min += delta; - Range.Max -= delta; - } - if (Range.Max <= Range.Min) - Range.Max = Range.Min + DBL_EPSILON; - } - - inline void UpdateTransformCache() { - ScaleToPixel = (PixelMax - PixelMin) / Range.Size(); - if (TransformForward != NULL) { - ScaleMin = TransformForward(Range.Min, TransformData); - ScaleMax = TransformForward(Range.Max, TransformData); - } - else { - ScaleMin = Range.Min; - ScaleMax = Range.Max; - } - } - - inline float PlotToPixels(double plt) const { - if (TransformForward != NULL) { - double s = TransformForward(plt, TransformData); - double t = (s - ScaleMin) / (ScaleMax - ScaleMin); - plt = Range.Min + Range.Size() * t; - } - return (float)(PixelMin + ScaleToPixel * (plt - Range.Min)); - } - - - inline double PixelsToPlot(float pix) const { - double plt = (pix - PixelMin) / ScaleToPixel + Range.Min; - if (TransformInverse != NULL) { - double t = (plt - Range.Min) / Range.Size(); - double s = t * (ScaleMax - ScaleMin) + ScaleMin; - plt = TransformInverse(s, TransformData); - } - return plt; - } - - inline void ExtendFit(double v) { - if (!ImNanOrInf(v) && v >= ConstraintRange.Min && v <= ConstraintRange.Max) { - FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min; - FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max; - } - } - - inline void ExtendFitWith(ImPlotAxis& alt, double v, double v_alt) { - if (ImHasFlag(Flags, ImPlotAxisFlags_RangeFit) && !alt.Range.Contains(v_alt)) - return; - if (!ImNanOrInf(v) && v >= ConstraintRange.Min && v <= ConstraintRange.Max) { - FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min; - FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max; - } - } - - inline void ApplyFit(float padding) { - const double ext_size = FitExtents.Size() * 0.5; - FitExtents.Min -= ext_size * padding; - FitExtents.Max += ext_size * padding; - if (!IsLockedMin() && !ImNanOrInf(FitExtents.Min)) - Range.Min = FitExtents.Min; - if (!IsLockedMax() && !ImNanOrInf(FitExtents.Max)) - Range.Max = FitExtents.Max; - if (ImAlmostEqual(Range.Min, Range.Max)) { - Range.Max += 0.5; - Range.Min -= 0.5; - } - Constrain(); - UpdateTransformCache(); - } - - inline bool HasLabel() const { return LabelOffset != -1 && !ImHasFlag(Flags, ImPlotAxisFlags_NoLabel); } - inline bool HasGridLines() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoGridLines); } - inline bool HasTickLabels() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickLabels); } - inline bool HasTickMarks() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickMarks); } - inline bool WillRender() const { return Enabled && (HasGridLines() || HasTickLabels() || HasTickMarks()); } - inline bool IsOpposite() const { return ImHasFlag(Flags, ImPlotAxisFlags_Opposite); } - inline bool IsInverted() const { return ImHasFlag(Flags, ImPlotAxisFlags_Invert); } - inline bool IsForeground() const { return ImHasFlag(Flags, ImPlotAxisFlags_Foreground); } - inline bool IsAutoFitting() const { return ImHasFlag(Flags, ImPlotAxisFlags_AutoFit); } - inline bool CanInitFit() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoInitialFit) && !HasRange && !LinkedMin && !LinkedMax; } - inline bool IsRangeLocked() const { return HasRange && RangeCond == ImPlotCond_Always; } - inline bool IsLockedMin() const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMin); } - inline bool IsLockedMax() const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMax); } - inline bool IsLocked() const { return IsLockedMin() && IsLockedMax(); } - inline bool IsInputLockedMin() const { return IsLockedMin() || IsAutoFitting(); } - inline bool IsInputLockedMax() const { return IsLockedMax() || IsAutoFitting(); } - inline bool IsInputLocked() const { return IsLocked() || IsAutoFitting(); } - inline bool HasMenus() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoMenus); } - - inline bool IsPanLocked(bool increasing) { - if (ImHasFlag(Flags, ImPlotAxisFlags_PanStretch)) { - return IsInputLocked(); - } - else { - if (IsLockedMin() || IsLockedMax() || IsAutoFitting()) - return false; - if (increasing) - return Range.Max == ConstraintRange.Max; - else - return Range.Min == ConstraintRange.Min; - } - } - - void PushLinks() { - if (LinkedMin) { *LinkedMin = Range.Min; } - if (LinkedMax) { *LinkedMax = Range.Max; } - } - - void PullLinks() { - if (LinkedMin) { SetMin(*LinkedMin,true); } - if (LinkedMax) { SetMax(*LinkedMax,true); } - } -}; - -// Align plots group data -struct ImPlotAlignmentData { - bool Vertical; - float PadA; - float PadB; - float PadAMax; - float PadBMax; - ImPlotAlignmentData() { - Vertical = true; - PadA = PadB = PadAMax = PadBMax = 0; - } - void Begin() { PadAMax = PadBMax = 0; } - void Update(float& pad_a, float& pad_b, float& delta_a, float& delta_b) { - float bak_a = pad_a; float bak_b = pad_b; - if (PadAMax < pad_a) { PadAMax = pad_a; } - if (PadBMax < pad_b) { PadBMax = pad_b; } - if (pad_a < PadA) { pad_a = PadA; delta_a = pad_a - bak_a; } else { delta_a = 0; } - if (pad_b < PadB) { pad_b = PadB; delta_b = pad_b - bak_b; } else { delta_b = 0; } - } - void End() { PadA = PadAMax; PadB = PadBMax; } - void Reset() { PadA = PadB = PadAMax = PadBMax = 0; } -}; - -// State information for Plot items -struct ImPlotItem -{ - ImGuiID ID; - ImU32 Color; - ImRect LegendHoverRect; - int NameOffset; - bool Show; - bool LegendHovered; - bool SeenThisFrame; - - ImPlotItem() { - ID = 0; - NameOffset = -1; - Show = true; - SeenThisFrame = false; - LegendHovered = false; - } - - ~ImPlotItem() { ID = 0; } -}; - -// Holds Legend state -struct ImPlotLegend -{ - ImPlotLegendFlags Flags; - ImPlotLegendFlags PreviousFlags; - ImPlotLocation Location; - ImPlotLocation PreviousLocation; - ImVector Indices; - ImGuiTextBuffer Labels; - ImRect Rect; - bool Hovered; - bool Held; - bool CanGoInside; - - ImPlotLegend() { - Flags = PreviousFlags = ImPlotLegendFlags_None; - CanGoInside = true; - Hovered = Held = false; - Location = PreviousLocation = ImPlotLocation_NorthWest; - } - - void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); } -}; - -// Holds Items and Legend data -struct ImPlotItemGroup -{ - ImGuiID ID; - ImPlotLegend Legend; - ImPool ItemPool; - int ColormapIdx; - - ImPlotItemGroup() { ColormapIdx = 0; } - - int GetItemCount() const { return ItemPool.GetBufSize(); } - ImGuiID GetItemID(const char* label_id) { return ImGui::GetID(label_id); /* GetIDWithSeed */ } - ImPlotItem* GetItem(ImGuiID id) { return ItemPool.GetByKey(id); } - ImPlotItem* GetItem(const char* label_id) { return GetItem(GetItemID(label_id)); } - ImPlotItem* GetOrAddItem(ImGuiID id) { return ItemPool.GetOrAddByKey(id); } - ImPlotItem* GetItemByIndex(int i) { return ItemPool.GetByIndex(i); } - int GetItemIndex(ImPlotItem* item) { return ItemPool.GetIndex(item); } - int GetLegendCount() const { return Legend.Indices.size(); } - ImPlotItem* GetLegendItem(int i) { return ItemPool.GetByIndex(Legend.Indices[i]); } - const char* GetLegendLabel(int i) { return Legend.Labels.Buf.Data + GetLegendItem(i)->NameOffset; } - void Reset() { ItemPool.Clear(); Legend.Reset(); ColormapIdx = 0; } -}; - -// Holds Plot state information that must persist after EndPlot -struct ImPlotPlot -{ - ImGuiID ID; - ImPlotFlags Flags; - ImPlotFlags PreviousFlags; - ImPlotLocation MouseTextLocation; - ImPlotMouseTextFlags MouseTextFlags; - ImPlotAxis Axes[ImAxis_COUNT]; - ImGuiTextBuffer TextBuffer; - ImPlotItemGroup Items; - ImAxis CurrentX; - ImAxis CurrentY; - ImRect FrameRect; - ImRect CanvasRect; - ImRect PlotRect; - ImRect AxesRect; - ImRect SelectRect; - ImVec2 SelectStart; - int TitleOffset; - bool JustCreated; - bool Initialized; - bool SetupLocked; - bool FitThisFrame; - bool Hovered; - bool Held; - bool Selecting; - bool Selected; - bool ContextLocked; - - ImPlotPlot() { - Flags = PreviousFlags = ImPlotFlags_None; - for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) - XAxis(i).Vertical = false; - for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) - YAxis(i).Vertical = true; - SelectStart = ImVec2(0,0); - CurrentX = ImAxis_X1; - CurrentY = ImAxis_Y1; - MouseTextLocation = ImPlotLocation_South | ImPlotLocation_East; - MouseTextFlags = ImPlotMouseTextFlags_None; - TitleOffset = -1; - JustCreated = true; - Initialized = SetupLocked = FitThisFrame = false; - Hovered = Held = Selected = Selecting = ContextLocked = false; - } - - inline bool IsInputLocked() const { - for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { - if (!XAxis(i).IsInputLocked()) - return false; - } - for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { - if (!YAxis(i).IsInputLocked()) - return false; - } - return true; - } - - inline void ClearTextBuffer() { TextBuffer.Buf.shrink(0); } - - inline void SetTitle(const char* title) { - if (title && ImGui::FindRenderedTextEnd(title, NULL) != title) { - TitleOffset = TextBuffer.size(); - TextBuffer.append(title, title + strlen(title) + 1); - } - else { - TitleOffset = -1; - } - } - inline bool HasTitle() const { return TitleOffset != -1 && !ImHasFlag(Flags, ImPlotFlags_NoTitle); } - inline const char* GetTitle() const { return TextBuffer.Buf.Data + TitleOffset; } - - inline ImPlotAxis& XAxis(int i) { return Axes[ImAxis_X1 + i]; } - inline const ImPlotAxis& XAxis(int i) const { return Axes[ImAxis_X1 + i]; } - inline ImPlotAxis& YAxis(int i) { return Axes[ImAxis_Y1 + i]; } - inline const ImPlotAxis& YAxis(int i) const { return Axes[ImAxis_Y1 + i]; } - - inline int EnabledAxesX() { - int cnt = 0; - for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) - cnt += XAxis(i).Enabled; - return cnt; - } - - inline int EnabledAxesY() { - int cnt = 0; - for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) - cnt += YAxis(i).Enabled; - return cnt; - } - - inline void SetAxisLabel(ImPlotAxis& axis, const char* label) { - if (label && ImGui::FindRenderedTextEnd(label, NULL) != label) { - axis.LabelOffset = TextBuffer.size(); - TextBuffer.append(label, label + strlen(label) + 1); - } - else { - axis.LabelOffset = -1; - } - } - - inline const char* GetAxisLabel(const ImPlotAxis& axis) const { return TextBuffer.Buf.Data + axis.LabelOffset; } -}; - -// Holds subplot data that must persist after EndSubplot -struct ImPlotSubplot { - ImGuiID ID; - ImPlotSubplotFlags Flags; - ImPlotSubplotFlags PreviousFlags; - ImPlotItemGroup Items; - int Rows; - int Cols; - int CurrentIdx; - ImRect FrameRect; - ImRect GridRect; - ImVec2 CellSize; - ImVector RowAlignmentData; - ImVector ColAlignmentData; - ImVector RowRatios; - ImVector ColRatios; - ImVector RowLinkData; - ImVector ColLinkData; - float TempSizes[2]; - bool FrameHovered; - bool HasTitle; - - ImPlotSubplot() { - Rows = Cols = CurrentIdx = 0; - FrameHovered = false; - Items.Legend.Location = ImPlotLocation_North; - Items.Legend.Flags = ImPlotLegendFlags_Horizontal|ImPlotLegendFlags_Outside; - Items.Legend.CanGoInside = false; - HasTitle = false; - } -}; - -// Temporary data storage for upcoming plot -struct ImPlotNextPlotData -{ - ImPlotCond RangeCond[ImAxis_COUNT]; - ImPlotRange Range[ImAxis_COUNT]; - bool HasRange[ImAxis_COUNT]; - bool Fit[ImAxis_COUNT]; - double* LinkedMin[ImAxis_COUNT]; - double* LinkedMax[ImAxis_COUNT]; - - ImPlotNextPlotData() { Reset(); } - - void Reset() { - for (int i = 0; i < ImAxis_COUNT; ++i) { - HasRange[i] = false; - Fit[i] = false; - LinkedMin[i] = LinkedMax[i] = NULL; - } - } - -}; - -// Temporary data storage for upcoming item -struct ImPlotNextItemData { - ImVec4 Colors[5]; // ImPlotCol_Line, ImPlotCol_Fill, ImPlotCol_MarkerOutline, ImPlotCol_MarkerFill, ImPlotCol_ErrorBar - float LineWeight; - ImPlotMarker Marker; - float MarkerSize; - float MarkerWeight; - float FillAlpha; - float ErrorBarSize; - float ErrorBarWeight; - float DigitalBitHeight; - float DigitalBitGap; - bool RenderLine; - bool RenderFill; - bool RenderMarkerLine; - bool RenderMarkerFill; - bool HasHidden; - bool Hidden; - ImPlotCond HiddenCond; - ImPlotNextItemData() { Reset(); } - void Reset() { - for (int i = 0; i < 5; ++i) - Colors[i] = IMPLOT_AUTO_COL; - LineWeight = MarkerSize = MarkerWeight = FillAlpha = ErrorBarSize = ErrorBarWeight = DigitalBitHeight = DigitalBitGap = IMPLOT_AUTO; - Marker = IMPLOT_AUTO; - HasHidden = Hidden = false; - } -}; - -// Holds state information that must persist between calls to BeginPlot()/EndPlot() -struct ImPlotContext { - // Plot States - ImPool Plots; - ImPool Subplots; - ImPlotPlot* CurrentPlot; - ImPlotSubplot* CurrentSubplot; - ImPlotItemGroup* CurrentItems; - ImPlotItem* CurrentItem; - ImPlotItem* PreviousItem; - - // Tick Marks and Labels - ImPlotTicker CTicker; - - // Annotation and Tabs - ImPlotAnnotationCollection Annotations; - ImPlotTagCollection Tags; - - // Flags - bool ChildWindowMade; - - // Style and Colormaps - ImPlotStyle Style; - ImVector ColorModifiers; - ImVector StyleModifiers; - ImPlotColormapData ColormapData; - ImVector ColormapModifiers; - - // Time - tm Tm; - - // Temp data for general use - ImVector TempDouble1, TempDouble2; - ImVector TempInt1; - - // Misc - int DigitalPlotItemCnt; - int DigitalPlotOffset; - ImPlotNextPlotData NextPlotData; - ImPlotNextItemData NextItemData; - ImPlotInputMap InputMap; - bool OpenContextThisFrame; - ImGuiTextBuffer MousePosStringBuilder; - - // Align plots - ImPool AlignmentData; - ImPlotAlignmentData* CurrentAlignmentH; - ImPlotAlignmentData* CurrentAlignmentV; -}; - -//----------------------------------------------------------------------------- -// [SECTION] Internal API -// No guarantee of forward compatibility here! -//----------------------------------------------------------------------------- - -namespace ImPlot { - -//----------------------------------------------------------------------------- -// [SECTION] Context Utils -//----------------------------------------------------------------------------- - -// Initializes an ImPlotContext -IMPLOT_API void Initialize(ImPlotContext* ctx); -// Resets an ImPlot context for the next call to BeginPlot -IMPLOT_API void ResetCtxForNextPlot(ImPlotContext* ctx); -// Resets an ImPlot context for the next call to BeginAlignedPlots -IMPLOT_API void ResetCtxForNextAlignedPlots(ImPlotContext* ctx); -// Resets an ImPlot context for the next call to BeginSubplot -IMPLOT_API void ResetCtxForNextSubplot(ImPlotContext* ctx); - -//----------------------------------------------------------------------------- -// [SECTION] Plot Utils -//----------------------------------------------------------------------------- - -// Gets a plot from the current ImPlotContext -IMPLOT_API ImPlotPlot* GetPlot(const char* title); -// Gets the current plot from the current ImPlotContext -IMPLOT_API ImPlotPlot* GetCurrentPlot(); -// Busts the cache for every plot in the current context -IMPLOT_API void BustPlotCache(); - -// Shows a plot's context menu. -IMPLOT_API void ShowPlotContextMenu(ImPlotPlot& plot); - -//----------------------------------------------------------------------------- -// [SECTION] Setup Utils -//----------------------------------------------------------------------------- - -// Lock Setup and call SetupFinish if necessary. -static inline void SetupLock() { - if (!GImPlot->CurrentPlot->SetupLocked) - SetupFinish(); - GImPlot->CurrentPlot->SetupLocked = true; -} - -//----------------------------------------------------------------------------- -// [SECTION] Subplot Utils -//----------------------------------------------------------------------------- - -// Advances to next subplot -IMPLOT_API void SubplotNextCell(); - -// Shows a subplot's context menu. -IMPLOT_API void ShowSubplotsContextMenu(ImPlotSubplot& subplot); - -//----------------------------------------------------------------------------- -// [SECTION] Item Utils -//----------------------------------------------------------------------------- - -// Begins a new item. Returns false if the item should not be plotted. Pushes PlotClipRect. -IMPLOT_API bool BeginItem(const char* label_id, ImPlotItemFlags flags=0, ImPlotCol recolor_from=IMPLOT_AUTO); - -// Same as above but with fitting functionality. -template -bool BeginItemEx(const char* label_id, const _Fitter& fitter, ImPlotItemFlags flags=0, ImPlotCol recolor_from=IMPLOT_AUTO) { - if (BeginItem(label_id, flags, recolor_from)) { - ImPlotPlot& plot = *GetCurrentPlot(); - if (plot.FitThisFrame && !ImHasFlag(flags, ImPlotItemFlags_NoFit)) - fitter.Fit(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]); - return true; - } - return false; -} - -// Ends an item (call only if BeginItem returns true). Pops PlotClipRect. -IMPLOT_API void EndItem(); - -// Register or get an existing item from the current plot. -IMPLOT_API ImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created = NULL); -// Get a plot item from the current plot. -IMPLOT_API ImPlotItem* GetItem(const char* label_id); -// Gets the current item. -IMPLOT_API ImPlotItem* GetCurrentItem(); -// Busts the cache for every item for every plot in the current context. -IMPLOT_API void BustItemCache(); - -//----------------------------------------------------------------------------- -// [SECTION] Axis Utils -//----------------------------------------------------------------------------- - -// Returns true if any enabled axis is locked from user input. -static inline bool AnyAxesInputLocked(ImPlotAxis* axes, int count) { - for (int i = 0; i < count; ++i) { - if (axes[i].Enabled && axes[i].IsInputLocked()) - return true; - } - return false; -} - -// Returns true if all enabled axes are locked from user input. -static inline bool AllAxesInputLocked(ImPlotAxis* axes, int count) { - for (int i = 0; i < count; ++i) { - if (axes[i].Enabled && !axes[i].IsInputLocked()) - return false; - } - return true; -} - -static inline bool AnyAxesHeld(ImPlotAxis* axes, int count) { - for (int i = 0; i < count; ++i) { - if (axes[i].Enabled && axes[i].Held) - return true; - } - return false; -} - -static inline bool AnyAxesHovered(ImPlotAxis* axes, int count) { - for (int i = 0; i < count; ++i) { - if (axes[i].Enabled && axes[i].Hovered) - return true; - } - return false; -} - -// Returns true if the user has requested data to be fit. -static inline bool FitThisFrame() { - return GImPlot->CurrentPlot->FitThisFrame; -} - -// Extends the current plot's axes so that it encompasses a vertical line at x -static inline void FitPointX(double x) { - ImPlotPlot& plot = *GetCurrentPlot(); - ImPlotAxis& x_axis = plot.Axes[plot.CurrentX]; - x_axis.ExtendFit(x); -} - -// Extends the current plot's axes so that it encompasses a horizontal line at y -static inline void FitPointY(double y) { - ImPlotPlot& plot = *GetCurrentPlot(); - ImPlotAxis& y_axis = plot.Axes[plot.CurrentY]; - y_axis.ExtendFit(y); -} - -// Extends the current plot's axes so that it encompasses point p -static inline void FitPoint(const ImPlotPoint& p) { - ImPlotPlot& plot = *GetCurrentPlot(); - ImPlotAxis& x_axis = plot.Axes[plot.CurrentX]; - ImPlotAxis& y_axis = plot.Axes[plot.CurrentY]; - x_axis.ExtendFitWith(y_axis, p.x, p.y); - y_axis.ExtendFitWith(x_axis, p.y, p.x); -} - -// Returns true if two ranges overlap -static inline bool RangesOverlap(const ImPlotRange& r1, const ImPlotRange& r2) -{ return r1.Min <= r2.Max && r2.Min <= r1.Max; } - -// Shows an axis's context menu. -IMPLOT_API void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_allowed = false); - -//----------------------------------------------------------------------------- -// [SECTION] Legend Utils -//----------------------------------------------------------------------------- - -// Gets the position of an inner rect that is located inside of an outer rect according to an ImPlotLocation and padding amount. -IMPLOT_API ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation location, const ImVec2& pad = ImVec2(0,0)); -// Calculates the bounding box size of a legend -IMPLOT_API ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical); -// Renders legend entries into a bounding box -IMPLOT_API bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList); -// Shows an alternate legend for the plot identified by #title_id, outside of the plot frame (can be called before or after of Begin/EndPlot but must occur in the same ImGui window!). -IMPLOT_API void ShowAltLegend(const char* title_id, bool vertical = true, const ImVec2 size = ImVec2(0,0), bool interactable = true); -// Shows an legends's context menu. -IMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible); - -//----------------------------------------------------------------------------- -// [SECTION] Label Utils -//----------------------------------------------------------------------------- - -// Create a a string label for a an axis value -IMPLOT_API void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round = false); - -//----------------------------------------------------------------------------- -// [SECTION] Styling Utils -//----------------------------------------------------------------------------- - -// Get styling data for next item (call between Begin/EndItem) -static inline const ImPlotNextItemData& GetItemData() { return GImPlot->NextItemData; } - -// Returns true if a color is set to be automatically determined -static inline bool IsColorAuto(const ImVec4& col) { return col.w == -1; } -// Returns true if a style color is set to be automaticaly determined -static inline bool IsColorAuto(ImPlotCol idx) { return IsColorAuto(GImPlot->Style.Colors[idx]); } -// Returns the automatically deduced style color -IMPLOT_API ImVec4 GetAutoColor(ImPlotCol idx); - -// Returns the style color whether it is automatic or custom set -static inline ImVec4 GetStyleColorVec4(ImPlotCol idx) { return IsColorAuto(idx) ? GetAutoColor(idx) : GImPlot->Style.Colors[idx]; } -static inline ImU32 GetStyleColorU32(ImPlotCol idx) { return ImGui::ColorConvertFloat4ToU32(GetStyleColorVec4(idx)); } - -// Draws vertical text. The position is the bottom left of the text rect. -IMPLOT_API void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char* text_begin, const char* text_end = NULL); -// Draws multiline horizontal text centered. -IMPLOT_API void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end = NULL); -// Calculates the size of vertical text -static inline ImVec2 CalcTextSizeVertical(const char *text) { - ImVec2 sz = ImGui::CalcTextSize(text); - return ImVec2(sz.y, sz.x); -} -// Returns white or black text given background color -static inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299f + bg.y * 0.587f + bg.z * 0.114f) > 0.5f ? IM_COL32_BLACK : IM_COL32_WHITE; } -static inline ImU32 CalcTextColor(ImU32 bg) { return CalcTextColor(ImGui::ColorConvertU32ToFloat4(bg)); } -// Lightens or darkens a color for hover -static inline ImU32 CalcHoverColor(ImU32 col) { return ImMixU32(col, CalcTextColor(col), 32); } - -// Clamps a label position so that it fits a rect defined by Min/Max -static inline ImVec2 ClampLabelPos(ImVec2 pos, const ImVec2& size, const ImVec2& Min, const ImVec2& Max) { - if (pos.x < Min.x) pos.x = Min.x; - if (pos.y < Min.y) pos.y = Min.y; - if ((pos.x + size.x) > Max.x) pos.x = Max.x - size.x; - if ((pos.y + size.y) > Max.y) pos.y = Max.y - size.y; - return pos; -} - -// Returns a color from the Color map given an index >= 0 (modulo will be performed). -IMPLOT_API ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap); -// Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired. -IMPLOT_API ImU32 NextColormapColorU32(); -// Linearly interpolates a color from the current colormap given t between 0 and 1. -IMPLOT_API ImU32 SampleColormapU32(float t, ImPlotColormap cmap); - -// Render a colormap bar -IMPLOT_API void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous); - -//----------------------------------------------------------------------------- -// [SECTION] Math and Misc Utils -//----------------------------------------------------------------------------- - -// Rounds x to powers of 2,5 and 10 for generating axis labels (from Graphics Gems 1 Chapter 11.2) -IMPLOT_API double NiceNum(double x, bool round); -// Computes order of magnitude of double. -static inline int OrderOfMagnitude(double val) { return val == 0 ? 0 : (int)(floor(log10(fabs(val)))); } -// Returns the precision required for a order of magnitude. -static inline int OrderToPrecision(int order) { return order > 0 ? 0 : 1 - order; } -// Returns a floating point precision to use given a value -static inline int Precision(double val) { return OrderToPrecision(OrderOfMagnitude(val)); } -// Round a value to a given precision -static inline double RoundTo(double val, int prec) { double p = pow(10,(double)prec); return floor(val*p+0.5)/p; } - -// Returns the intersection point of two lines A and B (assumes they are not parallel!) -static inline ImVec2 Intersection(const ImVec2& a1, const ImVec2& a2, const ImVec2& b1, const ImVec2& b2) { - float v1 = (a1.x * a2.y - a1.y * a2.x); float v2 = (b1.x * b2.y - b1.y * b2.x); - float v3 = ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x)); - return ImVec2((v1 * (b1.x - b2.x) - v2 * (a1.x - a2.x)) / v3, (v1 * (b1.y - b2.y) - v2 * (a1.y - a2.y)) / v3); -} - -// Fills a buffer with n samples linear interpolated from vmin to vmax -template -void FillRange(ImVector& buffer, int n, T vmin, T vmax) { - buffer.resize(n); - T step = (vmax - vmin) / (n - 1); - for (int i = 0; i < n; ++i) { - buffer[i] = vmin + i * step; - } -} - -// Calculate histogram bin counts and widths -template -static inline void CalculateBins(const T* values, int count, ImPlotBin meth, const ImPlotRange& range, int& bins_out, double& width_out) { - switch (meth) { - case ImPlotBin_Sqrt: - bins_out = (int)ceil(sqrt(count)); - break; - case ImPlotBin_Sturges: - bins_out = (int)ceil(1.0 + log2(count)); - break; - case ImPlotBin_Rice: - bins_out = (int)ceil(2 * cbrt(count)); - break; - case ImPlotBin_Scott: - width_out = 3.49 * ImStdDev(values, count) / cbrt(count); - bins_out = (int)round(range.Size() / width_out); - break; - } - width_out = range.Size() / bins_out; -} - -//----------------------------------------------------------------------------- -// Time Utils -//----------------------------------------------------------------------------- - -// Returns true if year is leap year (366 days long) -static inline bool IsLeapYear(int year) { - return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); -} -// Returns the number of days in a month, accounting for Feb. leap years. #month is zero indexed. -static inline int GetDaysInMonth(int year, int month) { - static const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - return days[month] + (int)(month == 1 && IsLeapYear(year)); -} - -// Make a UNIX timestamp from a tm struct expressed in UTC time (i.e. GMT timezone). -IMPLOT_API ImPlotTime MkGmtTime(struct tm *ptm); -// Make a tm struct expressed in UTC time (i.e. GMT timezone) from a UNIX timestamp. -IMPLOT_API tm* GetGmtTime(const ImPlotTime& t, tm* ptm); - -// Make a UNIX timestamp from a tm struct expressed in local time. -IMPLOT_API ImPlotTime MkLocTime(struct tm *ptm); -// Make a tm struct expressed in local time from a UNIX timestamp. -IMPLOT_API tm* GetLocTime(const ImPlotTime& t, tm* ptm); - -// NB: The following functions only work if there is a current ImPlotContext because the -// internal tm struct is owned by the context! They are aware of ImPlotStyle.UseLocalTime. - -// Make a timestamp from time components. -// year[1970-3000], month[0-11], day[1-31], hour[0-23], min[0-59], sec[0-59], us[0,999999] -IMPLOT_API ImPlotTime MakeTime(int year, int month = 0, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0); -// Get year component from timestamp [1970-3000] -IMPLOT_API int GetYear(const ImPlotTime& t); - -// Adds or subtracts time from a timestamp. #count > 0 to add, < 0 to subtract. -IMPLOT_API ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count); -// Rounds a timestamp down to nearest unit. -IMPLOT_API ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit); -// Rounds a timestamp up to the nearest unit. -IMPLOT_API ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit); -// Rounds a timestamp up or down to the nearest unit. -IMPLOT_API ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit); -// Combines the date of one timestamp with the time-of-day of another timestamp. -IMPLOT_API ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& time_part); - -// Formats the time part of timestamp t into a buffer according to #fmt -IMPLOT_API int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, bool use_24_hr_clk); -// Formats the date part of timestamp t into a buffer according to #fmt -IMPLOT_API int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601); -// Formats the time and/or date parts of a timestamp t into a buffer according to #fmt -IMPLOT_API int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt); - -// Shows a date picker widget block (year/month/day). -// #level = 0 for day, 1 for month, 2 for year. Modified by user interaction. -// #t will be set when a day is clicked and the function will return true. -// #t1 and #t2 are optional dates to highlight. -IMPLOT_API bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1 = NULL, const ImPlotTime* t2 = NULL); -// Shows a time picker widget block (hour/min/sec). -// #t will be set when a new hour, minute, or sec is selected or am/pm is toggled, and the function will return true. -IMPLOT_API bool ShowTimePicker(const char* id, ImPlotTime* t); - -//----------------------------------------------------------------------------- -// [SECTION] Transforms -//----------------------------------------------------------------------------- - -static inline double TransformForward_Log10(double v, void*) { - v = v <= 0.0 ? DBL_MIN : v; - return ImLog10(v); -} - -static inline double TransformInverse_Log10(double v, void*) { - return ImPow(10, v); -} - -static inline double TransformForward_SymLog(double v, void*) { - return 2.0 * ImAsinh(v / 2.0); -} - -static inline double TransformInverse_SymLog(double v, void*) { - return 2.0 * ImSinh(v / 2.0); -} - -static inline double TransformForward_Logit(double v, void*) { - v = ImClamp(v, DBL_MIN, 1.0 - DBL_EPSILON); - return ImLog10(v / (1 - v)); -} - -static inline double TransformInverse_Logit(double v, void*) { - return 1.0 / (1.0 + ImPow(10,-v)); -} - -//----------------------------------------------------------------------------- -// [SECTION] Formatters -//----------------------------------------------------------------------------- - -static inline int Formatter_Default(double value, char* buff, int size, void* data) { - char* fmt = (char*)data; - return ImFormatString(buff, size, fmt, value); -} - -static inline int Formatter_Logit(double value, char* buff, int size, void*) { - if (value == 0.5) - return ImFormatString(buff,size,"1/2"); - else if (value < 0.5) - return ImFormatString(buff,size,"%g", value); - else - return ImFormatString(buff,size,"1 - %g", 1 - value); -} - -struct Formatter_Time_Data { - ImPlotTime Time; - ImPlotDateTimeSpec Spec; - ImPlotFormatter UserFormatter; - void* UserFormatterData; -}; - -static inline int Formatter_Time(double, char* buff, int size, void* data) { - Formatter_Time_Data* ftd = (Formatter_Time_Data*)data; - return FormatDateTime(ftd->Time, buff, size, ftd->Spec); -} - -//------------------------------------------------------------------------------ -// [SECTION] Locator -//------------------------------------------------------------------------------ - -void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); -void Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); -void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); -void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); - -} // namespace ImPlot diff --git a/libs/zgui/libs/imgui/implot_items.cpp b/libs/zgui/libs/imgui/implot_items.cpp deleted file mode 100644 index 824454276..000000000 --- a/libs/zgui/libs/imgui/implot_items.cpp +++ /dev/null @@ -1,2824 +0,0 @@ -// MIT License - -// Copyright (c) 2020 Evan Pezent - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// ImPlot v0.14 - -#include "implot.h" -#include "implot_internal.h" - -//----------------------------------------------------------------------------- -// [SECTION] Macros and Defines -//----------------------------------------------------------------------------- - -#define SQRT_1_2 0.70710678118f -#define SQRT_3_2 0.86602540378f - -#ifndef IMPLOT_NO_FORCE_INLINE - #ifdef _MSC_VER - #define IMPLOT_INLINE __forceinline - #elif defined(__GNUC__) - #define IMPLOT_INLINE inline __attribute__((__always_inline__)) - #elif defined(__CLANG__) - #if __has_attribute(__always_inline__) - #define IMPLOT_INLINE inline __attribute__((__always_inline__)) - #else - #define IMPLOT_INLINE inline - #endif - #else - #define IMPLOT_INLINE inline - #endif -#else - #define IMPLOT_INLINE inline -#endif - -#if defined __SSE__ || defined __x86_64__ || defined _M_X64 -#ifndef IMGUI_ENABLE_SSE -#include -#endif -static IMPLOT_INLINE float ImInvSqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } -#else -static IMPLOT_INLINE float ImInvSqrt(float x) { return 1.0f / sqrtf(x); } -#endif - -#define IMPLOT_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImInvSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) - -// Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. -#if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll) -#define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All -#endif - -namespace ImPlot { - -//----------------------------------------------------------------------------- -// [SECTION] Utils -//----------------------------------------------------------------------------- - -// Calc maximum index size of ImDrawIdx -template -struct MaxIdx { static const unsigned int Value; }; -template <> const unsigned int MaxIdx::Value = 65535; -template <> const unsigned int MaxIdx::Value = 4294967295; - -IMPLOT_INLINE void GetLineRenderProps(const ImDrawList& draw_list, float& half_weight, ImVec2& tex_uv0, ImVec2& tex_uv1) { - const bool aa = ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLines) && - ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLinesUseTex); - if (aa) { - ImVec4 tex_uvs = draw_list._Data->TexUvLines[(int)(half_weight*2)]; - tex_uv0 = ImVec2(tex_uvs.x, tex_uvs.y); - tex_uv1 = ImVec2(tex_uvs.z, tex_uvs.w); - half_weight += 1; - } - else { - tex_uv0 = tex_uv1 = draw_list._Data->TexUvWhitePixel; - } -} - -IMPLOT_INLINE void PrimLine(ImDrawList& draw_list, const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, const ImVec2& tex_uv0, const ImVec2 tex_uv1) { - float dx = P2.x - P1.x; - float dy = P2.y - P1.y; - IMPLOT_NORMALIZE2F_OVER_ZERO(dx, dy); - dx *= half_weight; - dy *= half_weight; - draw_list._VtxWritePtr[0].pos.x = P1.x + dy; - draw_list._VtxWritePtr[0].pos.y = P1.y - dx; - draw_list._VtxWritePtr[0].uv = tex_uv0; - draw_list._VtxWritePtr[0].col = col; - draw_list._VtxWritePtr[1].pos.x = P2.x + dy; - draw_list._VtxWritePtr[1].pos.y = P2.y - dx; - draw_list._VtxWritePtr[1].uv = tex_uv0; - draw_list._VtxWritePtr[1].col = col; - draw_list._VtxWritePtr[2].pos.x = P2.x - dy; - draw_list._VtxWritePtr[2].pos.y = P2.y + dx; - draw_list._VtxWritePtr[2].uv = tex_uv1; - draw_list._VtxWritePtr[2].col = col; - draw_list._VtxWritePtr[3].pos.x = P1.x - dy; - draw_list._VtxWritePtr[3].pos.y = P1.y + dx; - draw_list._VtxWritePtr[3].uv = tex_uv1; - draw_list._VtxWritePtr[3].col = col; - draw_list._VtxWritePtr += 4; - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); - draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); - draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); - draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); - draw_list._IdxWritePtr += 6; - draw_list._VtxCurrentIdx += 4; -} - -IMPLOT_INLINE void PrimRectFill(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, ImU32 col, const ImVec2& uv) { - draw_list._VtxWritePtr[0].pos = Pmin; - draw_list._VtxWritePtr[0].uv = uv; - draw_list._VtxWritePtr[0].col = col; - draw_list._VtxWritePtr[1].pos = Pmax; - draw_list._VtxWritePtr[1].uv = uv; - draw_list._VtxWritePtr[1].col = col; - draw_list._VtxWritePtr[2].pos.x = Pmin.x; - draw_list._VtxWritePtr[2].pos.y = Pmax.y; - draw_list._VtxWritePtr[2].uv = uv; - draw_list._VtxWritePtr[2].col = col; - draw_list._VtxWritePtr[3].pos.x = Pmax.x; - draw_list._VtxWritePtr[3].pos.y = Pmin.y; - draw_list._VtxWritePtr[3].uv = uv; - draw_list._VtxWritePtr[3].col = col; - draw_list._VtxWritePtr += 4; - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); - draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); - draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); - draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); - draw_list._IdxWritePtr += 6; - draw_list._VtxCurrentIdx += 4; -} - -IMPLOT_INLINE void PrimRectLine(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, float weight, ImU32 col, const ImVec2& uv) { - - draw_list._VtxWritePtr[0].pos.x = Pmin.x; - draw_list._VtxWritePtr[0].pos.y = Pmin.y; - draw_list._VtxWritePtr[0].uv = uv; - draw_list._VtxWritePtr[0].col = col; - - draw_list._VtxWritePtr[1].pos.x = Pmin.x; - draw_list._VtxWritePtr[1].pos.y = Pmax.y; - draw_list._VtxWritePtr[1].uv = uv; - draw_list._VtxWritePtr[1].col = col; - - draw_list._VtxWritePtr[2].pos.x = Pmax.x; - draw_list._VtxWritePtr[2].pos.y = Pmax.y; - draw_list._VtxWritePtr[2].uv = uv; - draw_list._VtxWritePtr[2].col = col; - - draw_list._VtxWritePtr[3].pos.x = Pmax.x; - draw_list._VtxWritePtr[3].pos.y = Pmin.y; - draw_list._VtxWritePtr[3].uv = uv; - draw_list._VtxWritePtr[3].col = col; - - draw_list._VtxWritePtr[4].pos.x = Pmin.x + weight; - draw_list._VtxWritePtr[4].pos.y = Pmin.y + weight; - draw_list._VtxWritePtr[4].uv = uv; - draw_list._VtxWritePtr[4].col = col; - - draw_list._VtxWritePtr[5].pos.x = Pmin.x + weight; - draw_list._VtxWritePtr[5].pos.y = Pmax.y - weight; - draw_list._VtxWritePtr[5].uv = uv; - draw_list._VtxWritePtr[5].col = col; - - draw_list._VtxWritePtr[6].pos.x = Pmax.x - weight; - draw_list._VtxWritePtr[6].pos.y = Pmax.y - weight; - draw_list._VtxWritePtr[6].uv = uv; - draw_list._VtxWritePtr[6].col = col; - - draw_list._VtxWritePtr[7].pos.x = Pmax.x - weight; - draw_list._VtxWritePtr[7].pos.y = Pmin.y + weight; - draw_list._VtxWritePtr[7].uv = uv; - draw_list._VtxWritePtr[7].col = col; - - draw_list._VtxWritePtr += 8; - - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); - draw_list._IdxWritePtr += 3; - - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); - draw_list._IdxWritePtr += 3; - - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); - draw_list._IdxWritePtr += 3; - - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); - draw_list._IdxWritePtr += 3; - - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); - draw_list._IdxWritePtr += 3; - - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); - draw_list._IdxWritePtr += 3; - - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); - draw_list._IdxWritePtr += 3; - - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); - draw_list._IdxWritePtr += 3; - - draw_list._VtxCurrentIdx += 8; -} - - -//----------------------------------------------------------------------------- -// [SECTION] Item Utils -//----------------------------------------------------------------------------- - -ImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created) { - ImPlotContext& gp = *GImPlot; - ImPlotItemGroup& Items = *gp.CurrentItems; - ImGuiID id = Items.GetItemID(label_id); - if (just_created != NULL) - *just_created = Items.GetItem(id) == NULL; - ImPlotItem* item = Items.GetOrAddItem(id); - if (item->SeenThisFrame) - return item; - item->SeenThisFrame = true; - int idx = Items.GetItemIndex(item); - item->ID = id; - if (!ImHasFlag(flags, ImPlotItemFlags_NoLegend) && ImGui::FindRenderedTextEnd(label_id, NULL) != label_id) { - Items.Legend.Indices.push_back(idx); - item->NameOffset = Items.Legend.Labels.size(); - Items.Legend.Labels.append(label_id, label_id + strlen(label_id) + 1); - } - else { - item->Show = true; - } - return item; -} - -ImPlotItem* GetItem(const char* label_id) { - ImPlotContext& gp = *GImPlot; - return gp.CurrentItems->GetItem(label_id); -} - -bool IsItemHidden(const char* label_id) { - ImPlotItem* item = GetItem(label_id); - return item != NULL && !item->Show; -} - -ImPlotItem* GetCurrentItem() { - ImPlotContext& gp = *GImPlot; - return gp.CurrentItem; -} - -void SetNextLineStyle(const ImVec4& col, float weight) { - ImPlotContext& gp = *GImPlot; - gp.NextItemData.Colors[ImPlotCol_Line] = col; - gp.NextItemData.LineWeight = weight; -} - -void SetNextFillStyle(const ImVec4& col, float alpha) { - ImPlotContext& gp = *GImPlot; - gp.NextItemData.Colors[ImPlotCol_Fill] = col; - gp.NextItemData.FillAlpha = alpha; -} - -void SetNextMarkerStyle(ImPlotMarker marker, float size, const ImVec4& fill, float weight, const ImVec4& outline) { - ImPlotContext& gp = *GImPlot; - gp.NextItemData.Marker = marker; - gp.NextItemData.Colors[ImPlotCol_MarkerFill] = fill; - gp.NextItemData.MarkerSize = size; - gp.NextItemData.Colors[ImPlotCol_MarkerOutline] = outline; - gp.NextItemData.MarkerWeight = weight; -} - -void SetNextErrorBarStyle(const ImVec4& col, float size, float weight) { - ImPlotContext& gp = *GImPlot; - gp.NextItemData.Colors[ImPlotCol_ErrorBar] = col; - gp.NextItemData.ErrorBarSize = size; - gp.NextItemData.ErrorBarWeight = weight; -} - -ImVec4 GetLastItemColor() { - ImPlotContext& gp = *GImPlot; - if (gp.PreviousItem) - return ImGui::ColorConvertU32ToFloat4(gp.PreviousItem->Color); - return ImVec4(); -} - -void BustItemCache() { - ImPlotContext& gp = *GImPlot; - for (int p = 0; p < gp.Plots.GetBufSize(); ++p) { - ImPlotPlot& plot = *gp.Plots.GetByIndex(p); - plot.Items.Reset(); - } - for (int p = 0; p < gp.Subplots.GetBufSize(); ++p) { - ImPlotSubplot& subplot = *gp.Subplots.GetByIndex(p); - subplot.Items.Reset(); - } -} - -void BustColorCache(const char* plot_title_id) { - ImPlotContext& gp = *GImPlot; - if (plot_title_id == NULL) { - BustItemCache(); - } - else { - ImGuiID id = ImGui::GetCurrentWindow()->GetID(plot_title_id); - ImPlotPlot* plot = gp.Plots.GetByKey(id); - if (plot != NULL) - plot->Items.Reset(); - else { - ImPlotSubplot* subplot = gp.Subplots.GetByKey(id); - if (subplot != NULL) - subplot->Items.Reset(); - } - } -} - -//----------------------------------------------------------------------------- -// [SECTION] BeginItem / EndItem -//----------------------------------------------------------------------------- - -static const float ITEM_HIGHLIGHT_LINE_SCALE = 2.0f; -static const float ITEM_HIGHLIGHT_MARK_SCALE = 1.25f; - -// Begins a new item. Returns false if the item should not be plotted. -bool BeginItem(const char* label_id, ImPlotItemFlags flags, ImPlotCol recolor_from) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotX() needs to be called between BeginPlot() and EndPlot()!"); - SetupLock(); - bool just_created; - ImPlotItem* item = RegisterOrGetItem(label_id, flags, &just_created); - // set current item - gp.CurrentItem = item; - ImPlotNextItemData& s = gp.NextItemData; - // set/override item color - if (recolor_from != -1) { - if (!IsColorAuto(s.Colors[recolor_from])) - item->Color = ImGui::ColorConvertFloat4ToU32(s.Colors[recolor_from]); - else if (!IsColorAuto(gp.Style.Colors[recolor_from])) - item->Color = ImGui::ColorConvertFloat4ToU32(gp.Style.Colors[recolor_from]); - else if (just_created) - item->Color = NextColormapColorU32(); - } - else if (just_created) { - item->Color = NextColormapColorU32(); - } - // hide/show item - if (gp.NextItemData.HasHidden) { - if (just_created || gp.NextItemData.HiddenCond == ImGuiCond_Always) - item->Show = !gp.NextItemData.Hidden; - } - if (!item->Show) { - // reset next item data - gp.NextItemData.Reset(); - gp.PreviousItem = item; - gp.CurrentItem = NULL; - return false; - } - else { - ImVec4 item_color = ImGui::ColorConvertU32ToFloat4(item->Color); - // stage next item colors - s.Colors[ImPlotCol_Line] = IsColorAuto(s.Colors[ImPlotCol_Line]) ? ( IsColorAuto(ImPlotCol_Line) ? item_color : gp.Style.Colors[ImPlotCol_Line] ) : s.Colors[ImPlotCol_Line]; - s.Colors[ImPlotCol_Fill] = IsColorAuto(s.Colors[ImPlotCol_Fill]) ? ( IsColorAuto(ImPlotCol_Fill) ? item_color : gp.Style.Colors[ImPlotCol_Fill] ) : s.Colors[ImPlotCol_Fill]; - s.Colors[ImPlotCol_MarkerOutline] = IsColorAuto(s.Colors[ImPlotCol_MarkerOutline]) ? ( IsColorAuto(ImPlotCol_MarkerOutline) ? s.Colors[ImPlotCol_Line] : gp.Style.Colors[ImPlotCol_MarkerOutline] ) : s.Colors[ImPlotCol_MarkerOutline]; - s.Colors[ImPlotCol_MarkerFill] = IsColorAuto(s.Colors[ImPlotCol_MarkerFill]) ? ( IsColorAuto(ImPlotCol_MarkerFill) ? s.Colors[ImPlotCol_Line] : gp.Style.Colors[ImPlotCol_MarkerFill] ) : s.Colors[ImPlotCol_MarkerFill]; - s.Colors[ImPlotCol_ErrorBar] = IsColorAuto(s.Colors[ImPlotCol_ErrorBar]) ? ( GetStyleColorVec4(ImPlotCol_ErrorBar) ) : s.Colors[ImPlotCol_ErrorBar]; - // stage next item style vars - s.LineWeight = s.LineWeight < 0 ? gp.Style.LineWeight : s.LineWeight; - s.Marker = s.Marker < 0 ? gp.Style.Marker : s.Marker; - s.MarkerSize = s.MarkerSize < 0 ? gp.Style.MarkerSize : s.MarkerSize; - s.MarkerWeight = s.MarkerWeight < 0 ? gp.Style.MarkerWeight : s.MarkerWeight; - s.FillAlpha = s.FillAlpha < 0 ? gp.Style.FillAlpha : s.FillAlpha; - s.ErrorBarSize = s.ErrorBarSize < 0 ? gp.Style.ErrorBarSize : s.ErrorBarSize; - s.ErrorBarWeight = s.ErrorBarWeight < 0 ? gp.Style.ErrorBarWeight : s.ErrorBarWeight; - s.DigitalBitHeight = s.DigitalBitHeight < 0 ? gp.Style.DigitalBitHeight : s.DigitalBitHeight; - s.DigitalBitGap = s.DigitalBitGap < 0 ? gp.Style.DigitalBitGap : s.DigitalBitGap; - // apply alpha modifier(s) - s.Colors[ImPlotCol_Fill].w *= s.FillAlpha; - s.Colors[ImPlotCol_MarkerFill].w *= s.FillAlpha; // TODO: this should be separate, if it at all - // apply highlight mods - if (item->LegendHovered) { - if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightItem)) { - s.LineWeight *= ITEM_HIGHLIGHT_LINE_SCALE; - s.MarkerSize *= ITEM_HIGHLIGHT_MARK_SCALE; - s.MarkerWeight *= ITEM_HIGHLIGHT_LINE_SCALE; - // TODO: how to highlight fills? - } - if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)) { - if (gp.CurrentPlot->EnabledAxesX() > 1) - gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX].ColorHiLi = item->Color; - if (gp.CurrentPlot->EnabledAxesY() > 1) - gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY].ColorHiLi = item->Color; - } - } - // set render flags - s.RenderLine = s.Colors[ImPlotCol_Line].w > 0 && s.LineWeight > 0; - s.RenderFill = s.Colors[ImPlotCol_Fill].w > 0; - s.RenderMarkerFill = s.Colors[ImPlotCol_MarkerFill].w > 0; - s.RenderMarkerLine = s.Colors[ImPlotCol_MarkerOutline].w > 0 && s.MarkerWeight > 0; - // push rendering clip rect - PushPlotClipRect(); - return true; - } -} - -// Ends an item (call only if BeginItem returns true) -void EndItem() { - ImPlotContext& gp = *GImPlot; - // pop rendering clip rect - PopPlotClipRect(); - // reset next item data - gp.NextItemData.Reset(); - // set current item - gp.PreviousItem = gp.CurrentItem; - gp.CurrentItem = NULL; -} - -//----------------------------------------------------------------------------- -// [SECTION] Indexers -//----------------------------------------------------------------------------- - -template -IMPLOT_INLINE T IndexData(const T* data, int idx, int count, int offset, int stride) { - const int s = ((offset == 0) << 0) | ((stride == sizeof(T)) << 1); - switch (s) { - case 3 : return data[idx]; - case 2 : return data[(offset + idx) % count]; - case 1 : return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx) ) * stride); - case 0 : return *(const T*)(const void*)((const unsigned char*)data + (size_t)((offset + idx) % count) * stride); - default: return T(0); - } -} - -template -struct IndexerIdx { - IndexerIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) : - Data(data), - Count(count), - Offset(count ? ImPosMod(offset, count) : 0), - Stride(stride) - { } - template IMPLOT_INLINE double operator()(I idx) const { - return (double)IndexData(Data, idx, Count, Offset, Stride); - } - const T* Data; - int Count; - int Offset; - int Stride; -}; - -template -struct IndexerAdd { - IndexerAdd(const _Indexer1& indexer1, const _Indexer2& indexer2, double scale1 = 1, double scale2 = 1) - : Indexer1(indexer1), - Indexer2(indexer2), - Scale1(scale1), - Scale2(scale2), - Count(ImMin(Indexer1.Count, Indexer2.Count)) - { } - template IMPLOT_INLINE double operator()(I idx) const { - return Scale1 * Indexer1(idx) + Scale2 * Indexer2(idx); - } - const _Indexer1& Indexer1; - const _Indexer2& Indexer2; - double Scale1; - double Scale2; - int Count; -}; - -struct IndexerLin { - IndexerLin(double m, double b) : M(m), B(b) { } - template IMPLOT_INLINE double operator()(I idx) const { - return M * idx + B; - } - const double M; - const double B; -}; - -struct IndexerConst { - IndexerConst(double ref) : Ref(ref) { } - template IMPLOT_INLINE double operator()(I) const { return Ref; } - const double Ref; -}; - -//----------------------------------------------------------------------------- -// [SECTION] Getters -//----------------------------------------------------------------------------- - -template -struct GetterXY { - GetterXY(_IndexerX x, _IndexerY y, int count) : IndxerX(x), IndxerY(y), Count(count) { } - template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { - return ImPlotPoint(IndxerX(idx),IndxerY(idx)); - } - const _IndexerX IndxerX; - const _IndexerY IndxerY; - const int Count; -}; - -/// Interprets a user's function pointer as ImPlotPoints -struct GetterFuncPtr { - GetterFuncPtr(ImPlotGetter getter, void* data, int count) : - Getter(getter), - Data(data), - Count(count) - { } - template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { - return Getter(idx, Data); - } - ImPlotGetter Getter; - void* const Data; - const int Count; -}; - -template -struct GetterOverrideX { - GetterOverrideX(_Getter getter, double x) : Getter(getter), X(x), Count(getter.Count) { } - template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { - ImPlotPoint p = Getter(idx); - p.x = X; - return p; - } - const _Getter Getter; - const double X; - const int Count; -}; - -template -struct GetterOverrideY { - GetterOverrideY(_Getter getter, double y) : Getter(getter), Y(y), Count(getter.Count) { } - template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { - ImPlotPoint p = Getter(idx); - p.y = Y; - return p; - } - const _Getter Getter; - const double Y; - const int Count; -}; - -template -struct GetterLoop { - GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) { } - template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { - idx = idx % (Count - 1); - return Getter(idx); - } - const _Getter Getter; - const int Count; -}; - -template -struct GetterError { - GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) : - Xs(xs), - Ys(ys), - Neg(neg), - Pos(pos), - Count(count), - Offset(count ? ImPosMod(offset, count) : 0), - Stride(stride) - { } - template IMPLOT_INLINE ImPlotPointError operator()(I idx) const { - return ImPlotPointError((double)IndexData(Xs, idx, Count, Offset, Stride), - (double)IndexData(Ys, idx, Count, Offset, Stride), - (double)IndexData(Neg, idx, Count, Offset, Stride), - (double)IndexData(Pos, idx, Count, Offset, Stride)); - } - const T* const Xs; - const T* const Ys; - const T* const Neg; - const T* const Pos; - const int Count; - const int Offset; - const int Stride; -}; - -//----------------------------------------------------------------------------- -// [SECTION] Fitters -//----------------------------------------------------------------------------- - -template -struct Fitter1 { - Fitter1(const _Getter1& getter) : Getter(getter) { } - void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { - for (int i = 0; i < Getter.Count; ++i) { - ImPlotPoint p = Getter(i); - x_axis.ExtendFitWith(y_axis, p.x, p.y); - y_axis.ExtendFitWith(x_axis, p.y, p.x); - } - } - const _Getter1& Getter; -}; - -template -struct FitterX { - FitterX(const _Getter1& getter) : Getter(getter) { } - void Fit(ImPlotAxis& x_axis, ImPlotAxis&) const { - for (int i = 0; i < Getter.Count; ++i) { - ImPlotPoint p = Getter(i); - x_axis.ExtendFit(p.x); - } - } - const _Getter1& Getter; -}; - -template -struct FitterY { - FitterY(const _Getter1& getter) : Getter(getter) { } - void Fit(ImPlotAxis&, ImPlotAxis& y_axis) const { - for (int i = 0; i < Getter.Count; ++i) { - ImPlotPoint p = Getter(i); - y_axis.ExtendFit(p.y); - } - } - const _Getter1& Getter; -}; - -template -struct Fitter2 { - Fitter2(const _Getter1& getter1, const _Getter2& getter2) : Getter1(getter1), Getter2(getter2) { } - void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { - for (int i = 0; i < Getter1.Count; ++i) { - ImPlotPoint p = Getter1(i); - x_axis.ExtendFitWith(y_axis, p.x, p.y); - y_axis.ExtendFitWith(x_axis, p.y, p.x); - } - for (int i = 0; i < Getter2.Count; ++i) { - ImPlotPoint p = Getter2(i); - x_axis.ExtendFitWith(y_axis, p.x, p.y); - y_axis.ExtendFitWith(x_axis, p.y, p.x); - } - } - const _Getter1& Getter1; - const _Getter2& Getter2; -}; - -template -struct FitterBarV { - FitterBarV(const _Getter1& getter1, const _Getter2& getter2, double width) : - Getter1(getter1), - Getter2(getter2), - HalfWidth(width*0.5) - { } - void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { - int count = ImMin(Getter1.Count, Getter2.Count); - for (int i = 0; i < count; ++i) { - ImPlotPoint p1 = Getter1(i); p1.x -= HalfWidth; - ImPlotPoint p2 = Getter2(i); p2.x += HalfWidth; - x_axis.ExtendFitWith(y_axis, p1.x, p1.y); - y_axis.ExtendFitWith(x_axis, p1.y, p1.x); - x_axis.ExtendFitWith(y_axis, p2.x, p2.y); - y_axis.ExtendFitWith(x_axis, p2.y, p2.x); - } - } - const _Getter1& Getter1; - const _Getter2& Getter2; - const double HalfWidth; -}; - -template -struct FitterBarH { - FitterBarH(const _Getter1& getter1, const _Getter2& getter2, double height) : - Getter1(getter1), - Getter2(getter2), - HalfHeight(height*0.5) - { } - void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { - int count = ImMin(Getter1.Count, Getter2.Count); - for (int i = 0; i < count; ++i) { - ImPlotPoint p1 = Getter1(i); p1.y -= HalfHeight; - ImPlotPoint p2 = Getter2(i); p2.y += HalfHeight; - x_axis.ExtendFitWith(y_axis, p1.x, p1.y); - y_axis.ExtendFitWith(x_axis, p1.y, p1.x); - x_axis.ExtendFitWith(y_axis, p2.x, p2.y); - y_axis.ExtendFitWith(x_axis, p2.y, p2.x); - } - } - const _Getter1& Getter1; - const _Getter2& Getter2; - const double HalfHeight; -}; - -struct FitterRect { - FitterRect(const ImPlotPoint& pmin, const ImPlotPoint& pmax) : - Pmin(pmin), - Pmax(pmax) - { } - FitterRect(const ImPlotRect& rect) : - FitterRect(rect.Min(), rect.Max()) - { } - void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { - x_axis.ExtendFitWith(y_axis, Pmin.x, Pmin.y); - y_axis.ExtendFitWith(x_axis, Pmin.y, Pmin.x); - x_axis.ExtendFitWith(y_axis, Pmax.x, Pmax.y); - y_axis.ExtendFitWith(x_axis, Pmax.y, Pmax.x); - } - const ImPlotPoint Pmin; - const ImPlotPoint Pmax; -}; - -//----------------------------------------------------------------------------- -// [SECTION] Transformers -//----------------------------------------------------------------------------- - -struct Transformer1 { - Transformer1(double pixMin, double pltMin, double pltMax, double m, double scaMin, double scaMax, ImPlotTransform fwd, void* data) : - ScaMin(scaMin), - ScaMax(scaMax), - PltMin(pltMin), - PltMax(pltMax), - PixMin(pixMin), - M(m), - TransformFwd(fwd), - TransformData(data) - { } - - template IMPLOT_INLINE float operator()(T p) const { - if (TransformFwd != NULL) { - double s = TransformFwd(p, TransformData); - double t = (s - ScaMin) / (ScaMax - ScaMin); - p = PltMin + (PltMax - PltMin) * t; - } - return (float)(PixMin + M * (p - PltMin)); - } - - double ScaMin, ScaMax, PltMin, PltMax, PixMin, M; - ImPlotTransform TransformFwd; - void* TransformData; -}; - -struct Transformer2 { - Transformer2(const ImPlotAxis& x_axis, const ImPlotAxis& y_axis) : - Tx(x_axis.PixelMin, - x_axis.Range.Min, - x_axis.Range.Max, - x_axis.ScaleToPixel, - x_axis.ScaleMin, - x_axis.ScaleMax, - x_axis.TransformForward, - x_axis.TransformData), - Ty(y_axis.PixelMin, - y_axis.Range.Min, - y_axis.Range.Max, - y_axis.ScaleToPixel, - y_axis.ScaleMin, - y_axis.ScaleMax, - y_axis.TransformForward, - y_axis.TransformData) - { } - - Transformer2(const ImPlotPlot& plot) : - Transformer2(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]) - { } - - Transformer2() : - Transformer2(*GImPlot->CurrentPlot) - { } - - template IMPLOT_INLINE ImVec2 operator()(const P& plt) const { - ImVec2 out; - out.x = Tx(plt.x); - out.y = Ty(plt.y); - return out; - } - - template IMPLOT_INLINE ImVec2 operator()(T x, T y) const { - ImVec2 out; - out.x = Tx(x); - out.y = Ty(y); - return out; - } - - Transformer1 Tx; - Transformer1 Ty; -}; - -//----------------------------------------------------------------------------- -// [SECTION] Renderers -//----------------------------------------------------------------------------- - -struct RendererBase { - RendererBase(int prims, int idx_consumed, int vtx_consumed) : - Prims(prims), - IdxConsumed(idx_consumed), - VtxConsumed(vtx_consumed) - { } - const int Prims; - Transformer2 Transformer; - const int IdxConsumed; - const int VtxConsumed; -}; - -template -struct RendererLineStrip : RendererBase { - RendererLineStrip(const _Getter& getter, ImU32 col, float weight) : - RendererBase(getter.Count - 1, 6, 4), - Getter(getter), - Col(col), - HalfWeight(ImMax(1.0f,weight)*0.5f) - { - P1 = this->Transformer(Getter(0)); - } - void Init(ImDrawList& draw_list) const { - GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImVec2 P2 = this->Transformer(Getter(prim + 1)); - if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { - P1 = P2; - return false; - } - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); - P1 = P2; - return true; - } - const _Getter& Getter; - const ImU32 Col; - mutable float HalfWeight; - mutable ImVec2 P1; - mutable ImVec2 UV0; - mutable ImVec2 UV1; -}; - -template -struct RendererLineStripSkip : RendererBase { - RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight) : - RendererBase(getter.Count - 1, 6, 4), - Getter(getter), - Col(col), - HalfWeight(ImMax(1.0f,weight)*0.5f) - { - P1 = this->Transformer(Getter(0)); - } - void Init(ImDrawList& draw_list) const { - GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImVec2 P2 = this->Transformer(Getter(prim + 1)); - if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { - if (!ImNan(P2.x) && !ImNan(P2.y)) - P1 = P2; - return false; - } - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); - if (!ImNan(P2.x) && !ImNan(P2.y)) - P1 = P2; - return true; - } - const _Getter& Getter; - const ImU32 Col; - mutable float HalfWeight; - mutable ImVec2 P1; - mutable ImVec2 UV0; - mutable ImVec2 UV1; -}; - -template -struct RendererLineSegments1 : RendererBase { - RendererLineSegments1(const _Getter& getter, ImU32 col, float weight) : - RendererBase(getter.Count / 2, 6, 4), - Getter(getter), - Col(col), - HalfWeight(ImMax(1.0f,weight)*0.5f) - { } - void Init(ImDrawList& draw_list) const { - GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImVec2 P1 = this->Transformer(Getter(prim*2+0)); - ImVec2 P2 = this->Transformer(Getter(prim*2+1)); - if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) - return false; - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); - return true; - } - const _Getter& Getter; - const ImU32 Col; - mutable float HalfWeight; - mutable ImVec2 UV0; - mutable ImVec2 UV1; -}; - -template -struct RendererLineSegments2 : RendererBase { - RendererLineSegments2(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, float weight) : - RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), - Getter1(getter1), - Getter2(getter2), - Col(col), - HalfWeight(ImMax(1.0f,weight)*0.5f) - {} - void Init(ImDrawList& draw_list) const { - GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImVec2 P1 = this->Transformer(Getter1(prim)); - ImVec2 P2 = this->Transformer(Getter2(prim)); - if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) - return false; - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); - return true; - } - const _Getter1& Getter1; - const _Getter2& Getter2; - const ImU32 Col; - mutable float HalfWeight; - mutable ImVec2 UV0; - mutable ImVec2 UV1; -}; - -template -struct RendererBarsFillV : RendererBase { - RendererBarsFillV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width) : - RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), - Getter1(getter1), - Getter2(getter2), - Col(col), - HalfWidth(width/2) - {} - void Init(ImDrawList& draw_list) const { - UV = draw_list._Data->TexUvWhitePixel; - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImPlotPoint p1 = Getter1(prim); - ImPlotPoint p2 = Getter2(prim); - p1.x += HalfWidth; - p2.x -= HalfWidth; - ImVec2 P1 = this->Transformer(p1); - ImVec2 P2 = this->Transformer(p2); - float width_px = ImAbs(P1.x-P2.x); - if (width_px < 1.0f) { - P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; - P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2; - } - ImVec2 PMin = ImMin(P1, P2); - ImVec2 PMax = ImMax(P1, P2); - if (!cull_rect.Overlaps(ImRect(PMin, PMax))) - return false; - PrimRectFill(draw_list,PMin,PMax,Col,UV); - return true; - } - const _Getter1& Getter1; - const _Getter2& Getter2; - const ImU32 Col; - const double HalfWidth; - mutable ImVec2 UV; -}; - -template -struct RendererBarsFillH : RendererBase { - RendererBarsFillH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height) : - RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), - Getter1(getter1), - Getter2(getter2), - Col(col), - HalfHeight(height/2) - {} - void Init(ImDrawList& draw_list) const { - UV = draw_list._Data->TexUvWhitePixel; - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImPlotPoint p1 = Getter1(prim); - ImPlotPoint p2 = Getter2(prim); - p1.y += HalfHeight; - p2.y -= HalfHeight; - ImVec2 P1 = this->Transformer(p1); - ImVec2 P2 = this->Transformer(p2); - float height_px = ImAbs(P1.y-P2.y); - if (height_px < 1.0f) { - P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; - P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2; - } - ImVec2 PMin = ImMin(P1, P2); - ImVec2 PMax = ImMax(P1, P2); - if (!cull_rect.Overlaps(ImRect(PMin, PMax))) - return false; - PrimRectFill(draw_list,PMin,PMax,Col,UV); - return true; - } - const _Getter1& Getter1; - const _Getter2& Getter2; - const ImU32 Col; - const double HalfHeight; - mutable ImVec2 UV; -}; - -template -struct RendererBarsLineV : RendererBase { - RendererBarsLineV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width, float weight) : - RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), - Getter1(getter1), - Getter2(getter2), - Col(col), - HalfWidth(width/2), - Weight(weight) - {} - void Init(ImDrawList& draw_list) const { - UV = draw_list._Data->TexUvWhitePixel; - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImPlotPoint p1 = Getter1(prim); - ImPlotPoint p2 = Getter2(prim); - p1.x += HalfWidth; - p2.x -= HalfWidth; - ImVec2 P1 = this->Transformer(p1); - ImVec2 P2 = this->Transformer(p2); - float width_px = ImAbs(P1.x-P2.x); - if (width_px < 1.0f) { - P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; - P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2; - } - ImVec2 PMin = ImMin(P1, P2); - ImVec2 PMax = ImMax(P1, P2); - if (!cull_rect.Overlaps(ImRect(PMin, PMax))) - return false; - PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); - return true; - } - const _Getter1& Getter1; - const _Getter2& Getter2; - const ImU32 Col; - const double HalfWidth; - const float Weight; - mutable ImVec2 UV; -}; - -template -struct RendererBarsLineH : RendererBase { - RendererBarsLineH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height, float weight) : - RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), - Getter1(getter1), - Getter2(getter2), - Col(col), - HalfHeight(height/2), - Weight(weight) - {} - void Init(ImDrawList& draw_list) const { - UV = draw_list._Data->TexUvWhitePixel; - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImPlotPoint p1 = Getter1(prim); - ImPlotPoint p2 = Getter2(prim); - p1.y += HalfHeight; - p2.y -= HalfHeight; - ImVec2 P1 = this->Transformer(p1); - ImVec2 P2 = this->Transformer(p2); - float height_px = ImAbs(P1.y-P2.y); - if (height_px < 1.0f) { - P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; - P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2; - } - ImVec2 PMin = ImMin(P1, P2); - ImVec2 PMax = ImMax(P1, P2); - if (!cull_rect.Overlaps(ImRect(PMin, PMax))) - return false; - PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); - return true; - } - const _Getter1& Getter1; - const _Getter2& Getter2; - const ImU32 Col; - const double HalfHeight; - const float Weight; - mutable ImVec2 UV; -}; - - -template -struct RendererStairsPre : RendererBase { - RendererStairsPre(const _Getter& getter, ImU32 col, float weight) : - RendererBase(getter.Count - 1, 12, 8), - Getter(getter), - Col(col), - HalfWeight(ImMax(1.0f,weight)*0.5f) - { - P1 = this->Transformer(Getter(0)); - } - void Init(ImDrawList& draw_list) const { - UV = draw_list._Data->TexUvWhitePixel; - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImVec2 P2 = this->Transformer(Getter(prim + 1)); - if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { - P1 = P2; - return false; - } - PrimRectFill(draw_list, ImVec2(P1.x - HalfWeight, P1.y), ImVec2(P1.x + HalfWeight, P2.y), Col, UV); - PrimRectFill(draw_list, ImVec2(P1.x, P2.y + HalfWeight), ImVec2(P2.x, P2.y - HalfWeight), Col, UV); - P1 = P2; - return true; - } - const _Getter& Getter; - const ImU32 Col; - mutable float HalfWeight; - mutable ImVec2 P1; - mutable ImVec2 UV; -}; - -template -struct RendererStairsPost : RendererBase { - RendererStairsPost(const _Getter& getter, ImU32 col, float weight) : - RendererBase(getter.Count - 1, 12, 8), - Getter(getter), - Col(col), - HalfWeight(ImMax(1.0f,weight) * 0.5f) - { - P1 = this->Transformer(Getter(0)); - } - void Init(ImDrawList& draw_list) const { - UV = draw_list._Data->TexUvWhitePixel; - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImVec2 P2 = this->Transformer(Getter(prim + 1)); - if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { - P1 = P2; - return false; - } - PrimRectFill(draw_list, ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), Col, UV); - PrimRectFill(draw_list, ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), Col, UV); - P1 = P2; - return true; - } - const _Getter& Getter; - const ImU32 Col; - mutable float HalfWeight; - mutable ImVec2 P1; - mutable ImVec2 UV; -}; - -template -struct RendererStairsPreShaded : RendererBase { - RendererStairsPreShaded(const _Getter& getter, ImU32 col) : - RendererBase(getter.Count - 1, 6, 4), - Getter(getter), - Col(col) - { - P1 = this->Transformer(Getter(0)); - Y0 = this->Transformer(ImPlotPoint(0,0)).y; - } - void Init(ImDrawList& draw_list) const { - UV = draw_list._Data->TexUvWhitePixel; - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImVec2 P2 = this->Transformer(Getter(prim + 1)); - ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(Y0, P2.y)); - ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(Y0, P2.y)); - if (!cull_rect.Overlaps(ImRect(PMin, PMax))) { - P1 = P2; - return false; - } - PrimRectFill(draw_list, PMin, PMax, Col, UV); - P1 = P2; - return true; - } - const _Getter& Getter; - const ImU32 Col; - float Y0; - mutable ImVec2 P1; - mutable ImVec2 UV; -}; - -template -struct RendererStairsPostShaded : RendererBase { - RendererStairsPostShaded(const _Getter& getter, ImU32 col) : - RendererBase(getter.Count - 1, 6, 4), - Getter(getter), - Col(col) - { - P1 = this->Transformer(Getter(0)); - Y0 = this->Transformer(ImPlotPoint(0,0)).y; - } - void Init(ImDrawList& draw_list) const { - UV = draw_list._Data->TexUvWhitePixel; - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImVec2 P2 = this->Transformer(Getter(prim + 1)); - ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(P1.y, Y0)); - ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(P1.y, Y0)); - if (!cull_rect.Overlaps(ImRect(PMin, PMax))) { - P1 = P2; - return false; - } - PrimRectFill(draw_list, PMin, PMax, Col, UV); - P1 = P2; - return true; - } - const _Getter& Getter; - const ImU32 Col; - float Y0; - mutable ImVec2 P1; - mutable ImVec2 UV; -}; - - - -template -struct RendererShaded : RendererBase { - RendererShaded(const _Getter1& getter1, const _Getter2& getter2, ImU32 col) : - RendererBase(ImMin(getter1.Count, getter2.Count) - 1, 6, 5), - Getter1(getter1), - Getter2(getter2), - Col(col) - { - P11 = this->Transformer(Getter1(0)); - P12 = this->Transformer(Getter2(0)); - } - void Init(ImDrawList& draw_list) const { - UV = draw_list._Data->TexUvWhitePixel; - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImVec2 P21 = this->Transformer(Getter1(prim+1)); - ImVec2 P22 = this->Transformer(Getter2(prim+1)); - ImRect rect(ImMin(ImMin(ImMin(P11,P12),P21),P22), ImMax(ImMax(ImMax(P11,P12),P21),P22)); - if (!cull_rect.Overlaps(rect)) { - P11 = P21; - P12 = P22; - return false; - } - const int intersect = (P11.y > P12.y && P22.y > P21.y) || (P12.y > P11.y && P21.y > P22.y); - ImVec2 intersection = Intersection(P11,P21,P12,P22); - draw_list._VtxWritePtr[0].pos = P11; - draw_list._VtxWritePtr[0].uv = UV; - draw_list._VtxWritePtr[0].col = Col; - draw_list._VtxWritePtr[1].pos = P21; - draw_list._VtxWritePtr[1].uv = UV; - draw_list._VtxWritePtr[1].col = Col; - draw_list._VtxWritePtr[2].pos = intersection; - draw_list._VtxWritePtr[2].uv = UV; - draw_list._VtxWritePtr[2].col = Col; - draw_list._VtxWritePtr[3].pos = P12; - draw_list._VtxWritePtr[3].uv = UV; - draw_list._VtxWritePtr[3].col = Col; - draw_list._VtxWritePtr[4].pos = P22; - draw_list._VtxWritePtr[4].uv = UV; - draw_list._VtxWritePtr[4].col = Col; - draw_list._VtxWritePtr += 5; - draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); - draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1 + intersect); - draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); - draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); - draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); - draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3 - intersect); - draw_list._IdxWritePtr += 6; - draw_list._VtxCurrentIdx += 5; - P11 = P21; - P12 = P22; - return true; - } - const _Getter1& Getter1; - const _Getter2& Getter2; - const ImU32 Col; - mutable ImVec2 P11; - mutable ImVec2 P12; - mutable ImVec2 UV; -}; - -struct RectC { - ImPlotPoint Pos; - ImPlotPoint HalfSize; - ImU32 Color; -}; - -template -struct RendererRectC : RendererBase { - RendererRectC(const _Getter& getter) : - RendererBase(getter.Count, 6, 4), - Getter(getter) - {} - void Init(ImDrawList& draw_list) const { - UV = draw_list._Data->TexUvWhitePixel; - } - IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - RectC rect = Getter(prim); - ImVec2 P1 = this->Transformer(rect.Pos.x - rect.HalfSize.x , rect.Pos.y - rect.HalfSize.y); - ImVec2 P2 = this->Transformer(rect.Pos.x + rect.HalfSize.x , rect.Pos.y + rect.HalfSize.y); - if ((rect.Color & IM_COL32_A_MASK) == 0 || !cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) - return false; - PrimRectFill(draw_list,P1,P2,rect.Color,UV); - return true; - } - const _Getter& Getter; - mutable ImVec2 UV; -}; - -//----------------------------------------------------------------------------- -// [SECTION] RenderPrimitives -//----------------------------------------------------------------------------- - -/// Renders primitive shapes in bulk as efficiently as possible. -template -void RenderPrimitivesEx(const _Renderer& renderer, ImDrawList& draw_list, const ImRect& cull_rect) { - unsigned int prims = renderer.Prims; - unsigned int prims_culled = 0; - unsigned int idx = 0; - renderer.Init(draw_list); - while (prims) { - // find how many can be reserved up to end of current draw command's limit - unsigned int cnt = ImMin(prims, (MaxIdx::Value - draw_list._VtxCurrentIdx) / renderer.VtxConsumed); - // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time - if (cnt >= ImMin(64u, prims)) { - if (prims_culled >= cnt) - prims_culled -= cnt; // reuse previous reservation - else { - // add more elements to previous reservation - draw_list.PrimReserve((cnt - prims_culled) * renderer.IdxConsumed, (cnt - prims_culled) * renderer.VtxConsumed); - prims_culled = 0; - } - } - else - { - if (prims_culled > 0) { - draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed); - prims_culled = 0; - } - cnt = ImMin(prims, (MaxIdx::Value - 0/*draw_list._VtxCurrentIdx*/) / renderer.VtxConsumed); - // reserve new draw command - draw_list.PrimReserve(cnt * renderer.IdxConsumed, cnt * renderer.VtxConsumed); - } - prims -= cnt; - for (unsigned int ie = idx + cnt; idx != ie; ++idx) { - if (!renderer.Render(draw_list, cull_rect, idx)) - prims_culled++; - } - } - if (prims_culled > 0) - draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed); -} - -template