siwin

Simple window maker.

Pure Nim score 15/100 · tests present · no docs generated

Summary

Latest Version Unknown
License MIT
CI Status Failing
Downloads 0
Last Indexed 2026-07-21 05:24

Tags

Installation

nimble install siwin
choosenim install siwin
git clone https://github.com/levovix0/siwin

OS Compatibility

Platform Linux macOS Windows FreeBSD OpenBSD NetBSD Android iOS WASM Embedded
siwin - - - - - -

Source

Repository https://github.com/levovix0/siwin
Homepage https://github.com/levovix0/siwin
Registry Source nimble_official

README

Siwin

siwin

Cross-platform window creation and event handling library.

Can be used as an alternative to GLFW/GLUT/windy

Language Code size Latest version

Features

  • works with: OpenGL, Vulkan, Metal (on MacOS), software rendering
  • works on: Linux(X11 and Wayland), Windows, MacOS
  • handles events from: mouse, keyboard
  • and also supports: clipboard, offscreen rendering, interactive move/resize, etc.

Examples

simple window

import siwin, opengl

let win = newOpenglWindow()
opengl.loadExtensions()  # load opengl functions

win.eventsHandler.onRender = proc(e: RenderEvent) =
  glClearColor(0.1, 0.1, 0.1, 1)
  glClear(GlColorBufferBit or GlDepthBufferBit)

run win

software-rendering window

import siwin, vmath

