changeset 38:c3c963e7c1a6

did: tons of stuff, input, refactoring, fix some errors, some template improvment, sorry for super-commit
author Sam <sam@basx.dev>
date Wed, 18 Jan 2023 09:52:03 +0700
parents 6859bcfabc62
children 0cb294c5d2fd
files config.nims examples/alotof_triangles.nim examples/hello_cube.nim examples/hello_triangle.nim examples/input.nim examples/squares.nim src/zamikongine/engine.nim src/zamikongine/events.nim src/zamikongine/math/matrix.nim src/zamikongine/math/vector.nim src/zamikongine/mesh.nim src/zamikongine/platform/linux/symkey_map.nim src/zamikongine/platform/linux/vulkan.nim src/zamikongine/platform/linux/xlib.nim src/zamikongine/platform/windows/virtualkey_map.nim src/zamikongine/platform/windows/vulkan.nim src/zamikongine/platform/windows/win32.nim src/zamikongine/shader.nim src/zamikongine/vulkan.nim src/zamikongine/vulkan_helpers.nim src/zamikongine/window.nim
diffstat 21 files changed, 493 insertions(+), 131 deletions(-) [+]
line wrap: on
line diff
--- a/config.nims	Mon Jan 16 00:51:03 2023 +0700
+++ b/config.nims	Wed Jan 18 09:52:03 2023 +0700
@@ -120,5 +120,5 @@
   exec &"mv {dirname}/bin/glslangValidator.exe examples/"
   exec &"rm -rf {dirname}"
 
-if getCommand() in ["c", "compile", "r", "dump", "check"]:
+if getCommand() in ["c", "compile", "r", "dump", "check", "idetools"]:
   compilerFlags()
--- a/examples/alotof_triangles.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/examples/alotof_triangles.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -42,7 +42,6 @@
 
   for i in 1 .. 300:
     var randommesh = new Mesh[VertexDataA]
-    # TODO: create randomized position11 from baseTriangle with random transformation matrix
     let randomcolor1 = Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))])
     let transform1 = randomtransform()
     randommesh.vertexData = VertexDataA(
@@ -81,13 +80,6 @@
 
   const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]()
   const fragmentShader = generateFragmentShaderCode[VertexDataA]()
