承接 microscrap/sdl3 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

microscrap/sdl3

Composer 安装命令:

composer require microscrap/sdl3

包简介

LibSDL3 Bindings for The PHP SDL3 Extension

README 文档

README

PHP library that wraps the sdl3 extension with global helpers, enums, and data objects. Every helper delegates to a static wrapper class under Microscrap\Bindings\SDL3.

The package covers the entire extension surface (636+ methods across 22 extension classes): init, error, timers, properties, surfaces, 2D rendering, video/windows, OpenGL, events, keyboard, mouse, display, clipboard, joystick, gamepad, audio, file dialogs, and the full SDL GPU API.

Highlights

  • Two calling styles — exact C names (SDL_CreateWindow(...)) or static wrapper classes (Video::createWindow(...))
  • Every opaque SDL handle wrapped in a typed final readonly data object (SDLWindow, SDLRenderer, SDLGPUDevice, …)
  • Full enum layer transcribed from the SDL 3.4.x headers — 67 int/string-backed enums including Scancode (~290 cases), PixelFormat, EventType, and the GPU format family
  • C-style error handling: no exceptions in src/, failed creations return null, failed operations return false; details via SDL_GetError()
  • Headless-friendly: software renderer, dummy video driver, virtual joysticks, driverless audio streams all work without a display
  • Coverage drift guard: a Pest test reflects the extension and fails if any extension method lacks a wrapper method or helper function

Requirements

Installation

Confirm ext-sdl3 is loaded:

php -m | grep sdl3
composer require microscrap/sdl3

Composer autoloads all helper files in src/Helpers/, registering the global SDL_* functions when the package is installed. Helpers are only defined if the name is not already taken (function_exists guard).

The two calling styles

C-ish — global functions with exact SDL C names:

SDL_Init(InitFlag::SDL_INIT_VIDEO->value);
$window = SDL_CreateWindow('hello', 640, 480, WindowFlag::SDL_WINDOW_HIDDEN);
SDL_DestroyWindow($window);
SDL_Quit();

OO-ish — static wrapper classes, same behavior:

use Microscrap\Bindings\SDL3\Init;
use Microscrap\Bindings\SDL3\Video;

Init::init(InitFlag::SDL_INIT_VIDEO);
$window = Video::createWindow('hello', 640, 480, WindowFlag::SDL_WINDOW_HIDDEN);
Video::destroyWindow($window);
Init::quit();

Helpers never touch the extension directly; they delegate one-to-one to the wrapper classes, which are the only layer calling Sdl3\SDL\*. Both styles accept and return the same data objects, and every flag/enum parameter takes EnumType|int.

Name transforms

  • Wrapper methods drop the SDL prefix: SDLCreateWindowVideo::createWindow().
  • The GL class also drops the redundant GL token (SDLGLCreateContextGL::createContext()); EGL methods keep a lowercase egl prefix.
  • The GPU class drops the redundant GPU token (SDLCreateGPUTextureGPU::createTexture()).
  • Helpers use the exact SDL C name, including the odd ones: SDL_rand(), SDL_GL_CreateContext(), SDL_GDKSuspendGPU(). The extension-only transfer-buffer conveniences keep their extension names (writeToGPUTransferBuffer(), readFromGPUTransferBuffer()).

Wrapper classes

Class Wraps Methods Subsystem
Init Sdl3\SDL\SDL 22 init/quit, version, platform, app metadata
Error SDLError 4 get/set/clear error
Timer Timer\SDLTimer 2 ticks, delay
Properties SDLProperties 20 property groups
Surface Surface\SDLSurface 72 surfaces, palettes, pixel formats
Render Render\SDLRender 94 renderers, textures
Video Video\SDLVideo 83 windows, video drivers
GL Video\SDLGL 20 OpenGL + EGL
Events Events\SDLEvents + 4 more 25 event queue, watches, quit, drops
Keyboard Events\SDLKeyboard 28 keyboard state, keycodes
Mouse Events\SDLMouse 27 mouse state, cursors
Display Events\SDLDisplayEvents 13 displays, modes
Clipboard Events\SDLClipboardEvents 10 clipboard text/data
Joystick Input\SDLJoystick 58 joysticks incl. virtual
Gamepad Input\SDLGamepad 72 gamepads, mappings, sensors
Audio Audio\SDLAudio 57 devices, streams, WAV
Dialog Dialog\SDLDialog 4 native file dialogs
GPU Gpu\SDLGPU 105 full SDL GPU API + render states

Data objects live under Microscrap\Bindings\SDL3\DataObjects (27 classes; each holds the raw handle as ->ptr, or ->id for instance IDs). Enums live under Microscrap\Bindings\SDL3\Enums (67 enums; case names match the C macros exactly).

Examples

Software rendering (fully headless)

use Microscrap\Bindings\SDL3\Enums\PixelFormat;
use Sdl3\SDL\Surface\SDLSurface;

SDL_Init(0);

$surface = SDLSurface::SDLCreateSurface(64, 64, PixelFormat::SDL_PIXELFORMAT_RGBA8888->value);
$renderer = SDL_CreateSoftwareRenderer((int) $surface['ptr']);

SDL_SetRenderDrawColor($renderer, 255, 0, 0, 255);
SDL_RenderClear($renderer);
SDL_RenderFillRect($renderer, ['x' => 8, 'y' => 8, 'w' => 48, 'h' => 48]);
SDL_RenderPresent($renderer);