const color = [32'u8, 32, 32, 255]

run newSoftwareRenderingWindow(), WindowEventsHandler(
  onRender: proc(e: RenderEvent) =
    let pixelBuffer = e.window.pixelBuffer

    for y in 0..<pixelBuffer.size.y:
      for x in 0..<pixelBuffer.size.x:
        cast[ptr UncheckedArray[array[4, uint8]]](pixelBuffer.data)[y * pixelBuffer.size.x + x] = color

    convertPixelsInplace(pixelBuffer.data, pixelBuffer.size, PixelBufferFormat.bgrx_32bit, pixelBuffer.format)
  ,
  onKey: proc(e: KeyEvent) =
    if (not e.pressed) and e.key == Key.escape:
      close e.window
)

OpenGL

import siwin, opengl, vmath

let siwinGlobals = newSiwinGlobals(
  preferedPlatform = (when defined(linux): x11 else: defaultPreferedPlatform)
  # note: glBegin and other non- OpenGL ES functions don't work on Wayland,
  #       so we should set preferedPlatform to x11 on linux when using regular OpenGL.
  #       see tests/t_opengl_es.nim for more complex, wayland-compatible opengl example
)

var window = siwinGlobals.newOpenglWindow(
  title="OpenGL example",
)
loadExtensions()  # init opengl

run window, WindowEventsHandler(
  onResize: proc(e: ResizeEvent) =
    glViewport 0, 0, e.size.x.GLsizei, e.size.y.GLsizei
    glMatrixMode GlProjection
    glLoadIdentity()
    glOrtho -30, 30, -30, 30, -30, 30
    glMatrixMode(GlModelView)
  ,
  onRender: proc(e: RenderEvent) =
    glClearColor 0.3, 0.3, 0.3, 1
    glClear GlColorBufferBit or GlDepthBufferBit

    glShadeModel GlSmooth

    glLoadIdentity()
    glTranslatef -15, -15, 0

    glBegin GlTriangles
    glColor3f 1, 0, 0
    glVertex2f 0, 0
    glColor3f 0, 1, 0
    glVertex2f 30, 0
    glColor3f 0, 0, 1
    glVertex2f 0, 30
    glEnd()
)

note: call redraw(window) every time you want window.render to be called. siwin will automatically call window.render only when window resizes.
note: opengl 1.x and 2.x functions (like glBegin), is not supported on Wayland, due to Wayland only beeng able to initialize with EGL

Vulkan

see t_vulkan.nim

import siwin, nimgl/vulkan, sequtils

doassert vkInit()

let exts = getRequiredVulkanExtensions()
var cexts = exts.mapit(it[0].addr)

var appInfo = newVkApplicationInfo(
  pApplicationName = "siwin Vulkan example",
  applicationVersion = vkMakeVersion(1, 0, 0),
  pEngineName = "No Engine",
  engineVersion = vkMakeVersion(1, 0, 0),
  apiVersion = vkApiVersion1_1
)

var instanceCreateInfo = newVkInstanceCreateInfo(
  pApplicationInfo = appInfo.addr,
  enabledExtensionCount = exts.len,
  ppEnabledExtensionNames = cast[cstringArray](cexts[0].addr),
  enabledLayerCount = 0,
  ppEnabledLayerNames = nil,
)

var instance: VkInstance
doassert vkCreateInstance(instanceCreateInfo.addr, nil, result.addr) == VKSuccess

let siwinGlobals = newSiwinGlobals()

let window = siwinGlobals.newVulkanWindow(cast[pointer](instance), title="Vulkan example")
let surface = cast[VkSurfaceKHR](window.vulkanSurface)

# do other initialization using instance and surface...

run window, WindowEventsHandler(
  onRender: proc(e: RenderEvent) =
    ## do rendering...
  ,
  onClose: proc(e: CloseEvent) =
    ## uninitialize before surface destruction
)

# surface already destroyed, continue uninitializing...

pixie

note: very slow, but useful if opengl not needed and if window is used to just display one single image

import siwin, pixie

var image: Image

let siwinGlobals = newSiwinGlobals()

run siwinGlobals.newSoftwareRenderingWindow(title="pixie example"), WindowEventsHandler(
  onResize: proc(e: ResizeEvent) =
    if e.size.x * e.size.y <= 0: return
    image = newImage(e.size.x, e.size.y)
  ,
  onRender: proc(e: RenderEvent) =
    if e.window.size.x * e.window.size.y <= 0: return
    image.fill(rgba(255, 255, 255, 255))

    let ctx = image.newContext
    ctx.fillStyle = rgba(0, 255, 0, 255)

    let
      wh = vec2(250, 250)
      pos = vec2(image.width.float, image.height.float) / 2 - wh / 2

    ctx.fillRoundedRect(rect(pos, wh), 25.0)

    let pixelBuffer = e.window.pixelBuffer
    copyMem(pixelBuffer.data, image.data[0].addr, pixelBuffer.size.x * pixelBuffer.size.y * Color32bit.sizeof)
    convertPixelsInplace(pixelBuffer.data, pixelBuffer.size, PixelBufferFormat.rgbx_32bit, pixelBuffer.format)
  ,
  onKey: proc(e: KeyEvent) =
    if (not e.pressed) and e.key == Key.escape:
      close e.window
)

popup windows

This api adds popup windows. On Wayland these are required to do popups, but on other platforms these will just be frameless windows.

import siwin, vmath

let globals = newSiwinGlobals()
let parent = globals.newSoftwareRenderingWindow()

let placement = PopupPlacement(
  anchorRectPos: ivec2(100, 100),
  anchorRectSize: ivec2(120, 40),
  size: ivec2(320, 220),
  anchor: Edge.bottomLeft,
  gravity: Edge.topLeft,
  offset: ivec2(0, 8),
  constraintAdjustment: {PopupConstraintAdjustment.pcaSlideX, PopupConstraintAdjustment.pcaFlipY},
  reactive: true,
)

let popup = globals.newPopupWindow(parent, placement)

clipboard

let clipboard = window.clipboard

echo clipboard.text
clipboard.text = "some text"

offscreen rendering

note: this will create invisible window. ctx mustn't be discarded as its destructor will close the window.
If you have multiple contexts, use makeCurrent to select.

import siwin/offscreen, opengl

let siwinGlobals = newSiwinGlobals()

let ctx {.used.} = siwinGlobals.newOpenglContext()
loadExtensions()

# do any opengl computing

manual main cycle

import siwin

let siwinGlobals = newSiwinGlobals()
let window = siwinGlobals.newOenglWindow()
loadExtensions()

let eventsHandler = WindowEventsHandler(
  # ...
)

window.firstStep(eventsHandler, makeVisible=true)
while window.opened:
  window.step(eventsHandler)

running multiple windows

import siwin

let siwinGlobals = newSiwinGlobals()

let win1 = siwinGlobals.newOpenglWindow()
let win2 = siwinGlobals.newOpenglWindow()
loadExtensions()

let win1_eventsHandler = WindowEventsHandler(
  onResize: proc(e: ResizeEvent) =
    makeCurrent e.window
    #...
  ,
  onRender: proc(e: RenderEvent) =
    makeCurrent e.window
    #...
)
let win2_eventsHandler = WindowEventsHandler(
  onResize: proc(e: ResizeEvent) =
    makeCurrent e.window
    #...
  ,
  onRender: proc(e: RenderEvent) =
    makeCurrent e.window
    #...
)

runMultiple(
  (window: win1, eventsHandler: win1_eventsHandler, makeVisible: true),
  (window: win2, eventsHandler: win2_eventsHandler, makeVisible: true),
)

client-side decorations

import siwin

let siwinGlobals = newSiwinGlobals()

let window = siwinGlobals.newOpenglWindow(transparent=true, frameless=true)
loadExtensions()

run window, WindowEventsHandler(
  onMouseMove: proc(e: MouseMoveEvent) =
    if MouseButton.left in e.window.mouse.pressed:
      window.startInteractiveMove()
      # see also: startInteractiveResize
)

transparent backdrop blur

Backdrop blur uses the same API on macOS, Windows, Wayland, and X11. It is available on macOS, Windows 11 build 22621 or newer, and KDE compositors that advertise the KWin blur extension. Create the window with an alpha-capable surface so the effect can show through transparent pixels.

import siwin

let window = newSoftwareRenderingWindow(transparent = true, frameless = true)

if window.supports(wvcBackdropBlur):
  discard window.trySetBackdrop(initWindowBackdrop()) # whole-window blur

# Empty regions mean the whole window. Non-empty regions use Siwin window coordinates.
# Regional blur is currently available on macOS and KDE only.
# discard window.trySetBackdrop(initWindowBackdrop(regions))

window.clearBackdrop()

macOS also supports system materials such as wbmSidebar and wbmHud:

if window.supports(wvcBackdropMaterial):
  window.setBackdrop(initWindowBackdrop(wbmSidebar))

See backdrop_blur_demo.nim for a runnable cross-platform example.

all methods and events

see siwin/platforms/any/window

I want to get system handle of window and do some magic, but it is private?

import std/importutils
import siwin/platforms/x11/window
privateAccess WindowX11Obj
# ...
window.handle

Contributions

If you want to support this project, here is some tasks to do: * See issues * Any bugfixes is always accepted, just describe somewhere what you fixed * Refactoring (my code is bad, i know it) * if you doing very big refactoring, first create issue to ask is all your changes needed, and if it is, refactor * Documentation * Optimization * MacOS support * Android/IOS support * Web support * copy/paste images * Make cool site that adverts siwin

Just fork levovix0/siwin to your account, make changes and submit a pull request.
Or if it requires new repository to be created, create it and add an "change dependency" issue.