-  static:
-    echo "--------------"
-    for (i, line) in enumerate(vertexShader.splitLines()):
-      echo $(i + 1) & " " & line
-    echo "--------------"
-    echo fragmentShader
-    echo "--------------"
   var pipeline = setupPipeline[VertexDataA, float32, uint16](
     myengine,
     scene,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/hello_cube.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -0,0 +1,130 @@
+#
+#   TODO: Needs Depth-Buffer first!
+#
+#
+#
+#
+#
+#
+#
+#
+import std/times
+import std/strutils
+import std/enumerate
+
+import zamikongine/engine
+import zamikongine/math/vector
+import zamikongine/math/matrix
+import zamikongine/vertex
+import zamikongine/descriptor
+import zamikongine/mesh
+import zamikongine/thing
+import zamikongine/shader
+import zamikongine/buffer
+
+type
+  # define type of vertex
+  VertexDataA = object
+    position: PositionAttribute[Vec3[float32]]
+    color: ColorAttribute[Vec3[float32]]
+  Uniforms = object
+    model: Descriptor[Mat44[float32]]
+    view: Descriptor[Mat44[float32]]
+    projection: Descriptor[Mat44[float32]]
+
+var
+  pipeline: RenderPipeline[VertexDataA, Uniforms]
+  uniforms:Uniforms
+  t: float32
+
+
+proc globalUpdate(engine: var Engine, dt: float32) =
+  let ratio = float32(engine.vulkan.frameDimension.height) / float32(engine.vulkan.frameDimension.width)
+  t += dt
+  uniforms.model.value = translate3d(0'f32, 0'f32, 10'f32) * rotate3d(t, Yf32) #  * rotate3d(float32(PI), Yf32)
+
+  uniforms.view.value = Unit44f32
+  uniforms.projection.value = Mat44[float32](data:[
+    ratio, 0'f32, 0'f32, 0'f32,
+    0'f32, 1'f32, 0'f32, 0'f32,
+    0'f32, 0'f32, 1'f32, 0'f32,
+    0'f32, 0'f32, 0'f32, 1'f32,
+  ])
+  uniforms.projection.value = perspective(float32(PI / 4), float32(engine.vulkan.frameDimension.width) / float32(engine.vulkan.frameDimension.height), 0.1'f32, 100'f32)
+  for buffer in pipeline.uniformBuffers:
+    buffer.updateData(uniforms)
+  echo uniforms.projection.value
+
+const
+  TopLeftFront =     Vec3([ -0.5'f32, -0.5'f32, -0.5'f32])
+  TopRightFront =    Vec3([  0.5'f32, -0.5'f32, -0.5'f32])
+  BottomRightFront = Vec3([  0.5'f32,  0.5'f32, -0.5'f32])
+  BottomLeftFront =  Vec3([ -0.5'f32,  0.5'f32, -0.5'f32])
+  TopLeftBack =      Vec3([  0.5'f32, -0.5'f32,  0.5'f32])
+  TopRightBack =     Vec3([ -0.5'f32, -0.5'f32,  0.5'f32])
+  BottomRightBack =  Vec3([ -0.5'f32,  0.5'f32,  0.5'f32])
+  BottomLeftBack =   Vec3([  0.5'f32,  0.5'f32,  0.5'f32])
+const
+  cube_pos = @[
+    TopLeftFront, TopRightFront, BottomRightFront, BottomLeftFront, # front
+    TopLeftBack, TopRightBack, BottomRightBack, BottomLeftBack, # back
+    TopLeftBack, TopLeftFront, BottomLeftFront, BottomLeftBack, # left
+    TopRightBack, TopRightFront, BottomRightFront, BottomRightBack, # right
+    TopLeftBack, TopRightBack, TopRightFront, TopLeftFront, # top
+    BottomLeftFront, BottomRightFront, BottomRightBack, BottomLeftBack, # bottom
+  ]
+  cube_color = @[
+    Rf32, Rf32, Rf32, Rf32,
+    Rf32 * 0.5'f32, Rf32 * 0.5'f32, Rf32 * 0.5'f32, Rf32 * 0.5'f32,
+    Gf32, Gf32, Gf32, Gf32,
+    Gf32 * 0.5'f32, Gf32 * 0.5'f32, Gf32 * 0.5'f32, Gf32 * 0.5'f32,
+    Bf32, Bf32, Bf32, Bf32,
+    Bf32 * 0.5'f32, Bf32 * 0.5'f32, Bf32 * 0.5'f32, Bf32 * 0.5'f32,
+  ]
+var
+  tris: seq[array[3, uint16]]
+# for i in 0'u16 ..< 6'u16:
+  #  let off = i * 4
+var off = 0'u16 * 4
+# tris.add [off + 0'u16, off + 1'u16, off + 2'u16]
+# tris.add [off + 2'u16, off + 3'u16, off + 0'u16]
+# off = 1'u16 * 4
+# tris.add [off + 0'u16, off + 1'u16, off + 2'u16]
+# tris.add [off + 2'u16, off + 3'u16, off + 0'u16]
+# off = 4'u16 * 4
+# tris.add [off + 0'u16, off + 1'u16, off + 2'u16]
+# tris.add [off + 2'u16, off + 3'u16, off + 0'u16]
+# off = 3'u16 * 4
+# tris.add [off + 0'u16, off + 1'u16, off + 2'u16]
+# tris.add [off + 2'u16, off + 3'u16, off + 0'u16]
+
+when isMainModule:
+  var myengine = igniteEngine("Hello cube")
+
+  # build a mesh
+  var trianglemesh = new IndexedMesh[VertexDataA, uint16]
+  trianglemesh.vertexData = VertexDataA(
+    position: PositionAttribute[Vec3[float32]](data: cube_pos),
+    color: ColorAttribute[Vec3[float32]](data: cube_color),
+  )
+  trianglemesh.indices = tris
+  # build a single-object scene graph
+  var triangle = new Thing
+  # add the triangle mesh to the object
+  triangle.parts.add trianglemesh
+
+  # upload data, prepare shaders, etc
+  const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]("""
+  out_position = (uniforms.projection * uniforms.view * uniforms.model) * vec4(in_position, 1);
+  """)
+  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
+  pipeline = setupPipeline[VertexDataA, Uniforms, uint16](
+    myengine,
+    triangle,
+    vertexShader,
+    fragmentShader
+  )
+  # show something
+  myengine.run(pipeline, globalUpdate)
+  pipeline.trash()
+  myengine.trash()
--- a/examples/hello_triangle.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/examples/hello_triangle.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -55,7 +55,7 @@
   # upload data, prepare shaders, etc
   const vertexShader = generateVertexShaderCode[VertexDataA, void]()
   const fragmentShader = generateFragmentShaderCode[VertexDataA]()
-  pipeline = setupPipeline[VertexDataA, void, uint16](
+  pipeline = setupPipeline[VertexDataA, void, void](
     myengine,
     triangle,
     vertexShader,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/input.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -0,0 +1,99 @@
+import std/times
+import std/strutils
+import std/enumerate
+
+import zamikongine/engine
+import zamikongine/math/vector
+import zamikongine/math/matrix
+import zamikongine/vertex
+import zamikongine/descriptor
+import zamikongine/mesh
+import zamikongine/thing
+import zamikongine/shader
+import zamikongine/buffer
+
+type
+  # define type of vertex
+  VertexDataA = object
+    position: PositionAttribute[Vec2[float32]]
+    color: ColorAttribute[Vec3[float32]]
+  Uniforms = object
+    cursor: Descriptor[Vec2[float32]]
+    aspect: Descriptor[float32]
+
+var
+  pipeline: RenderPipeline[VertexDataA, Uniforms]
+  uniforms: Uniforms
+uniforms.aspect.value = 1
+
+
+proc globalUpdate(engine: var Engine, dt: float32) =
+  uniforms.aspect.value = float32(engine.vulkan.frameDimension.height) / float32(engine.vulkan.frameDimension.width)
+  uniforms.cursor.value[0] = ((float32(engine.input.mouseX) / float32(engine.vulkan.frameDimension.width)) * 2'f32 ) - 1'f32
+  uniforms.cursor.value[1] = ((float32(engine.input.mouseY) / float32(engine.vulkan.frameDimension.height)) * 2'f32 ) - 1'f32
+  for buffer in pipeline.uniformBuffers:
+    buffer.updateData(uniforms)
+
+# vertex data (types must match the above VertexAttributes)
+const
+  shape = @[
+    Vec2([-  1'f32, -  1'f32]),
+    Vec2([   1'f32, -  1'f32]),
+    Vec2([-0.3'f32, -0.3'f32]),
+    Vec2([-0.3'f32, -0.3'f32]),
+    Vec2([-  1'f32,    1'f32]),
+    Vec2([-  1'f32, -  1'f32]),
+  ]
+  colors = @[
+    Vec3([1'f32, 0'f32, 0'f32]),
+    Vec3([1'f32, 0'f32, 0'f32]),
+    Vec3([1'f32, 0'f32, 0'f32]),
+    Vec3([0.8'f32, 0'f32, 0'f32]),
+    Vec3([0.8'f32, 0'f32, 0'f32]),
+    Vec3([0.8'f32, 0'f32, 0'f32]),
+  ]
+
+when isMainModule:
+  var myengine = igniteEngine("Input")
+
+  # build a single-object scene graph
+  var cursor = new Thing
+  var cursorpart = new Mesh[VertexDataA]
+  cursorpart.vertexData = VertexDataA(
+    position: PositionAttribute[Vec2[float32]](data: shape),
+    color: ColorAttribute[Vec3[float32]](data: colors),
+  )
+  # transform the cursor a bit to make it look nice
+  for i in 0 ..< cursorpart.vertexData.position.data.len:
+    let cursorscale = (
+      scale2d(0.07'f32, 0.07'f32) *
+      translate2d(1'f32, 1'f32) *
+      rotate2d(-float32(PI) / 4'f32) *
+      scale2d(0.5'f32, 1'f32) *
+      rotate2d(float32(PI) / 4'f32)
+    )
+    let pos = Vec3[float32]([cursorpart.vertexData.position.data[i][0], cursorpart.vertexData.position.data[i][1], 1'f32])
+    cursorpart.vertexData.position.data[i] = (cursorscale * pos).xy
+  cursor.parts.add cursorpart
+
+  var scene = new Thing
+  scene.children.add cursor
+
+  # upload data, prepare shaders, etc
+  const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]("""
+  out_position.x = in_position.x * uniforms.aspect + uniforms.cursor.x;
+  out_position.y = in_position.y + uniforms.cursor.y;
+  """)
+  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
+  echo vertexShader
+  echo fragmentShader
+  pipeline = setupPipeline[VertexDataA, Uniforms, uint16](
+    myengine,
+    scene,
+    vertexShader,
+    fragmentShader
+  )
+  # show something
+  myengine.run(pipeline, globalUpdate)
+  pipeline.trash()
+  myengine.trash()
--- a/examples/squares.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/examples/squares.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -33,7 +33,7 @@
 
 when isMainModule:
   randomize()
-  var myengine = igniteEngine("A lot of triangles")
+  var myengine = igniteEngine("Squares")
   const
     COLUMNS = 10
     ROWS = 10
--- a/src/zamikongine/engine.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/engine.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -79,10 +79,20 @@
     imageAvailableSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore]
     renderFinishedSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore]
     inFlightFences*: array[MAX_FRAMES_IN_FLIGHT, VkFence]
+  Input* = object
+    keysDown*: set[Key]
+    keysPressed*: set[Key]
+    keysReleased*: set[Key]
+    mouseDown*: set[MouseButton]
+    mousePressed*: set[MouseButton]
+    mouseReleased*: set[MouseButton]
+    mouseX*: int
+    mouseY*: int
   Engine* = object
     vulkan*: Vulkan
     window*: NativeWindow
     currentscenedata*: ref Thing
+    input*: Input
 
 proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhysicalDevice] =
   for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance):
@@ -518,13 +528,14 @@
     var ubermesh = createUberMesh(allmeshes)
     result.vertexBuffers.add createVertexBuffers(ubermesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue)
 
-  # vertex buffers with indexes
-  var allindexedmeshes: seq[IndexedMesh[VertexType, IndexType]]
-  for mesh in partsOfType[ref IndexedMesh[VertexType, IndexType]](engine.currentscenedata):
-    allindexedmeshes.add(mesh[])
-  if allindexedmeshes.len > 0:
-    var indexedubermesh = createUberMesh(allindexedmeshes)
-    result.indexedVertexBuffers.add createIndexedVertexBuffers(indexedubermesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue)
+  when not IndexType is void:
+    # vertex buffers with indexes
+    var allindexedmeshes: seq[IndexedMesh[VertexType, IndexType]]
+    for mesh in partsOfType[ref IndexedMesh[VertexType, IndexType]](engine.currentscenedata):
+      allindexedmeshes.add(mesh[])
+    if allindexedmeshes.len > 0:
+      var indexedubermesh = createUberMesh(allindexedmeshes)
+      result.indexedVertexBuffers.add createIndexedVertexBuffers(indexedubermesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue)
 
   # uniform buffers
   result.uniformBuffers = createUniformBuffers[MAX_FRAMES_IN_FLIGHT, UniformType](
@@ -704,16 +715,31 @@
   while not killed:
 
     # process input
+    engine.input.keysPressed = {}
+    engine.input.keysReleased = {}
+    engine.input.mousePressed = {}
+    engine.input.mouseReleased = {}
     for event in engine.window.pendingEvents():
       case event.eventType:
         of Quit:
           killed = true
         of ResizedWindow:
           resized = true
-        of KeyDown:
-          echo event
-          if event.key == Escape:
-            killed = true
+        of KeyPressed:
+          engine.input.keysPressed.incl event.key
+          engine.input.keysDown.incl event.key
+        of KeyReleased:
+          engine.input.keysReleased.incl event.key
+          engine.input.keysDown.excl event.key
+        of MousePressed:
+          engine.input.mousePressed.incl event.button
+          engine.input.mouseDown.incl event.button
+        of MouseReleased:
+          engine.input.mouseReleased.incl event.button
+          engine.input.mouseDown.excl event.button
+        of MouseMoved:
+          engine.input.mouseX = event.x
+          engine.input.mouseY = event.y
         else:
           discard
 
--- a/src/zamikongine/events.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/events.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -2,19 +2,28 @@
   EventType* = enum
     Quit
     ResizedWindow
-    KeyDown
-    KeyUp
-  Key* = enum
+    KeyPressed, KeyReleased
+    MousePressed, MouseReleased, MouseMoved,
+  Key* {.size: sizeof(cint), pure.} = enum
     UNKNOWN
+    Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12
+    NumberRowExtra1, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `0`, NumberRowExtra2, NumberRowExtra3 # tilde, minus, plus
     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
-    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
-    `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `0`
-    Minus, Plus, Underscore, Equals, Space, Enter, Backspace, Tab
-    Comma, Period, Semicolon, Colon,
-    Escape, CtrlL, ShirtL, AltL, CtrlR, ShirtR, AltR
+    Tab, CapsLock, ShiftL, ShiftR, CtrlL, CtrlR, SuperL, SuperR, AltL, AltR, Space, Enter, Backspace
+    LetterRow1Extra1, LetterRow1Extra2, LetterRow1Extra3 # open bracket, close brackt, backslash
+    LetterRow2Extra1, LetterRow2Extra2 # semicolon, quote
+    LetterRow3Extra1, LetterRow3Extra2, LetterRow3Extra3 # comma, period, slash
+    Up, Down, Left, Right
+    PageUp, PageDown, Home, End, Insert, Delete
+  MouseButton* {.size: sizeof(cint), pure.} = enum
+    UNKNOWN, Mouse1, Mouse2, Mouse3
   Event* = object
     case eventType*: EventType
-    of KeyDown, KeyUp:
+    of KeyPressed, KeyReleased:
       key*: Key
+    of MousePressed, MouseReleased:
+      button*: MouseButton
+    of MouseMoved:
+      x*, y*: int
     else:
       discard
--- a/src/zamikongine/math/matrix.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/math/matrix.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -6,6 +6,8 @@
 
 import ./vector
 
+export math
+
 type
   # layout is row-first
   # having an object instead of directly aliasing the array seems a bit ugly at
@@ -15,19 +17,19 @@
   #
   # Though, great news is that objects have zero overhead!
   Mat22*[T: SomeNumber] = object
-    data: array[4, T]
+    data*: array[4, T]
   Mat23*[T: SomeNumber] = object
-    data: array[6, T]
+    data*: array[6, T]
   Mat32*[T: SomeNumber] = object
-    data: array[6, T]
+    data*: array[6, T]
   Mat33*[T: SomeNumber] = object
-    data: array[9, T]
+    data*: array[9, T]
   Mat34*[T: SomeNumber] = object
-    data: array[12, T]
+    data*: array[12, T]
   Mat43*[T: SomeNumber] = object
-    data: array[12, T]
+    data*: array[12, T]
   Mat44*[T: SomeNumber] = object
-    data: array[16, T]
+    data*: array[16, T]
   MatMM* = Mat22|Mat33|Mat44
   MatMN* = Mat23|Mat32|Mat34|Mat43
   Mat* = MatMM|MatMN
@@ -343,14 +345,14 @@
   let
     cosa = cos(angle)
     sina = sin(angle)
-    x = a.x
-    y = a.y
-    z = a.z
+    x = a[0]
+    y = a[1]
+    z = a[2]
   Mat44[T](data: [
     x * x * (1 - cosa) + cosa,     y * x * (1 - cosa) - z * sina, z * x * (1 - cosa) + y * sina, T(0),
     x * y * (1 - cosa) + z * sina, y * y * (1 - cosa) + cosa,     z * y * (1 - cosa) - x * sina, T(0),
     x * z * (1 - cosa) - y * sina, y * z * (1 - cosa) + x * sina, z * z * (1 - cosa) + cosa,     T(0),
-    T(0),                             T(0),                             T(0),                             1,
+    T(0),                          T(0),                          T(0),                          T(1),
   ])
 
 
@@ -370,3 +372,20 @@
 makeRandomInit(Mat34)
 makeRandomInit(Mat43)
 makeRandomInit(Mat44)
+
+func perspective*[T: SomeFloat](fovy, aspect, zNear, zFar: T): Mat44[T] =
+  let tanHalfFovy = tan(fovy / T(2))
+  return Mat44[T](data:[
+    T(1) / (aspect * tanHalfFovy), T(0),               T(0),                     T(0),
+    T(0),                          T(1) / tanHalfFovy, T(0),                     T(0),
+    T(0),                          T(0),               T(zFar / (zFar - zNear)), T(-(zFar * zNear) / (zFar - zNear)),
+    T(0),                          T(0),               T(1),                     T(1),
+  ])
+
+func ortho*[T: SomeFloat](left, right, bottom, top, zNear, zFar: T): Mat44[T] =
+  Mat44[T](data:[
+    T(2) / (right - left), T(0),                  T(0),                  -(right + left) / (right - left),
+    T(0),                  T(2) / (top - bottom), T(0),                  -(top + bottom) / (top - bottom),
+    T(0),                  T(0),                  T(1) / (zFar - zNear), -zNear / (zFar - zNear),
+    T(0),                  T(0),                  T(1),                  T(1),
+  ])
--- a/src/zamikongine/math/vector.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/math/vector.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -5,6 +5,8 @@
 import std/typetraits
 import std/tables
 
+export math
+
 
 type
   Vec2*[T: SomeNumber] = array[2, T]
--- a/src/zamikongine/mesh.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/mesh.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -107,5 +107,5 @@
   data: @[Vec2[T]([T(0), T(0)]), Vec2[T]([T(0), T(1)]), Vec2[T]([T(1), T(1)]), Vec2[T]([T(1), T(0)])]
 )
 func squareIndices*[T:uint16|uint32](): auto = seq[array[3, T]](
-  @[[T(1), T(0), T(3)], [T(2), T(1), T(3)], ]
+  @[[T(0), T(1), T(3)], [T(2), T(1), T(3)]]
 )
--- a/src/zamikongine/platform/linux/symkey_map.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/platform/linux/symkey_map.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -1,16 +1,27 @@
 import std/tables
 export tables
 
-import x11/keysym
-# import x11/x
+import x11/x
+
 
 import ../../events
 
+# got values (keycodes) from xev
 const KeyTypeMap* = {
-  XK_A: A, XK_B: B, XK_C: C, XK_D: D, XK_E: E, XK_F: F, XK_G: G, XK_H: H, XK_I: I, XK_J: J, XK_K: K, XK_L: L, XK_M: M, XK_N: N, XK_O: O, XK_P: P, XK_Q: Q, XK_R: R, XK_S: S, XK_T: T, XK_U: U, XK_V: V, XK_W: W, XK_X: X, XK_Y: Y, XK_Z: Z,
-  XK_a: a, XK_b: b, XK_c: c, XK_d: d, XK_e: e, XK_f: f, XK_g: g, XK_h: h, XK_i: i, XK_j: j, XK_k: k, XK_l: l, XK_m: m, XK_n: n, XK_o: o, XK_p: p, XK_q: q, XK_r: r, XK_s: s, XK_t: t, XK_u: u, XK_v: v, XK_w: w, XK_x: Key.x, XK_y: y, XK_z: z,
-  XK_1: `1`, XK_2: `2`, XK_3: `3`, XK_4: `4`, XK_5: `5`, XK_6: `6`, XK_7: `7`, XK_8: `8`, XK_9: `9`, XK_0: `0`,
-  XK_minus: Minus, XK_plus: Plus, XK_underscore: Underscore, XK_equal: Equals, XK_space: Space, XK_Return: Enter, XK_BackSpace: Backspace, XK_Tab: Tab,
-  XK_comma: Comma, XK_period: Period, XK_semicolon: Semicolon, XK_colon: Colon,
-  XK_Escape: Escape, XK_Control_L: CtrlL, XK_Shift_L: ShirtL, XK_Alt_L: AltL, XK_Control_R: CtrlR, XK_Shift_R: ShirtR, XK_Alt_R: AltR
+  9: Escape, 67: F1, 68: F2, 69: F3, 70: F4, 71: F5, 72: F6, 73: F7, 74: F8, 75: F9, 76: F10, 95: F11, 96: F12,
+  49: NumberRowExtra1, 10: `1`, 11: `2`, 12: `3`, 13: `4`, 14: `5`, 15: `6`, 16: `7`, 17: `8`, 18: `9`, 19: `0`, 20: NumberRowExtra2, 21: NumberRowExtra3,
+  24: Q, 25: W, 26: E, 27: R, 28: T, 29: Y, 30: U, 31: I, 32: O, 33: P, 38: A, 39: S, 40: D, 41: Key.F, 42: G, 43: H, 44: J, 45: K, 46: L, 52: Z, 53: X, 54: C, 55: V, 56: B, 57: N, 58: M,
+
+  23: Tab, 66: CapsLock, 50: ShiftL, 62: ShiftR, 37: CtrlL, 105: CtrlR, 133: SuperL, #[ SuperR, ]# 64: AltL, #[ AltR, ]# 65: Space, 36: Enter, 22: Backspace,
+    34: LetterRow1Extra1, 35: LetterRow1Extra2, 51: LetterRow1Extra3,
+    47: LetterRow2Extra1, 48: LetterRow2Extra2,
+    59: LetterRow3Extra1, 60: LetterRow3Extra2, 61: LetterRow3Extra3,
+    111: Up, 116: Down, 113: Left, 114: Right,
+    112: PageUp, 117: PageDown, 110: Home, 115: End, 118: Insert, 119: Delete,
 }.toTable
+
+const MouseButtonTypeMap* = {
+  Button1: MouseButton.Mouse1,
+  Button2: MouseButton.Mouse2,
+  Button3: MouseButton.Mouse3,
+}.toTable
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/zamikongine/platform/linux/vulkan.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -0,0 +1,13 @@
+# included in zamikongine/vulkan_helpers
+const REQUIRED_PLATFORM_EXTENSIONS = @["VK_KHR_xlib_surface".cstring]
+
+proc load_platform_extensions() =
+  loadVK_KHR_xlib_surface()
+
+proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR =
+  var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR(
+    sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
+    dpy: window.display,
+    window: window.window,
+  )
+  checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result))
--- a/src/zamikongine/platform/linux/xlib.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/platform/linux/xlib.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -1,7 +1,9 @@
 import
   x11/xlib,
   x11/xutil,
-  x11/keysym
+  x11/keysym,
+  x11/xinput,
+  x11/xinput2
 import x11/x
 
 import ../../events
@@ -16,6 +18,7 @@
   NativeWindow* = object
     display*: PDisplay
     window*: Window
+    emptyCursor: Cursor
 
 template checkXlibResult*(call: untyped) =
   let value = call
@@ -36,15 +39,24 @@
 
   let window = XCreateSimpleWindow(display, rootWindow, -1, -1, 800, 600, 0, foregroundColor, backgroundColor)
   checkXlibResult XSetStandardProperties(display, window, title, "window", 0, nil, 0, nil)
-  checkXlibResult XSelectInput(display, window, ButtonPressMask or KeyPressMask or ExposureMask)
+  checkXlibResult XSelectInput(display, window, PointerMotionMask or ButtonPressMask or ButtonReleaseMask or KeyPressMask or KeyReleaseMask or ExposureMask)
   checkXlibResult XMapWindow(display, window)
 
   deleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", XBool(false))
   checkXlibResult XSetWMProtocols(display, window, addr(deleteMessage), 1)
 
-  return NativeWindow(display: display, window: window)
+  # quite a lot of work to hide the cursor...
+  var data = "\0".cstring
+  var pixmap = XCreateBitmapFromData(display, window, data, 1, 1)
+  var color: XColor
+  var empty_cursor = XCreatePixmapCursor(display, pixmap, pixmap, addr(color), addr(color), 0, 0)
+  checkXlibResult XFreePixmap(display, pixmap)
+  checkXlibResult XDefineCursor(display, window, empty_cursor)
+
+  return NativeWindow(display: display, window: window, emptyCursor: empty_cursor)
 
 proc trash*(window: NativeWindow) =
+  checkXlibResult window.display.XFreeCursor(window.emptyCursor)
   checkXlibResult window.display.XDestroyWindow(window.window)
   discard window.display.XCloseDisplay() # always returns 0
 
@@ -62,12 +74,21 @@
       if cast[Atom](event.xclient.data.l[0]) == deleteMessage:
         result.add(Event(eventType: Quit))
     of KeyPress:
-      let xkey: KeySym = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0)
-      result.add(Event(eventType: KeyDown, key: KeyTypeMap.getOrDefault(xkey, UNKNOWN)))
+      let xkey = int(cast[PXKeyEvent](addr(event)).keycode)
+      result.add Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN))
     of KeyRelease:
-      let xkey: KeySym = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0)
-      result.add(Event(eventType: KeyUp, key: KeyTypeMap.getOrDefault(xkey, UNKNOWN)))
+      let xkey = int(cast[PXKeyEvent](addr(event)).keycode)
+      result.add Event(eventType: KeyReleased, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN))
+    of ButtonPress:
+      let button = int(cast[PXButtonEvent](addr(event)).button)
+      result.add Event(eventType: MousePressed, button: MouseButtonTypeMap.getOrDefault(button, MouseButton.UNKNOWN))
+    of ButtonRelease:
+      let button = int(cast[PXButtonEvent](addr(event)).button)
+      result.add Event(eventType: MouseReleased, button: MouseButtonTypeMap.getOrDefault(button, MouseButton.UNKNOWN))
+    of MotionNotify:
+      let motion = cast[PXMotionEvent](addr(event))
+      result.add Event(eventType: MouseMoved, x: motion.x, y: motion.y)
     of ConfigureNotify:
-      result.add(Event(eventType: ResizedWindow))
+      result.add Event(eventType: ResizedWindow)
     else:
       discard
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/zamikongine/platform/windows/virtualkey_map.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -0,0 +1,18 @@
+import std/tables
+export tables
+
+import winim/core
+
+import ../../events
+
+const KeyTypeMap* = {
+  VK_ESCAPE: Key.Escape, VK_F1: F1, VK_F2: F2, VK_F3: F3, VK_F4: F4, VK_F5: F5, VK_F6: F6, VK_F7: F7, VK_F8: F8, VK_F9: F9, VK_F10: F10, VK_F11: F11, VK_F12: F12,
+  VK_OEM_3: NumberRowExtra1, int('0'): `0`, int('1'): `1`, int('2'): `2`, int('3'): `3`, int('4'): `4`, int('5'): `5`, int('6'): `6`, int('7'): `7`, int('8'): `8`, int('9'): `9`, VK_OEM_MINUS: NumberRowExtra2, VK_OEM_PLUS: NumberRowExtra3,
+  int('A'): A, int('B'): B, int('C'): C, int('D'): D, int('E'): E, int('F'): F, int('G'): G, int('H'): H, int('I'): I, int('J'): J, int('K'): K, int('L'): L, int('M'): M, int('N'): N, int('O'): O, int('P'): P, int('Q'): Q, int('R'): R, int('S'): S, int('T'): T, int('U'): U, int('V'): V, int('W'): W, int('X'): X, int('Y'): Y, int('Z'): Z,
+  VK_TAB: Tab, VK_CAPITAL: CapsLock, VK_LSHIFT: ShiftL, VK_SHIFT: ShiftL, VK_RSHIFT: ShiftR, VK_LCONTROL: CtrlL, VK_CONTROL: CtrlL, VK_RCONTROL: CtrlR, VK_LWIN: SuperL, VK_RWIN: SuperR, VK_LMENU: AltL, VK_RMENU: AltR, VK_SPACE: Space, VK_RETURN: Enter, VK_BACK: Backspace,
+  VK_OEM_4: LetterRow1Extra1, VK_OEM_6: LetterRow1Extra2, VK_OEM_5: LetterRow1Extra3,
+  VK_OEM_1: LetterRow2Extra1, VK_OEM_7: LetterRow2Extra2,
+  VK_OEM_COMMA: LetterRow3Extra1, VK_OEM_PERIOD: LetterRow3Extra2, VK_OEM_2: LetterRow3Extra3,
+    VK_UP: Up, VK_DOWN: Down, VK_LEFT: Left, VK_RIGHT: Right,
+    VK_PRIOR: PageUp, VK_NEXT: PageDown, VK_HOME: Home, VK_END: End, VK_INSERT: Insert, VK_DELETE: Key.Delete,
+}.toTable
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/zamikongine/platform/windows/vulkan.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -0,0 +1,13 @@
+# included in zamikongine/vulkan_helpers
+const REQUIRED_PLATFORM_EXTENSIONS* = @["VK_KHR_win32_surface".cstring]
+
+proc load_platform_extensions() =
+  loadVK_KHR_win32_surface()
+
+proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR =
+  var surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR(
+    sType: VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
+    hinstance: window.hinstance,
+    hwnd: window.hwnd,
+  )
+  checkVkResult vkCreateWin32SurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result))
--- a/src/zamikongine/platform/windows/win32.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/platform/windows/win32.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -1,5 +1,6 @@
 import winim
 
+import ./virtualkey_map
 import ../../events
 
 type
@@ -7,6 +8,7 @@
     hinstance*: HINSTANCE
     hwnd*: HWND
 
+# sorry, have to use module-global variable to capture windows events
 var currentEvents: seq[Event]
 
 template checkWin32Result*(call: untyped) =
@@ -14,10 +16,42 @@
   if value != 0:
     raise newException(Exception, "Win32 error: " & astToStr(call) & " returned " & $value)
 
+
+proc MapLeftRightKeys(key: INT, lparam: LPARAM): INT =
+  case key
+  of VK_SHIFT:
+    MapVirtualKey(UINT((lParam and 0x00ff0000) shr 16), MAPVK_VSC_TO_VK_EX)
+  of VK_CONTROL:
+    if (lParam and 0x01000000) == 0: VK_LCONTROL else: VK_RCONTROL
+  of VK_MENU:
+    if (lParam and 0x01000000) == 0: VK_LMENU else: VK_RMENU
+  else:
+    key
+
 proc WindowHandler(hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM): LRESULT {.stdcall.} =
   case uMsg
   of WM_DESTROY:
     currentEvents.add(Event(eventType: events.EventType.Quit))
+  of WM_KEYDOWN, WM_SYSKEYDOWN:
+    let key = MapLeftRightKeys(INT(wParam), lParam)
+    currentEvents.add(Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(key, Key.UNKNOWN)))
+  of WM_KEYUP, WM_SYSKEYUP:
+    let key = MapLeftRightKeys(INT(wParam), lParam)
+    currentEvents.add(Event(eventType: KeyReleased, key: KeyTypeMap.getOrDefault(key, Key.UNKNOWN)))
+  of WM_LBUTTONDOWN:
+    currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse1))
+  of WM_LBUTTONUP:
+    currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse1))
+  of WM_MBUTTONDOWN:
+    currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse2))
+  of WM_MBUTTONUP:
+    currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse2))
+  of WM_RBUTTONDOWN:
+    currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse3))
+  of WM_RBUTTONUP:
+    currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse3))
+  of WM_MOUSEMOVE:
+    currentEvents.add(Event(eventType: events.MouseMoved, x: GET_X_LPARAM(lParam), y: GET_Y_LPARAM(lParam)))
   else:
     return DefWindowProc(hwnd, uMsg, wParam, lParam)
 
@@ -49,7 +83,8 @@
       nil
     )
 
-  discard ShowWindow(result.hwnd, 1)
+  discard ShowWindow(result.hwnd, SW_SHOW)
+  discard ShowCursor(false)
 
 proc trash*(window: NativeWindow) =
   discard
@@ -60,8 +95,10 @@
   (int(rect.right - rect.left), int(rect.bottom - rect.top))
 
 proc pendingEvents*(window: NativeWindow): seq[Event] =
+  # empty queue
   currentEvents = newSeq[Event]()
   var msg: MSG
+  # fill queue
   while PeekMessage(addr(msg), window.hwnd, 0, 0, PM_REMOVE):
     DispatchMessage(addr(msg))
   return currentEvents
--- a/src/zamikongine/shader.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/shader.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -1,9 +1,10 @@
+import std/os
+import std/hashes
 import std/strformat
 import std/strutils
 import std/tables
 import std/compilesettings
 
-
 import ./vulkan_helpers
 import ./glsl_helpers
 import ./vulkan
@@ -31,20 +32,22 @@
   of VK_SHADER_STAGE_COMPUTE_BIT: "comp"
   of VK_SHADER_STAGE_ALL: ""
 
-proc compileGLSLToSPIRV(stage: VkShaderStageFlagBits, shaderSource: string, entrypoint: string): seq[uint32] {.compileTime.} =
-  let stagename = stage2string(stage)
+proc compileGLSLToSPIRV(stage: static VkShaderStageFlagBits, shaderSource: static string, entrypoint: string): seq[uint32] {.compileTime.} =
+  const
+    stagename = stage2string(stage)
+    shaderHash = hash(shaderSource)
+    # cross compilation for windows workaround, sorry computer
+    shaderout = getTempDir().replace("\\", "/") & "/" & fmt"shader_{shaderHash}.{stagename}"
+    projectPath = querySetting(projectPath)
 
-  # TODO: compiles only on linux for now (because we don't have compile-time functionality in std/tempfile)
-  let (tmpfile, exitCode) = gorgeEx(command=fmt"mktemp --tmpdir shader_XXXXXXX.{stagename}")
-  if exitCode != 0:
-    raise newException(Exception, tmpfile)
-
-  let (output, exitCode_glsl) = gorgeEx(command=fmt"{querySetting(projectPath)}/glslangValidator --entry-point {entrypoint} -V --stdin -S {stagename} -o {tmpfile}", input=shaderSource)
+  let (output, exitCode_glsl) = gorgeEx(command=fmt"{projectPath}/glslangValidator --entry-point {entrypoint} -V --stdin -S {stagename} -o {shaderout}", input=shaderSource)
   if exitCode_glsl != 0:
     raise newException(Exception, output)
-  let shaderbinary = staticRead tmpfile
+  if output == "": # this happens when the nim was invoked with "check" instead of "compile/c", as it prevents the gorgeEx command to really run. However, there is hope, see https://github.com/nim-lang/RFCs/issues/430
+    return result
+  let shaderbinary = staticRead shaderout
 
-  let (output_rm, exitCode_rm) = gorgeEx(command=fmt"rm {tmpfile}")
+  let (output_rm, exitCode_rm) = gorgeEx(command=fmt"rm {shaderout}")
   if exitCode_rm != 0:
     raise newException(Exception, output_rm)
 
--- a/src/zamikongine/vulkan.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/vulkan.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -8,23 +8,30 @@
 when defined(linux):
   import x11/x
   import x11/xlib
+  const vkDLL = "libvulkan.so.1"
+else:
+  type
+    Display* = ptr object
+    VisualID* = ptr object
+    Window* = ptr object
+
 when defined(windows):
+  const vkDLL = "vulkan-1.dll"
   import winim
+else:
+  type
+    HINSTANCE* = ptr object
+    HWND* = ptr object
+    HMONITOR* = ptr object
+    HANDLE* = ptr object
+    SECURITY_ATTRIBUTES* = ptr object
+    DWORD* = ptr object
+    LPCWSTR* = ptr object
 
 var vkGetProc: proc(procName: cstring): pointer {.cdecl.}
 
 import dynlib
 
-when defined(windows):
-  {. emit: """#define VK_USE_PLATFORM_WIN32_KHR""" .}
-  const vkDLL = "vulkan-1.dll"
-elif defined(linux):
-  {.passl: gorge("pkg-config --libs vulkan").}
-  {. emit: """#define VK_USE_PLATFORM_X11_KHR""" .}
-  const vkDLL = "libvulkan.so.1"
-else:
-  raise quit("Unsupported platform")
-
 let vkHandleDLL = loadLib(vkDLL)
 if isNil(vkHandleDLL):
   quit("could not load: " & vkDLL)
@@ -1188,22 +1195,6 @@
 
 # Types
 
-# stub types if we are on the wrong platform, so we don't need to "when" all platform functions
-when not defined(linux):
-  type
-    Display* = ptr object
-    VisualID* = ptr object
-    Window* = ptr object
-when not defined(windows):
-  type
-    HINSTANCE* = ptr object
-    HWND* = ptr object
-    HMONITOR* = ptr object
-    HANDLE* = ptr object
-    SECURITY_ATTRIBUTES* = ptr object
-    DWORD* = ptr object
-    LPCWSTR* = ptr object
-
 type
   RROutput* = ptr object
   wl_display* = ptr object
--- a/src/zamikongine/vulkan_helpers.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/vulkan_helpers.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -7,10 +7,6 @@
 import ./vulkan
 import ./window
 
-
-const ENABLEVULKANVALIDATIONLAYERS* = not defined(release)
-
-
 template checkVkResult*(call: untyped) =
   when defined(release):
     discard call
@@ -25,6 +21,14 @@
       error "Vulkan error: ",  astToStr(call),  " returned ", $value
       raise newException(Exception, "Vulkan error: " & astToStr(call) & " returned " & $value)
 
+# the included code need checkVkResult, therefore having the template above
+when defined(linux):
+  include ./platform/linux/vulkan
+when defined(windows):
+  include ./platform/windows/vulkan
+
+const ENABLEVULKANVALIDATIONLAYERS* = not defined(release)
+
 func addrOrNil[T](obj: var openArray[T]): ptr T =
   if obj.len > 0: addr(obj[0]) else: nil
 
@@ -132,11 +136,7 @@
 
 proc createVulkanInstance*(vulkanVersion: uint32): VkInstance =
 
-  var requiredExtensions = @["VK_KHR_surface".cstring]
-  when defined(linux):
-    requiredExtensions.add("VK_KHR_xlib_surface".cstring)
-  when defined(windows):
-    requiredExtensions.add("VK_KHR_win32_surface".cstring)
+  var requiredExtensions = @["VK_KHR_surface".cstring] & REQUIRED_PLATFORM_EXTENSIONS
   when ENABLEVULKANVALIDATIONLAYERS:
     requiredExtensions.add("VK_EXT_debug_utils".cstring)
   
@@ -177,10 +177,7 @@
   checkVkResult vkCreateInstance(addr(createinfo), nil, addr(result))
 
   loadVK_KHR_surface()
-  when defined(linux):
-    loadVK_KHR_xlib_surface()
-  when defined(windows):
-    loadVK_KHR_win32_surface()
+  load_platform_extensions()
   loadVK_KHR_swapchain()
   when ENABLEVULKANVALIDATIONLAYERS:
     loadVK_EXT_debug_utils(result)
@@ -228,20 +225,3 @@
 
 proc getSurfaceCapabilities*(device: VkPhysicalDevice, surface: VkSurfaceKHR): VkSurfaceCapabilitiesKHR =
     checkVkResult device.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(surface, addr(result))
-
-when defined(linux):
-  proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR =
-    var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR(
-      sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
-      dpy: window.display,
-      window: window.window,
-    )
-    checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result))
-when defined(windows):
-  proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR =
-    var surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR(
-      sType: VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
-      hinstance: window.hinstance,
-      hwnd: window.hwnd,
-    )
-    checkVkResult vkCreateWin32SurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result))
--- a/src/zamikongine/window.nim	Mon Jan 16 00:51:03 2023 +0700
+++ b/src/zamikongine/window.nim	Wed Jan 18 09:52:03 2023 +0700
@@ -1,6 +1,4 @@
 when defined(linux):
-  import ./platform/linux/xlib
-  export xlib
+  include ./platform/linux/xlib
 elif defined(windows):
-  import ./platform/windows/win32
-  export win32
+  include ./platform/windows/win32