$pixels = SDL_RenderReadPixels($renderer);

SDL_DestroyRenderer($renderer);
SDLSurface::SDLDestroySurface((int) $surface['ptr']);
SDL_Quit();

Window + events (dummy driver works headless)

use Microscrap\Bindings\SDL3\Enums\EventType;
use Microscrap\Bindings\SDL3\Enums\InitFlag;
use Microscrap\Bindings\SDL3\Enums\WindowFlag;

putenv('SDL_VIDEODRIVER=dummy');
SDL_Init(InitFlag::SDL_INIT_VIDEO->value);

$window = SDL_CreateWindow('demo', 320, 240, WindowFlag::SDL_WINDOW_HIDDEN);

while (! is_null($event = SDL_PollEvent())) { // ?SDLEventRef
    if ($event->eventType === EventType::SDL_EVENT_QUIT->value) {
        break;
    }
}

SDL_DestroyWindow($window);
SDL_Quit();

Virtual joystick (no hardware needed)

use Microscrap\Bindings\SDL3\Enums\InitFlag;
use Microscrap\Bindings\SDL3\Enums\JoystickType;

SDL_Init(InitFlag::SDL_INIT_JOYSTICK->value);

$id = SDL_AttachVirtualJoystick([
    'type' => JoystickType::SDL_JOYSTICK_TYPE_GAMEPAD->value,
    'naxes' => 2,
    'nbuttons' => 4,
]);
$joystick = SDL_OpenJoystick($id);

SDL_SetJoystickVirtualAxis($joystick, 0, 12345);
SDL_UpdateJoysticks();
$value = SDL_GetJoystickAxis($joystick, 0); // 12345

SDL_CloseJoystick($joystick);
SDL_DetachVirtualJoystick($id);
SDL_Quit();

Audio stream round-trip (driverless)

use Microscrap\Bindings\SDL3\Enums\AudioFormat;

SDL_Init(0);

$spec = ['format' => AudioFormat::SDL_AUDIO_S16LE->value, 'channels' => 2, 'freq' => 44100];
$stream = SDL_CreateAudioStream($spec, $spec);

SDL_PutAudioStreamData($stream, $pcmBytes);
SDL_FlushAudioStream($stream);
$out = SDL_GetAudioStreamData($stream, strlen($pcmBytes));

SDL_DestroyAudioStream($stream);
SDL_Quit();

GPU buffer round-trip (Metal/Vulkan/D3D12)

use Microscrap\Bindings\SDL3\Enums\GPUBufferUsage;
use Microscrap\Bindings\SDL3\Enums\GPUShaderFormat;
use Microscrap\Bindings\SDL3\Enums\GPUTransferBufferUsage;
use Microscrap\Bindings\SDL3\Enums\InitFlag;
use Microscrap\Bindings\SDL3\GPU;

SDL_Init(InitFlag::SDL_INIT_VIDEO->value); // GPU devices need the video subsystem

$device = GPU::createDevice(GPUShaderFormat::SDL_GPU_SHADERFORMAT_MSL->value | GPUShaderFormat::SDL_GPU_SHADERFORMAT_SPIRV->value);

$buffer = GPU::createBuffer($device, ['usage' => GPUBufferUsage::SDL_GPU_BUFFERUSAGE_VERTEX->value, 'size' => 256]);
$upload = GPU::createTransferBuffer($device, ['usage' => GPUTransferBufferUsage::SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD->value, 'size' => 256]);

GPU::writeToTransferBuffer($device, $upload, $payload);

$cb = GPU::acquireCommandBuffer($device);
$copyPass = GPU::beginCopyPass($cb);
GPU::uploadToBuffer($copyPass, ['transfer_buffer' => $upload->ptr, 'offset' => 0], ['buffer' => $buffer->ptr, 'offset' => 0, 'size' => 256]);
GPU::endCopyPass($copyPass);

$fence = GPU::submitCommandBufferAndAcquireFence($cb);
GPU::waitForFences($device, true, [$fence]);
GPU::releaseFence($device, $fence);

GPU::releaseTransferBuffer($device, $upload);
GPU::releaseBuffer($device, $buffer);
GPU::destroyDevice($device);
SDL_Quit();

Struct parameters (rects, audio specs, GPU create-infos, pass targets) are associative arrays mirroring the C struct field names exactly; each wrapper method documents the expected keys in its docblock.

Error handling

Nothing in src/ throws. The package keeps SDL's C conventions:

  • creation functions return null on failure (?SDLWindow, ?SDLGPUDevice, …)
  • operations return false (or -1) on failure
  • call SDL_GetError() for the reason

Note: the underlying extension itself throws RuntimeException from a handful of calls (e.g. SDL_CreateGPUDevice with no usable driver, GDK functions off-Xbox). Those propagate as-is.

Testing

./vendor/bin/pest
  • tests/Unit runs without the extension: coverage drift guard (against a committed 0.5.0 method snapshot), style audit (no class constants, no throws, guarded helpers, backed enums, uppercase cases).
  • tests/Feature is gated on extension_loaded('sdl3') and runs headless: dummy video driver, software renderer pixel round-trips, virtual joysticks, driverless audio streams, and a real GPU buffer round-trip when a device is available (skips gracefully otherwise).

License

MIT. See LICENSE.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 2
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-07

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固