UNPKG

@kmamal/sdl

Version:
1,148 lines (853 loc) 301 kB
# @kmamal/sdl [![Package](https://img.shields.io/npm/v/%2540kmamal%252Fsdl)](https://www.npmjs.com/package/@kmamal/sdl) [![Dependencies](https://img.shields.io/librariesio/release/npm/@kmamal/sdl)](https://libraries.io/npm/@kmamal%2Fsdl) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) SDL bindings for Node.js. Provides access to systems that are not normally available to Node.js applications: - 💻 Window management - ⌨ Keyboard - 🖱 Mouse - 🕹 Joysticks - 🎮 Controllers - 🔈 Audio playback - 🎤 Audio recording - 📋 Clipboard manipulation - 🔋 Battery status - 🧭 Sensors Also allows using Canvas2D, WebGL, and WebGPU without a browser, through these libraries: - __Canvas2D:__ [@napi-rs/canvas](https://www.npmjs.com/package/@napi-rs/canvas). In my experience, this is the fastest library out of the many available on npm. - __WebGL:__ [@kmamal/gl](https://github.com/kmamal/headless-gl#readme). This is a fork of [headless-gl](https://github.com/stackgl/headless-gl#readme) that I've modified to render directly to SDL windows. - __WebGPU:__ [@kmamal/gpu](https://github.com/kmamal/gpu#readme). This is a fork of [Google Dawn](https://dawn.googlesource.com/dawn/+/refs/heads/main/src/dawn/node/) that I've modified to render directly to SDL windows. Officially supports Linux (x64, arm64), Mac (x64, arm64), and Windows (x64). Should theoretically work on any system supported by both SDL and Node.js, but I haven't tried any others. Prebuilt binaries are available for the supported architectures. ## Installation This package is self-contained. Just run: ```bash npm install @kmamal/sdl ``` You do __not__ have to manually install any other libs or DLLs to your system. A compatible version of SDL will be automatically downloaded by the install script and placed inside `node_modules` along with this lib's prebuilt binding binaries. (But if the install script fails, have a look at the instructions for [building the package manually](#building-from-source)) ## Examples ### "Hello, World!" ```js import sdl from '@kmamal/sdl' const window = sdl.video.createWindow({ title: "Hello, World!" }) window.on('*', console.log) ``` ### Canvas2D ```js import sdl from '@kmamal/sdl' import { createCanvas } from '@napi-rs/canvas' // Setup const window = sdl.video.createWindow({ title: "Canvas2D" }) const { pixelWidth: width, pixelHeight: height } = window const canvas = createCanvas(width, height) const ctx = canvas.getContext('2d') // Clear screen to red ctx.fillStyle = 'red' ctx.fillRect(0, 0, width, height) // Render to window const buffer = Buffer.from(ctx.getImageData(0, 0, width, height).data) window.render(width, height, width * 4, 'rgba32', buffer) ``` ### WebGL ```js import sdl from '@kmamal/sdl' import createContext from '@kmamal/gl' // Setup const window = sdl.video.createWindow({ title: "WebGL", opengl: true }) const { pixelWidth: width, pixelHeight: height, native } = window const gl = createContext(width, height, { window: native }) // Clear screen to red gl.clearColor(1, 0, 0, 1) gl.clear(gl.COLOR_BUFFER_BIT) // Render to window gl.swap() ``` ### WebGPU ```js import sdl from '@kmamal/sdl' import gpu from '@kmamal/gpu' // Setup const window = sdl.video.createWindow({ title: "WebGPU", webgpu: true }) const instance = gpu.create([]) const adapter = await instance.requestAdapter() const device = await adapter.requestDevice() const renderer = gpu.renderGPUDeviceToWindow({ device, window }) // Clear screen to red const commandEncoder = device.createCommandEncoder() const renderPass = commandEncoder.beginRenderPass({ colorAttachments: [ { view: renderer.getCurrentTextureView(), clearValue: { r: 1.0, g: 0.0, b: 0.0, a: 1.0 }, loadOp: 'clear', storeOp: 'store', }, ], }) renderPass.end() device.queue.submit([ commandEncoder.finish() ]) // Render to window renderer.swap() ``` ### More examples Check the [`examples/`](https://github.com/kmamal/node-sdl/tree/master/examples#readme) folder. # API Reference ## Contents - [sdl](#sdl) - [sdl.info](#sdlinfo) - [sdl.video](#sdlvideo) - [Image data](#image-data) - [High-DPI](#high-dpi) - [Pixel formats](#pixel-formats) - [Event: 'displayAdd'](#event-displayadd) - [Event: 'displayRemove'](#event-displayremove) - [Event: 'displayOrient'](#event-displayorient) - [Event: 'displayMove'](#event-displaymove) - [sdl.video.displays](#sdlvideodisplays) - [sdl.video.windows](#sdlvideowindows) - [sdl.video.focused](#sdlvideofocused) - [sdl.video.hovered](#sdlvideohovered) - [sdl.video.createWindow([options])](#sdlvideocreatewindowoptions) - [class Window](#class-window) - [Event: 'show'](#event-show) - [Event: 'hide'](#event-hide) - [Event: 'expose'](#event-expose) - [Event: 'minimize'](#event-minimize) - [Event: 'maximize'](#event-maximize) - [Event: 'restore'](#event-restore) - [Event: 'move'](#event-move) - [Event: 'resize'](#event-resize) - [Event: 'displayChange'](#event-displaychange) - [Event: 'focus'](#event-focus) - [Event: 'blur'](#event-blur) - [Event: 'hover'](#event-hover) - [Event: 'leave'](#event-leave) - [Event: 'beforeClose'](#event-beforeclose) - [Event: 'close'](#event-close) - [Event: 'keyDown'](#event-keydown) - [Event: 'keyUp'](#event-keyup) - [Event: 'textInput'](#event-textinput) - [Event: 'mouseButtonDown'](#event-mousebuttondown) - [Event: 'mouseButtonUp'](#event-mousebuttonup) - [Event: 'mouseMove'](#event-mousemove) - [Event: 'mouseWheel'](#event-mousewheel) - [Event: 'fingerDown'](#event-fingerdown) - [Event: 'fingerUp'](#event-fingerup) - [Event: 'fingerMove'](#event-fingermove) - [Event: 'dropBegin'](#event-dropbegin) - [Event: 'dropText'](#event-droptext) - [Event: 'dropFile'](#event-dropfile) - [Event: 'dropComplete'](#event-dropcomplete) - [window.id](#windowid) - [window.title](#windowtitle) - [window.setTitle(title)](#windowsettitletitle) - [window.x](#windowx) - [window.y](#windowy) - [window.setPosition(x, y)](#windowsetpositionx-y) - [window.width](#windowwidth) - [window.height](#windowheight) - [window.pixelWidth](#windowpixelwidth) - [window.pixelHeight](#windowpixelheight) - [window.setSize(width, height)](#windowsetsizewidth-height) - [window.setSizeInPixels(pixelWidth, pixelHeight)](#windowsetsizeinpixelspixelwidth-pixelheight) - [window.display](#windowdisplay) - [window.visible](#windowvisible) - [window.show([show])](#windowshowshow) - [window.hide()](#windowhide) - [window.fullscreen](#windowfullscreen) - [window.setFullscreen(fullscreen)](#windowsetfullscreenfullscreen) - [window.resizable](#windowresizable) - [window.setResizable(resizable)](#windowsetresizableresizable) - [window.borderless](#windowborderless) - [window.setBorderless(borderless)](#windowsetborderlessborderless) - [window.alwaysOnTop](#windowalwaysontop) - [window.accelerated](#windowaccelerated) - [window.setAccelerated(accelerated)](#windowsetacceleratedaccelerated) - [window.vsync](#windowvsync) - [window.setVsync(vsync)](#windowsetvsyncvsync) - [window.opengl](#windowopengl) - [window.webgpu](#windowwebgpu) - [window.native](#windownative) - [window.maximized](#windowmaximized) - [window.maximize()](#windowmaximize) - [window.minimized](#windowminimized) - [window.minimize()](#windowminimize) - [window.restore()](#windowrestore) - [window.focused](#windowfocused) - [window.focus()](#windowfocus) - [window.hovered](#windowhovered) - [window.skipTaskbar](#windowskiptaskbar) - [window.popupMenu](#windowpopupmenu) - [window.tooltip](#windowtooltip) - [window.utility](#windowutility) - [window.render(width, height, stride, format, buffer[, options])](#windowrenderwidth-height-stride-format-buffer-options) - [window.setIcon(width, height, stride, format, buffer)](#windowseticonwidth-height-stride-format-buffer) - [window.flash([untilFocused])](#windowflashuntilfocused) - [window.stopFlashing()](#windowstopflashing) - [window.destroyed](#windowdestroyed) - [window.destroy()](#windowdestroy) - [window.destroyGently()](#windowdestroygently) - [sdl.keyboard](#sdlkeyboard) - [Virtual keys](#virtual-keys) - [Enum: SCANCODE](#enum-scancode) - [Event: 'keymapChange'](#event-keymapchange) - [sdl.keyboard.getKey(scancode)](#sdlkeyboardgetkeyscancode) - [sdl.keyboard.getScancode(key)](#sdlkeyboardgetscancodekey) - [sdl.keyboard.getState()](#sdlkeyboardgetstate) - [sdl.mouse](#sdlmouse) - [Enum: BUTTON](#enum-button) - [sdl.mouse.getButton(button)](#sdlmousegetbuttonbutton) - [sdl.mouse.position](#sdlmouseposition) - [sdl.mouse.setPosition(x, y)](#sdlmousesetpositionx-y) - [sdl.mouse.setCursor(cursor)](#sdlmousesetcursorcursor) - [sdl.mouse.resetCursor()](#sdlmouseresetcursor) - [sdl.mouse.setCursorImage(width, height, stride, format, buffer, x, y)](#sdlmousesetcursorimagewidth-height-stride-format-buffer-x-y) - [sdl.mouse.showCursor([show])](#sdlmouseshowcursorshow) - [sdl.mouse.hideCursor()](#sdlmousehidecursor) - [sdl.mouse.redrawCursor()](#sdlmouseredrawcursor) - [sdl.mouse.capture([capture])](#sdlmousecapturecapture) - [sdl.mouse.uncapture()](#sdlmouseuncapture) - [sdl.joystick](#sdljoystick) - [Hat positions](#hat-positions) - [Power levels](#power-levels) - [Event: 'deviceAdd'](#joystick-event-deviceadd) - [Event: 'deviceRemove'](#joystick-event-deviceremove) - [sdl.joystick.devices](#sdljoystickdevices) - [sdl.joystick.openDevice(device)](#sdljoystickopendevicedevice) - [class JoystickInstance](#class-joystickinstance) - [Event: 'axisMotion'](#joystick-instance-event-axismotion) - [Event: 'ballMotion'](#event-ballmotion) - [Event: 'buttonDown'](#joystick-instance-event-buttondown) - [Event: 'buttonUp'](#joystick-instance-event-buttonup) - [Event: 'hatMotion'](#event-hatmotion) - [Event: 'powerUpdate'](#joystick-instance-event-power-update) - [Event: 'close'](#joystick-instance-event-close) - [joystickInstance.device](#joystickinstancedevice) - [joystickInstance.firmwareVersion](#joystickinstancefirmwareversion) - [joystickInstance.serialNumber](#joystickinstanceserialnumber) - [joystickInstance.axes](#joystickinstanceaxes) - [joystickInstance.balls](#joystickinstanceballs) - [joystickInstance.buttons](#joystickinstancebuttons) - [joystickInstance.hats](#joystickinstancehats) - [joystickInstance.power](#joystickinstancepower) - [joystickInstance.setPlayer(index)](#joystickinstancesetplayerindex) - [joystickInstance.resetPlayer()](#joystickinstanceresetplayer) - [joystickInstance.hasLed](#joystickinstancehasled) - [joystickInstance.setLed(red, green, blue)](#joystickinstancesetledred-green-blue) - [joystickInstance.hasRumble](#joystickinstancehasrumble) - [joystickInstance.rumble([low[, high[, duration]]])](#joystickinstancerumblelow-high-duration) - [joystickInstance.stopRumble()](#joystickinstancestoprumble) - [joystickInstance.hasRumbleTriggers](#joystickinstancehasrumbletriggers) - [joystickInstance.rumbleTriggers([left[, right[, duration]]])](#joystickinstancerumbletriggersleft-right-duration) - [joystickInstance.stopRumbleTriggers()](#joystickinstancestoprumbletriggers) - [joystickInstance.closed](#joystickinstanceclosed) - [joystickInstance.close()](#joystickinstanceclose) - [sdl.controller](#sdlcontroller) - [Event: 'deviceAdd'](#controller-event-deviceadd) - [Event: 'deviceRemove'](#controller-event-deviceremove) - [sdl.controller.addMappings(mappings)](#sdlcontrolleraddmappingsmappings) - [sdl.controller.devices](#sdlcontrollerdevices) - [sdl.controller.openDevice(device)](#sdlcontrolleropendevicedevice) - [class ControllerInstance](#class-controllerinstance) - [Event: 'axisMotion'](#controller-instance-event-axismotion) - [Event: 'buttonDown'](#controller-instance-event-buttondown) - [Event: 'buttonUp'](#controller-instance-event-buttonup) - [Event: 'powerUpdate'](#controller-instance-event-power-update) - [Event: 'steamHandleUpdate'](#event-steamhandleupdate) - [Event: 'remap'](#event-remap) - [Event: 'close'](#controller-instance-event-close) - [controllerInstance.device](#controllerinstancedevice) - [controllerInstance.firmwareVersion](#controllerinstancefirmwareversion) - [controllerInstance.serialNumber](#controllerinstanceserialnumber) - [controllerInstance.steamHandle](#controllerinstancesteamhandle) - [controllerInstance.axes](#controllerinstanceaxes) - [controllerInstance.buttons](#controllerinstancebuttons) - [controllerInstance.power](#controllerinstancepower) - [controllerInstance.setPlayer(index)](#controllerinstancesetplayerindex) - [controllerInstance.resetPlayer()](#controllerinstanceresetplayer) - [controllerInstance.hasLed](#controllerinstancehasled) - [controllerInstance.setLed(red, green, blue)](#controllerinstancesetledred-green-blue) - [controllerInstance.hasRumble](#controllerinstancehasrumble) - [controllerInstance.rumble([low[, high[, duration]]])](#controllerinstancerumblelow-high-duration) - [controllerInstance.stopRumble()](#controllerinstancestoprumble) - [controllerInstance.hasRumbleTriggers](#controllerinstancehasrumbletriggers) - [controllerInstance.rumbleTriggers([left[, right[, duration]]])](#controllerinstancerumbletriggersleft-right-duration) - [controllerInstance.stopRumbleTriggers()](#controllerinstancestoprumbletriggers) - [controllerInstance.closed](#controllerinstanceclosed) - [controllerInstance.close()](#controllerinstanceclose) - [sdl.sensor](#sdlsensor) - [sdl.sensor.STANDARD_GRAVITY](#sdlsensorstandard_gravity) - [sdl.sensor.devices](#sdlsensordevices) - [sdl.sensor.openDevice(device)](#sdlsensoropendevicedevice) - [class SensorInstance](#class-sensorinstance) - [Event: 'update'](#sensor-instance-event-update) - [sensorInstance.device](#sensorinstancedevice) - [sensorInstance.data](#sensorinstancedata) - [sensorInstance.closed](#sensorinstanceclosed) - [sensorInstance.close()](#sensorinstanceclose) - [sdl.audio](#sdlaudio) - [Audio data](#audio-data) - [Sample formats](#sample-formats) - [Event: 'deviceAdd'](#audio-event-deviceadd) - [Event: 'deviceRemove'](#audio-event-deviceremove) - [sdl.audio.bytesPerSample(format)](#sdlaudiobytespersampleformat) - [sdl.audio.minSampleValue(format)](#sdlaudiominsamplevalueformat) - [sdl.audio.maxSampleValue(format)](#sdlaudiomaxsamplevalueformat) - [sdl.audio.zeroSampleValue(format)](#sdlaudiozerosamplevalueformat) - [sdl.audio.readSample(format, buffer[, offset])](#sdlaudioreadsampleformat-buffer-offset) - [sdl.audio.writeSample(format, buffer, value[, offset])](#sdlaudiowritesampleformat-buffer-value-offset) - [sdl.audio.devices](#sdlaudiodevices) - [sdl.audio.openDevice(device[, options])](#sdlaudioopendevicedevice-options) - [class AudioInstance](#class-audioinstance) - [Event: 'close'](#audio-instance-event-close) - [audioInstance.id](#audioinstanceid) - [audioInstance.channels](#audioinstancechannels) - [audioInstance.frequency](#audioinstancefrequency) - [audioInstance.format](#audioinstanceformat) - [audioInstance.bytesPerSample](#audioinstancebytespersample) - [audioInstance.minSampleValue](#audioinstanceminsamplevalue) - [audioInstance.maxSampleValue](#audioinstancemaxsamplevalue) - [audioInstance.zeroSampleValue](#audioinstancezerosamplevalue) - [audioInstance.readSample(buffer[, offset])](#audioinstancereadsamplebuffer-offset) - [audioInstance.writeSample(buffer, value[, offset])](#audioinstancewritesamplebuffer-value-offset) - [audioInstance.buffered](#audioinstancebuffered) - [audioInstance.playing](#audioinstanceplaying) - [audioInstance.play([play])](#audioinstanceplayplay) - [audioInstance.pause()](#audioinstancepause) - [audioInstance.queued](#audioinstancequeued) - [audioInstance.clearQueue()](#audioinstanceclearqueue) - [audioInstance.closed](#audioinstanceclosed) - [audioInstance.close()](#audioinstanceclose) - [class AudioPlaybackInstance extends AudioInstance](#class-audioplaybackinstance-extends-audioinstance) - [playbackInstance.enqueue(buffer[, bytes])](#playbackinstanceenqueuebuffer-bytes) - [class AudioRecordingInstance extends AudioInstance](#class-audiorecordinginstance-extends-audioinstance) - [recordingInstance.dequeue(buffer[, bytes])](#recordinginstancedequeuebuffer-bytes) - [sdl.clipboard](#sdlclipboard) - [Event: 'update'](#clipboard-event-update) - [sdl.clipboard.text](#sdlclipboardtext) - [sdl.clipboard.setText(text)](#sdlclipboardsettexttext) - [sdl.power](#sdlpower) - [sdl.power.info](#sdlpowerinfo) - [helpers](#helpers) ## sdl ### sdl.info - `<object>` - `version: <object>` - `compile: <object>` The version of the SDL library that this package was compiled against. - `major, minor, patch: <Semver>` The components of the version. - `runtime: <object>` The version of the SDL library that was found and loaded at runtime. - `major, minor, patch: <Semver>` The components of the version. - `platform: <string>` The name of the platform we are running on. Possible values are: `'Linux'`, `'Windows'`, and `'Mac OS X'`. - `drivers: <object>` - `video: <object>` - `all: <string>[]` A list of all video drivers. - `current: <string>|<null>` The video driver that is currently selected. - `audio: <object>` - `all: <string>[]` A list of all audio drivers. - `current: <string>|<null>` The audio driver that is currently selected. - `initialized: <object>` - `video: <boolean>`: Is `true` if the video subsystem was successfully initialized, or `false` otherwise. - `audio: <boolean>`: Is `true` if the audio subsystem was successfully initialized, or `false` otherwise. - `joystick: <boolean>`: Is `true` if the joystick subsystem was successfully initialized, or `false` otherwise. - `controller: <boolean>`: Is `true` if the controller subsystem was successfully initialized, or `false` otherwise. - `haptic: <boolean>`: Is `true` if the haptic subsystem was successfully initialized, or `false` otherwise. - `sensor: <boolean>`: Is `true` if the sensor subsystem was successfully initialized, or `false` otherwise. The `sdl.info` object is filled with information produced during the initialization of SDL. All the values remain constant throughout the execution of the program. To initialize SDL with video/audio drivers other than the default ones, set the appropriate [environment variables](https://wiki.libsdl.org/FAQUsingSDL) to the desired value. Note that the `current` video or audio driver may be `null`. This usually happens on systems that don't have any compatible devices, such as on a CI pipeline. Sample data for Ubuntu: ```js { version: { compile: { major: 2, minor: 0, patch: 10 }, runtime: { major: 2, minor: 0, patch: 10 }, }, platform: 'Linux', drivers: { video: { all: [ 'x11', 'wayland', 'dummy' ], current: 'x11', }, audio: { all: [ 'pulseaudio', 'alsa', 'sndio', 'dsp', 'disk', 'dummy' ], current: 'pulseaudio', }, }, initialized: { video: true, audio: true, joystick: true, controller: true, haptic: true, sensor: true, }, } ``` ## sdl.video ### Image data There are 3 places in the API where you must provide an image to the library: - [`window.render()`](#windowrenderwidth-height-stride-format-buffer-options) - [`window.setIcon()`](#windowseticonwidth-height-stride-format-buffer) - [`mouse.setCursorImage()`](#sdlmousesetcursorimagewidth-height-stride-format-buffer-x-y) All three of these functions accept the image as a series of arguments: - `width: <number>` The width of the image in pixels. - `height: <number>` The height of the image in pixels. - `stride: <number>` How many bytes each row of the image takes up in the buffer. Usually equal to `width * bytesPerPixel`, but may be larger if the rows of the buffer are padded to always be some multiple of bytes. - `format: `[`<PixelFormat>`](#pixel-formats) The binary representation of the data in the buffer. - `buffer: <Buffer>` Holds the actual pixel data for the image, in the format and layout specified by all the above arguments. So, for example, to fill the window with a red+green gradient you could do: ```js const { pixelWidth: width, pixelHeight: height } = window const stride = width * 4 const buffer = Buffer.alloc(stride * height) let offset = 0 for (let i = 0; i < height; i++) { for (let j = 0; j < width; j++) { buffer[offset++] = Math.floor(256 * i / height) // R buffer[offset++] = Math.floor(256 * j / width) // G buffer[offset++] = 0 // B buffer[offset++] = 255 // A } } window.render(width, height, stride, 'rgba32', buffer) ``` ### High-DPI On a high-dpi display, windows have more pixels than their `width` and `height` would indicate. On such systems `width` and `height` (and all other measurements such as `x` and `y`) are measured in "points" instead of pixels. Points are an abstract unit of measurement and don't necessarily correspond to pixels. If you need to work with pixels, you can use the window's `pixelWidth` and `pixelHeight` properties. You usually need these values when creating a "surface" that will be displayed on the window, such as a Buffer, Canvas, or 3D rendering viewport. I recommend that in these cases you always use `pixelWidth` and `pixelHeight`, since you don't know beforehand if your program will be running on a high-dpi system or not. ### Pixel formats String values used to represent how the pixels of an image are stored in a Buffer. | Value | Corresponding `SDL_PixelFormatEnum` | Comment | | --- | --- | --- | | `'rgb332'` | `SDL_PIXELFORMAT_RGB332` | | | `'rgb444'` | `SDL_PIXELFORMAT_RGB444` | | | `'rgb555'` | `SDL_PIXELFORMAT_RGB555` | | | `'bgr555'` | `SDL_PIXELFORMAT_BGR555` | | | `'argb4444'` | `SDL_PIXELFORMAT_ARGB4444` | | | `'rgba4444'` | `SDL_PIXELFORMAT_RGBA4444` | | | `'abgr4444'` | `SDL_PIXELFORMAT_ABGR4444` | | | `'bgra4444'` | `SDL_PIXELFORMAT_BGRA4444` | | | `'argb1555'` | `SDL_PIXELFORMAT_ARGB1555` | | | `'rgba5551'` | `SDL_PIXELFORMAT_RGBA5551` | | | `'abgr1555'` | `SDL_PIXELFORMAT_ABGR1555` | | | `'bgra5551'` | `SDL_PIXELFORMAT_BGRA5551` | | | `'rgb565'` | `SDL_PIXELFORMAT_RGB565` | | | `'bgr565'` | `SDL_PIXELFORMAT_BGR565` | | | `'rgb24'` | `SDL_PIXELFORMAT_RGB24` | | | `'bgr24'` | `SDL_PIXELFORMAT_BGR24` | | | `'rgb888'` | `SDL_PIXELFORMAT_RGB888` | | | `'rgbx8888'` | `SDL_PIXELFORMAT_RGBX8888` | | | `'bgr888'` | `SDL_PIXELFORMAT_BGR888` | | | `'bgrx8888'` | `SDL_PIXELFORMAT_BGRX8888` | | | `'argb8888'` | `SDL_PIXELFORMAT_ARGB8888` | | | `'rgba8888'` | `SDL_PIXELFORMAT_RGBA8888` | | | `'abgr8888'` | `SDL_PIXELFORMAT_ABGR8888` | | | `'bgra8888'` | `SDL_PIXELFORMAT_BGRA8888` | | | `'argb2101010'` | `SDL_PIXELFORMAT_ARGB2101010` | | | `'rgba32'` | `SDL_PIXELFORMAT_RGBA32` | alias for `'rgba8888'` on big endian machines and for `'abgr8888'` on little endian machines | | `'argb32'` | `SDL_PIXELFORMAT_ARGB32` | alias for `'argb8888'` on big endian machines and for `'bgra8888'` on little endian machines | | `'bgra32'` | `SDL_PIXELFORMAT_BGRA32` | alias for `'bgra8888'` on big endian machines and for `'argb8888'` on little endian machines | | `'abgr32'` | `SDL_PIXELFORMAT_ABGR32` | alias for `'abgr8888'` on big endian machines and for `'rgba8888'` on little endian machines | | `'yv12'` | `SDL_PIXELFORMAT_YV12` | planar mode: Y + V + U (3 planes) | | `'iyuv'` | `SDL_PIXELFORMAT_IYUV` | planar mode: Y + U + V (3 planes) | | `'yuy2'` | `SDL_PIXELFORMAT_YUY2` | packed mode: Y0+U0+Y1+V0 (1 plane) | | `'uyvy'` | `SDL_PIXELFORMAT_UYVY` | packed mode: U0+Y0+V0+Y1 (1 plane) | | `'yvyu'` | `SDL_PIXELFORMAT_YVYU` | packed mode: Y0+V0+Y1+U0 (1 plane) | | `'nv12'` | `SDL_PIXELFORMAT_NV12` | planar mode: Y + U/V interleaved (2 planes) | | `'nv21'` | `SDL_PIXELFORMAT_NV21` | planar mode: Y + V/U interleaved (2 planes) | ### Event: 'displayAdd' - `device: <object>`: An object from [`sdl.video.displays`](#sdlvideodisplays) indicating the display that caused the event. Fired when a display is added to the system. Check [`sdl.video.displays`](#sdlvideodisplays) to get the new list of displays. ### Event: 'displayRemove' - `device: <object>`: An object from [`sdl.video.displays`](#sdlvideodisplays) indicating the display that caused the event. Fired when a display is removed from the system. Check [`sdl.video.displays`](#sdlvideodisplays) to get the new list of displays. ### Event: 'displayOrient' - `device: <object>`: An object from [`sdl.video.displays`](#sdlvideodisplays) indicating the display that caused the event. - `orientation: <string>`: The display's new orientation. Fired when a display changes orientation. ### Event: 'displayMove' - `device: <object>`: An object from [`sdl.video.displays`](#sdlvideodisplays) indicating the display that caused the event. Fired when a display changes position. ### sdl.video.displays - `<object>[]` - `name: <string>|<null>` The name of the display, or `null` if it can't be determined. - `format: `[`<PixelFormat>`](#pixel-formats) The pixel format of the display. - `frequency: <number>` The refresh rate of the display. - `geometry: <object>` The desktop region represented by the display. - `x, y, width, height: <Rect>` The position and size of the display's geometry. - `usable: <object>` Similar to `geometry`, but excludes areas taken up by the OS or window manager such as menus, docks, e.t.c. - `x, y, width, height: <Rect>` The position and size of the display's usable region. - `dpi: <object>|<null>` Return pixel density for the display in dots/pixels-per-inch units. Might be `null` on some devices if DPI info can't be retrieved. - `horizontal: <number>` The horizontal density. - `vertical: <number>` The vertical density. - `diagonal: <number>` The diagonal density. - `orientation: <string>|<null>` The orientation of the display. A list of all detected displays. Possible values for `orientation` are `null` if it is unknown, or one of: | Value | Corresponding `SDL_DisplayOrientation` | | --- | --- | | `'landscape'` | `SDL_ORIENTATION_LANDSCAPE` | | `'landscapeFlipped'` | `SDL_ORIENTATION_LANDSCAPE_FLIPPED` | | `'portrait'` | `SDL_ORIENTATION_PORTRAIT` | | `'portraitFlipped'` | `SDL_ORIENTATION_PORTRAIT_FLIPPED` | Sample output for two side-to-side monitors is below. Notice how the geometries don't overlap: ```js [ { name: '0', format: 'rgb888', frequency: 60, geometry: { x: 0, y: 0, width: 1920, height: 1080 }, usable: { x: 0, y: 27, width: 1920, height: 1053 }, dpi: { horizontal: 141.76, vertical: 142.13, diagonal: 141.85 }, }, { name: '1', format: 'rgb888', frequency: 60, geometry: { x: 1920, y: 0, width: 1920, height: 1080 }, usable: { x: 1920, y: 27, width: 1920, height: 1053 }, dpi: { horizontal: 141.76, vertical: 142.13, diagonal: 141.85 }, }, ] ``` ### sdl.video.windows - [`<Window>`](#class-window)`[]` A list of all open windows. ### sdl.video.focused - [`<Window>`](#class-window)`|<null>` The window that has the current keyboard focus, or `null` if no window has the keyboard focus. ### sdl.video.hovered - [`<Window>`](#class-window)`|<null>` The window that the mouse is hovered over, or `null` if the mouse is not over a window. ### sdl.video.createWindow([options]) - `options: <object>` - `title: <string>` Appears in the window's title bar. Default: `''` - `display: <number>` An object from `sdl.video.displays` to specify in which display the window should appear (if you have multiple displays). Default: `sdl.video.displays[0]` - `x: <number>` The x position in which the window should appear relative to the screen, or `null` for centered. Default: `null` - `y: <number>` The y position in which the window should appear relative to the screen, or `null` for centered. Default: `null` - `width: <number>` The width of the window. Default: `640` - `height: <number>` The height of the window. Default: `480` - `visible: <boolean>` Set to `false` to create a hidden window that will only be shown when you call [`window.show()`](#windowshowshow). Default: `true` - `fullscreen: <boolean>` Set to `true` to create the window in fullscreen mode. Default: `false` - `resizable: <boolean>` Set to `true` to allow resizing the window by dragging its borders. Default: `false` - `borderless: <boolean>` Set to `true` to completely hide the window's borders and title bar. Default: `false` - `alwaysOnTop: <boolean>` Set to `true` to always show the window above others. Default: `false` - `accelerated: <boolean>` Set to `false` to disable hardware accelerated rendering. Default: `true` - `vsync: <boolean>` Set to `false` to disable frame rate synchronization. Default: `true` - `opengl: <boolean>` Set to `true` to create an OpenGL-compatible window (for use with [@kmamal/gl](https://github.com/kmamal/headless-gl#readme)). Default: `false` - `webgpu: <boolean>` Set to `true` to create an WebGPU-compatible window (for use with [@kmamal/gpu](https://github.com/kmamal/gpu#readme)). Default: `false` - `skipTaskbar: <boolean>` X11 only. Set to `true` to not add the window to the taskbar. Default: `false` - `popupMenu: <boolean>` X11 only. Set to `true` to treat the window like a popup menu. Default: `false` - `tooltip: <boolean>` X11 only. Set to `true` to treat the window like a tooltip. Default: `false` - `utility: <boolean>` X11 only. Set to `true` to treat the window like a utility window. Default: `false` - Returns: [`<Window>`](#class-window) an object representing the new window. Creates a new window. The following restrictions apply: - The `display` option is mutually exclusive with the `x` and `y` options. - The `resizable` and `borderless` options are mutually exclusive. - The `opengl` and `webgpu` options are mutually exclusive. - The `vsync` option only applies to windows that are also `accelerated`. - The `accelerated` and `vsync` options have no effect if either `opengl` or `webgpu` is also specified. If you set the `opengl` or `webgpu` options, then you must use OpenGL/WebGPU calls to render to the window. Calls to [`render()`](#windowrenderwidth-height-stride-format-buffer-options) will fail. ## class Window The `Window` class is not directly exposed by the API so you can't (and shouldn't) use it with the `new` operator. Instead, objects returned by [`sdl.video.createWindow()`](#sdlvideocreatewindowoptions) are of type `Window`. ### Event: 'show' Fired when the window becomes visible. ### Event: 'hide' Fired when the window becomes hidden. ### Event: 'expose' Fired when the window becomes exposed and should be redrawn. ### Event: 'minimize' Fired when the window becomes minimized. ### Event: 'maximize' Fired when the window becomes maximized. ### Event: 'restore' Fired when the window gets restored. ### Event: 'move' - `x: <number>` The window's new x position, relative to the screen. - `y: <number>` The window's new y position, relative to the screen. Fired when the window changes position. ### Event: 'resize' - `width: <number>` The window's new width. - `height: <number>` The window's new height. - `pixelWidth: <number>` The window's new width in pixels. See [high-dpi](#high-dpi). - `pixelHeight: <number>` The window's new height in pixels. See [high-dpi](#high-dpi). Fired when the window changes size. ### Event: 'displayChange' - `display: <object>` An object from [`sdl.video.displays`](#sdlvideodisplays) indicating the window's new display. Fired when the window moves from one display to another. ### Event: 'focus' Fired when the window gains the keyboard focus. ### Event: 'blur' Fired when the window loses the keyboard focus. ### Event: 'hover' Fired when the mouse enters the window. ### Event: 'leave' Fired when the mouse leaves the window. ### Event: 'beforeClose' - `prevent: <function (void) => void>` Call this function to prevent the window from closing. Fired to indicate that the user requested that the window should close (usually by clicking the "x" button). If you need to display any confirmation dialogs, call `event.prevent()` and afterwards handle destruction manually. If `prevent` is not called, then the `beforeClose` event will be followed by a [`'close'`](#event-close) event. ### Event: 'close' Indicates that the window is about to be destroyed. Handle any cleanup here. ### Event: 'keyDown' - `scancode: `[`<Scancode>`](#enum-scancode) The scancode of the key that caused the event. - `key: `[`<Key>`](#virtual-keys)`|<null>` The virtual key that caused the event, or `null` if the physical key does not correspond to any virtual key. - `repeat: <boolean>` Is `true` if the event was generated by holding down a key for a long time. - `shift: <boolean>` Is `true` if the Shift key was pressed when the event was generated. - `ctrl: <boolean>` Is `true` if the Ctrl key was pressed when the event was generated. - `alt: <boolean>` Is `true` if the Alt key was pressed when the event was generated. - `super: <boolean>` Is `true` if the "Windows" key was pressed when the event was generated. - `altgr: <boolean>` Is `true` if the AltGr key was pressed when the event was generated. - `capslock: <boolean>` Is `true` if CapsLock was active when the event was generated. - `numlock: <boolean>` Is `true` if NumLock was active when the event was generated. Fired when a key is pressed, and will also be fired repeatedly afterwards if the key is held down. ### Event: 'keyUp' - `scancode: `[`<Scancode>`](#enum-scancode) The scancode of the key that caused the event. - `key: `[`<Key>`](#virtual-keys)`|<null>` The virtual key that caused the event, or `null` if the physical key does not correspond to any virtual key. - `shift: <boolean>` Is `true` if the Shift key was pressed when the event was generated. - `ctrl: <boolean>` Is `true` if the Ctrl key was pressed when the event was generated. - `alt: <boolean>` Is `true` if the Alt key was pressed when the event was generated. - `super: <boolean>` Is `true` if the "Windows" key was pressed when the event was generated. - `altgr: <boolean>` Is `true` if the AltGr key was pressed when the event was generated. - `capslock: <boolean>` Is `true` if CapsLock was active when the event was generated. - `numlock: <boolean>` Is `true` if NumLock was active when the event was generated. Fired when a key is released. ### Event: 'textInput' - `text: <string>` The unicode representation of the character that was entered. Fired when text is entered via the keyboard. ### Event: 'mouseButtonDown' - `x: <number>` The mouse's x position when the event happened, relative to the window. - `y: <number>` The mouse's y position when the event happened, relative to the window. - `touch: <boolean>` Is `true` if the event was caused by a touch event. - `button: `[`<sdl.mouse.BUTTON>`](#enum-button) The button that was pressed. Fired when a mouse button is pressed. ### Event: 'mouseButtonUp' - `x: <number>` The mouse's x position when the event happened, relative to the window. - `y: <number>` The mouse's y position when the event happened, relative to the window. - `touch: <boolean>` Is `true` if the event was caused by a touch event. - `button: `[`<sdl.mouse.BUTTON>`](#enum-button) The button that was released. Fired when a mouse button is released. ### Event: 'mouseMove' - `x: <number>` The mouse's x position when the event happened, relative to the window. - `y: <number>` The mouse's y position when the event happened, relative to the window. - `touch: <boolean>` Is `true` if the event was caused by a touch event. Fired when the mouse moves. ### Event: 'mouseWheel' - `x: <number>` The mouse's x position when the event happened, relative to the window. - `y: <number>` The mouse's y position when the event happened, relative to the window. - `touch: <boolean>` Is `true` if the event was caused by a touch event. - `dx: <number>` The wheel's x movement, relative to its last position. - `dy: <number>` The wheel's y movement, relative to its last position. - `flipped: <boolean>` Is `true` if the underlying platform reverses the mouse wheel's scroll direction. Multiply `dx` and `dy` by `-1` to get the correct values. Fired when the mouse wheel is scrolled. ### Event: 'fingerDown' - `device: <object>`: An object from [`sdl.touch.devices`](#sdltouchdevices) indicating the touch device that caused the event. - `fingerId: <number>` The id of the finger that coused the event. - `x: <number>` The finger's x position when the event happened, normalized in the range from `0` to `1`. - `y: <number>` The finger's y position when the event happened, normalized in the range from `0` to `1`. - `pressure: <number>` The finger's pressure when the event happened, normalized in the range from `0` to `1`. - `mouse: <boolean>` Is `true` if the event was caused by a mouse event. Fired when a finger is presed to the touch surface. ### Event: 'fingerUp' - `device: <object>`: An object from [`sdl.touch.devices`](#sdltouchdevices) indicating the touch device that caused the event. - `fingerId: <number>` The id of the finger that coused the event. - `x: <number>` The finger's x position when the event happened, normalized in the range from `0` to `1`. - `y: <number>` The finger's y position when the event happened, normalized in the range from `0` to `1`. - `pressure: <number>` The finger's pressure when the event happened, normalized in the range from `0` to `1`. - `mouse: <boolean>` Is `true` if the event was caused by a mouse event. Fired when a finger is lifted from the touch surface. ### Event: 'fingerMove' - `device: <object>`: An object from [`sdl.touch.devices`](#sdltouchdevices) indicating the touch device that caused the event. - `fingerId: <number>` The id of the finger that coused the event. - `x: <number>` The finger's x position when the event happened, normalized in the range from `0` to `1`. - `y: <number>` The finger's y position when the event happened, normalized in the range from `0` to `1`. - `dx: <number>` The finger's x position when the event happened, normalized in the range from `-1` to `1`. - `dy: <number>` The finger's y position when the event happened, normalized in the range from `-1` to `1`. - `pressure: <number>` The finger's pressure when the event happened, normalized in the range from `0` to `1`. - `mouse: <boolean>` Is `true` if the event was caused by a mouse event. Fired when a finger moves on the touch surface. ### Event: 'dropBegin' When you drop a set of items onto a window, first the [`'dropBegin'`](#event-dropbegin) event is fired, then a number of [`'dropText'`](#event-droptext) and/or [`'dropFile'`](#event-dropfile) events are fired, corresponding to the contents of the drop, then finally the [`'dropComplete'`](#event-dropcomplete) event is fired. ### Event: 'dropText' - `text: <string>`: The text that was dropped onto the window. Fired when one of the drops is a text item. ### Event: 'dropFile' - `file: <string>`: The path to the file that was dropped onto the window. Fired when one of the drops is a file. ### Event: 'dropComplete' Fired after a set of items has been dropped on a window. ### window.id - `<number>` A unique identifier for the window. ### window.title - `<string>` The text that appears in the window's title bar. ### window.setTitle(title) - `title: <string>`: The new title. Changes the text that appears in the window's title bar. ### window.x - `<number>` The window's x position, relative to the screen. ### window.y - `<number>` The window's y position, relative to the screen. ### window.setPosition(x, y) - `x: <number>`: The new x position, relative to the screen. - `y: <number>`: The new y position, relative to the screen. Moves the window to a new position on the screen. ### window.width - `<number>` The window's width. ### window.height - `<number>` The window's height. ### window.pixelWidth - `<number>` The window's width in pixels. Is larger than [`width`](#windowwidth) on [high-dpi](#high-dpi) displays. ### window.pixelHeight - `<number>` The window's height in pixels. Is larger than [`height`](#windowheight) on [high-dpi](#high-dpi) displays. ### window.setSize(width, height) - `width: <number>`: The new width. - `height: <number>`: The new height. Changes the size of the window. ### window.setSizeInPixels(pixelWidth, pixelHeight) - `pixelWidth: <number>`: The new width in pixels. - `pixelHeight: <number>`: The new height in pixels. Changes the size of the window. This function only behaves differently from [`window.setSize()`](#windowsetsizewidth-height) for [high-dpi](#high-dpi) displays. ### window.display - `<object>` An object from [`sdl.video.displays`](#sdlvideodisplays) indicating the display the window belongs to. If the window spans multiple displays, then the display that contains the center of the window is returned. ### window.visible - `<boolean>` Is `true` if the window is visible. ### window.show([show]) - `show: <boolean>` Set to `true` to make the window visible, `false` to hide it. Default: `true` Shows or hides the window. ### window.hide() Equivalent to [`window.show(false)`](#windowshowshow). ### window.fullscreen - `<boolean>` Is `true` if the window is fullscreen. A fullscreen window is displayed over the entire screen. ### window.setFullscreen(fullscreen) - `fullscreen: <boolean>` The new value of the property. Changes the window's fullscreen property. ### window.resizable - `<boolean>` Is `true` if the window is resizable. A resizable window can be resized by dragging its borders. ### window.setResizable(resizable) - `resizable: <boolean>` The new value of the property. Changes the window's resizable property. ### window.borderless - `<boolean>` Is `true` if the window is borderless. A borderless window has no borders or title bar. ### window.setBorderless(borderless) - `borderless: <boolean>` The new value of the property. Changes the window's borderless property. ### window.alwaysOnTop - `<boolean>` Is `true` if the window was created with `alwaysOnTop: true`. Such a window is always be shown above other windows. ### window.accelerated - `<boolean>` Is `true` if the window is using hardware accelerated rendering. ### window.setAccelerated(accelerated) - `accelerated: <boolean>` The new value of the property. Changes the window's accelerated property. If you have set the `opengl` or `webgpu` options, then calls to this function will fail. ### window.vsync - `<boolean>` Is `true` if the window is using vsync. Vsync synchronizes the window's frame rate with the display's refresh rate to prevent tearing. Note that vsync can only be set to `true` if [`accelerated`](#windowaccelerated) is also `true`. ### window.setVsync(vsync) - `vsync: <boolean>` The new value of the property. Changes the window's vsync property. If you have set the `opengl` or `webgpu` options, then calls to this function will fail. ### window.opengl - `<boolean>` Is `true` if the window was created in OpenGl mode. In OpenGL mode, you must use OpenGL calls to render to the window. Calls to [`render()`](#windowrenderwidth-height-stride-format-buffer-options) will fail. ### window.webgpu - `<boolean>` Is `true` if the window was created in WebGPU mode. In WebGPU mode, you must use WebGPU calls to render to the window. Calls to [`render()`](#windowrenderwidth-height-stride-format-buffer-options) will fail. ### window.native - `<object>` - `handle : <Buffer>|<null>` The platform-specific handle of the window, or `null` if it can't be determined. The native type of `handle` is HWND on Windows, NSView* on macOS, and Window (unsigned long) on Linux. It should work exactly like the [`win.getNativeWindowHandle()`](https://www.electronjs.org/docs/latest/api/browser-window#wingetnativewindowhandle) electron method. The `window.native` object might also sometimes include extra fields other than the ones documented here. Please ignore and do not use these. They are used internally for passing to [@kmamal/gl](https://github.com/kmamal/headless-gl#readme) or [@kmamal/gpu](https://github.com/kmamal/gpu#readme) and can change at any time. ### window.maximized - `<boolean>` Is `true` if the window is maximized. ### window.maximize() Maximizes the window. ### window.minimized - `<boolean>` Is `true` if the window is minimized. ### window.minimize() Minimizes the window. ### window.restore() Restores the window so it is neither minimized nor maximized. ### window.focused - `<boolean>` Is `true` if the window has keyboard input. ### window.focus() Gives the window the keyboard focus. ### window.hovered - `<boolean>` Is `true` if the mouse is over the window. ### window.skipTaskbar - `<boolean>` X11 only. Is `true` if the window was created with `skipTaskbar: true`. Such a window is not added to the taskbar. ### window.popupMenu - `<boolean>` X11 only. Is `true` if the window was created with `popupMenu: true`. Such a window is always treated as a popup menu. ### window.tooltip - `<boolean>` X11 only. Is `true` if the window was created with `tooltip: true`. Such a window is always treated as a tooltip. ### window.utility - `<boolean>` X11 only. Is `true` if the window was created with `utility: true`. Such a window is always treated as a utility window. ### window.render(width, height, stride, format, buffer[, options]) - `width, height, stride, format, buffer: `[`<Image>`](#image-data) The image to display on the window. - `options: <object>` - `scaling: <string>` How to scale the image to match the window size. Default: `'nearest'` - `dstRect: <object>` Where exactly on the window to draw the image. Default: whole window. - `x, y, width, he