| 1488 | 1 import std/sequtils | 
|  | 2 import std/enumutils | 
| 1487 | 3 import std/tables | 
| 1485 | 4 import std/strutils | 
|  | 5 import std/logging | 
| 1486 | 6 import std/os | 
| 1485 | 7 | 
|  | 8 include ./vkapi | 
|  | 9 | 
| 1489 | 10 # ============================================================================= | 
|  | 11 # UTILS ======================================================================= | 
|  | 12 # ============================================================================= | 
|  | 13 | 
|  | 14 if not defined(release): | 
|  | 15   addHandler(newConsoleLogger()) | 
|  | 16   addHandler(newFileLogger("svk.log")) | 
|  | 17 | 
|  | 18 # log level | 
|  | 19 when defined(release): | 
|  | 20   const LOGLEVEL {.strdefine.}: string = "Warn" | 
|  | 21 else: | 
|  | 22   const LOGLEVEL {.strdefine.}: string = "Debug" | 
|  | 23 setLogFilter(parseEnum[Level]("lvl" & LOGLEVEL)) | 
|  | 24 | 
|  | 25 template debugAssert(arg: untyped): untyped = | 
|  | 26   when not defined(release): | 
|  | 27     assert arg | 
|  | 28 | 
|  | 29 # we will support vulkan 1.2 for maximum portability | 
|  | 30 const VULKAN_VERSION = VK_MAKE_API_VERSION(0, 1, 2, 0) | 
| 1486 | 31 | 
| 1488 | 32 iterator items*[T: HoleyEnum](E: typedesc[T]): T = | 
|  | 33   for a in enumFullRange(E): | 
|  | 34     yield a | 
|  | 35 | 
| 1485 | 36 template checkVkResult*(call: untyped) = | 
|  | 37   when defined(release): | 
|  | 38     discard call | 
|  | 39   else: | 
|  | 40     # yes, a bit cheap, but this is only for nice debug output | 
|  | 41     var callstr = astToStr(call).replace("\n", "") | 
|  | 42     while callstr.find("  ") >= 0: | 
|  | 43       callstr = callstr.replace("  ", " ") | 
|  | 44     debug "Calling vulkan: ", callstr | 
|  | 45     let value = call | 
|  | 46     if value != VK_SUCCESS: | 
|  | 47       error "Vulkan error: ", astToStr(call), " returned ", $value | 
|  | 48       raise newException( | 
|  | 49         Exception, "Vulkan error: " & astToStr(call) & " returned " & $value | 
|  | 50       ) | 
|  | 51 | 
| 1489 | 52 # ============================================================================= | 
|  | 53 # PLATFORM TYPES ============================================================== | 
|  | 54 # ============================================================================= | 
|  | 55 | 
|  | 56 when defined(windows): | 
|  | 57   type NativeWindow* = object | 
|  | 58     hinstance*: HINSTANCE | 
|  | 59     hwnd*: HWND | 
|  | 60     g_wpPrev*: WINDOWPLACEMENT | 
| 1490 | 61 | 
| 1489 | 62 else: | 
|  | 63   type NativeWindow* = object | 
|  | 64     display*: ptr Display | 
|  | 65     window*: Window | 
|  | 66     emptyCursor*: Cursor | 
|  | 67     ic*: XIC | 
|  | 68 | 
|  | 69 # ============================================================================= | 
|  | 70 # VULKAN INSTANCE ============================================================= | 
|  | 71 # ============================================================================= | 
|  | 72 | 
| 1485 | 73 type SVkInstance* = object | 
|  | 74   vkInstance: VkInstance | 
| 1486 | 75   debugMessenger: VkDebugUtilsMessengerEXT | 
| 1489 | 76   window: NativeWindow | 
|  | 77   vkSurface*: VkSurfaceKHR | 
|  | 78 | 
|  | 79 proc createWindow(title: string): NativeWindow | 
| 1485 | 80 | 
|  | 81 proc `=copy`(a: var SVkInstance, b: SVkInstance) {.error.} | 
|  | 82 | 
|  | 83 proc `=destroy`(a: SVkInstance) = | 
| 1489 | 84   debugAssert a.vkInstance.pointer == nil | 
|  | 85   debugAssert a.vkSurface.pointer == nil | 
|  | 86   debugAssert a.debugMessenger.pointer == nil | 
|  | 87 | 
|  | 88 proc destroy*(a: var SVkInstance) = | 
| 1485 | 89   if a.vkInstance.pointer != nil: | 
| 1486 | 90     if a.debugMessenger.pointer != nil: | 
|  | 91       vkDestroyDebugUtilsMessengerEXT(a.vkInstance, a.debugMessenger, nil) | 
| 1489 | 92       a.debugMessenger = VkDebugUtilsMessengerEXT(nil) | 
|  | 93     vkDestroySurfaceKHR(a.vkInstance, a.vkSurface, nil) | 
|  | 94     a.vkSurface = VkSurfaceKHR(nil) | 
| 1485 | 95     a.vkInstance.vkDestroyInstance(nil) | 
| 1489 | 96     a.vkInstance = VkInstance(nil) | 
| 1485 | 97 | 
| 1486 | 98 proc debugCallback( | 
|  | 99     messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT, | 
|  | 100     messageTypes: VkDebugUtilsMessageTypeFlagsEXT, | 
|  | 101     pCallbackData: ptr VkDebugUtilsMessengerCallbackDataEXT, | 
|  | 102     userData: pointer, | 
|  | 103 ): VkBool32 {.cdecl.} = | 
|  | 104   const LOG_LEVEL_MAPPING = { | 
|  | 105     VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: lvlDebug, | 
|  | 106     VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: lvlInfo, | 
|  | 107     VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: lvlWarn, | 
|  | 108     VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: lvlError, | 
|  | 109   }.toTable | 
| 1489 | 110   log LOG_LEVEL_MAPPING[messageSeverity], "SVK-LOG: ", $pCallbackData.pMessage | 
| 1486 | 111   if messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: | 
|  | 112     let errorMsg = $pCallbackData.pMessage & ": " & getStackTrace() | 
|  | 113     raise newException(Exception, errorMsg) | 
|  | 114   return VK_FALSE | 
|  | 115 | 
| 1485 | 116 proc svkCreateInstance*( | 
|  | 117     applicationName: string, | 
|  | 118     enabledLayers: openArray[string] = [], | 
| 1486 | 119     enabledExtensions: openArray[string] = | 
|  | 120       if defined(release): | 
| 1489 | 121         if defined(windows): | 
|  | 122           @["VK_KHR_surface", "VK_KHR_win32_surface"] | 
|  | 123         else: | 
|  | 124           @["VK_KHR_surface", "VK_KHR_xlib_surface"] | 
| 1486 | 125       else: | 
| 1489 | 126         if defined(windows): | 
|  | 127           @["VK_KHR_surface", "VK_EXT_debug_utils", "VK_KHR_win32_surface"] | 
|  | 128         else: | 
|  | 129           @["VK_KHR_surface", "VK_EXT_debug_utils", "VK_KHR_xlib_surface"], | 
| 1485 | 130     engineName = "semicongine", | 
|  | 131 ): SVkInstance = | 
| 1486 | 132   putEnv("VK_LOADER_LAYERS_ENABLE", "*validation") | 
|  | 133   putEnv( | 
|  | 134     "VK_LAYER_ENABLES", | 
|  | 135     "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD,VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXTVK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT", | 
|  | 136   ) | 
|  | 137   initVulkanLoader() | 
|  | 138 | 
| 1489 | 139   var allLayers = @enabledLayers | 
|  | 140   when not defined(release): | 
|  | 141     allLayers.add "VK_LAYER_KHRONOS_validation" | 
|  | 142 | 
| 1485 | 143   let | 
|  | 144     appinfo = VkApplicationInfo( | 
|  | 145       pApplicationName: applicationName, | 
|  | 146       pEngineName: engineName, | 
| 1486 | 147       apiVersion: VULKAN_VERSION, | 
| 1485 | 148     ) | 
| 1489 | 149     enabledLayersC = allocCStringArray(allLayers) | 
| 1486 | 150     enabledExtensionsC = allocCStringArray(enabledExtensions) | 
| 1485 | 151     createinfo = VkInstanceCreateInfo( | 
|  | 152       pApplicationInfo: addr appinfo, | 
| 1489 | 153       enabledLayerCount: allLayers.len.uint32, | 
| 1486 | 154       ppEnabledLayerNames: enabledLayersC, | 
| 1485 | 155       enabledExtensionCount: enabledExtensions.len.uint32, | 
| 1486 | 156       ppEnabledExtensionNames: enabledExtensionsC, | 
| 1485 | 157     ) | 
|  | 158   checkVkResult vkCreateInstance(addr createinfo, nil, addr result.vkInstance) | 
| 1486 | 159 | 
|  | 160   enabledLayersC.deallocCStringArray() | 
|  | 161   enabledExtensionsC.deallocCStringArray() | 
|  | 162 | 
| 1489 | 163   # only support up to vulkan 1.2 for maximum portability | 
| 1486 | 164   load_VK_VERSION_1_0(result.vkInstance) | 
|  | 165   load_VK_VERSION_1_1(result.vkInstance) | 
|  | 166   load_VK_VERSION_1_2(result.vkInstance) | 
|  | 167 | 
|  | 168   for extension in enabledExtensions: | 
|  | 169     loadExtension(result.vkInstance, extension) | 
| 1489 | 170   load_VK_KHR_swapchain(result.vkInstance) | 
| 1486 | 171 | 
| 1489 | 172   var allTypes: VkDebugUtilsMessageTypeFlagsEXT | 
|  | 173   for t in VkDebugUtilsMessageTypeFlagBitsEXT: | 
|  | 174     allTypes = allTypes or t | 
| 1486 | 175   when not defined(release): | 
|  | 176     var debugMessengerCreateInfo = VkDebugUtilsMessengerCreateInfoEXT( | 
| 1490 | 177       messageSeverity: | 
|  | 178         VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT or | 
|  | 179         VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT or | 
|  | 180         VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT or | 
|  | 181         VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, | 
|  | 182       messageType: | 
|  | 183         VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT or | 
|  | 184         VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT or | 
|  | 185         VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, | 
| 1486 | 186       pfnUserCallback: debugCallback, | 
|  | 187     ) | 
|  | 188     checkVkResult vkCreateDebugUtilsMessengerEXT( | 
|  | 189       result.vkInstance, addr debugMessengerCreateInfo, nil, addr result.debugMessenger | 
|  | 190     ) | 
| 1489 | 191 | 
|  | 192   result.window = createWindow(applicationName) | 
|  | 193   when defined(windows): | 
| 1490 | 194     var surfaceCreateInfo = | 
|  | 195       VkWin32SurfaceCreateInfoKHR(hinstance: window.hinstance, hwnd: window.hwnd) | 
|  | 196     checkVkResult vkCreateWin32SurfaceKHR( | 
|  | 197       instance, addr surfaceCreateInfo, nil, addr result.vkSurface | 
|  | 198     ) | 
| 1489 | 199   else: | 
| 1490 | 200     var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( | 
|  | 201       dpy: result.window.display, window: result.window.window | 
|  | 202     ) | 
|  | 203     checkVkResult result.vkInstance.vkCreateXlibSurfaceKHR( | 
|  | 204       addr surfaceCreateInfo, nil, addr result.vkSurface | 
|  | 205     ) | 
| 1489 | 206 | 
|  | 207 # ============================================================================= | 
|  | 208 # PHYSICAL DEVICES ============================================================ | 
|  | 209 # ============================================================================= | 
|  | 210 | 
|  | 211 type | 
|  | 212   SVkMemoryType* = object | 
|  | 213     size: uint64 | 
|  | 214     deviceLocal: bool # fast for gpu access | 
|  | 215     hostCached: bool # fast for host access | 
|  | 216     hostVisible: bool # can use vkMapMemory | 
| 1490 | 217     hostCohorent: bool | 
|  | 218       # does *not* require vkFlushMappedMemoryRanges and vkInvalidateMappedMemoryRanges | 
|  | 219 | 
| 1489 | 220   SVkQueueFamilies* = object | 
|  | 221     count: int | 
|  | 222     hasGraphics: bool # implies "hasTransfer" | 
|  | 223     hasCompute: bool # implies "hasTransfer" | 
|  | 224 | 
|  | 225   SVkPhysicalDevice* = object | 
|  | 226     name*: string | 
| 1490 | 227     discreteGPU*: bool | 
| 1489 | 228     vkPhysicalDevice*: VkPhysicalDevice | 
|  | 229     vkPhysicalDeviceFeatures*: VkPhysicalDeviceFeatures | 
|  | 230     vkPhysicalDeviceProperties*: VkPhysicalDeviceProperties | 
|  | 231     memoryTypes*: seq[SVkMemoryType] | 
|  | 232     queueFamily*: uint32 | 
|  | 233 | 
|  | 234 proc getUsablePhysicalDevices*(instance: SVkInstance): seq[SVkPhysicalDevice] = | 
|  | 235   var nDevices: uint32 | 
|  | 236   checkVkResult instance.vkInstance.vkEnumeratePhysicalDevices(addr nDevices, nil) | 
|  | 237   var devices = newSeq[VkPhysicalDevice](nDevices) | 
| 1490 | 238   checkVkResult instance.vkInstance.vkEnumeratePhysicalDevices( | 
|  | 239     addr nDevices, addr devices[0] | 
|  | 240   ) | 
| 1489 | 241   for d in devices: | 
|  | 242     var dev = SVkPhysicalDevice(vkPhysicalDevice: d) | 
|  | 243     d.vkGetPhysicalDeviceFeatures(addr dev.vkPhysicalDeviceFeatures) | 
|  | 244     d.vkGetPhysicalDeviceProperties(addr dev.vkPhysicalDeviceProperties) | 
|  | 245 | 
| 1490 | 246     if dev.vkPhysicalDeviceProperties.deviceType notin | 
|  | 247         [VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU]: | 
| 1489 | 248       continue | 
|  | 249     dev.name = $cast[cstring](addr dev.vkPhysicalDeviceProperties.deviceName[0]) | 
| 1490 | 250     dev.discreteGPU = | 
|  | 251       dev.vkPhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU | 
| 1489 | 252 | 
|  | 253     var memoryProperties: VkPhysicalDeviceMemoryProperties | 
|  | 254     d.vkGetPhysicalDeviceMemoryProperties(addr memoryProperties) | 
|  | 255     for i in 0 ..< memoryProperties.memoryTypeCount: | 
|  | 256       let heapI = memoryProperties.memoryTypes[i].heapIndex | 
|  | 257       dev.memoryTypes.add SVkMemoryType( | 
|  | 258         size: memoryProperties.memoryHeaps[heapI].size.uint64, | 
| 1490 | 259         deviceLocal: | 
|  | 260           VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT in | 
|  | 261           memoryProperties.memoryTypes[i].propertyFlags, | 
|  | 262         hostCached: | 
|  | 263           VK_MEMORY_PROPERTY_HOST_CACHED_BIT in | 
|  | 264           memoryProperties.memoryTypes[i].propertyFlags, | 
|  | 265         hostVisible: | 
|  | 266           VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in | 
|  | 267           memoryProperties.memoryTypes[i].propertyFlags, | 
|  | 268         hostCohorent: | 
|  | 269           VK_MEMORY_PROPERTY_HOST_COHERENT_BIT in | 
|  | 270           memoryProperties.memoryTypes[i].propertyFlags, | 
| 1489 | 271       ) | 
|  | 272 | 
|  | 273     var familyQueueCount: uint32 | 
|  | 274     var vkQueueFamilyProperties: seq[VkQueueFamilyProperties] | 
|  | 275     d.vkGetPhysicalDeviceQueueFamilyProperties(addr familyQueueCount, nil) | 
|  | 276     vkQueueFamilyProperties.setLen(familyQueueCount) | 
| 1490 | 277     d.vkGetPhysicalDeviceQueueFamilyProperties( | 
|  | 278       addr familyQueueCount, addr vkQueueFamilyProperties[0] | 
|  | 279     ) | 
| 1489 | 280     dev.queueFamily = high(uint32) | 
|  | 281     for i in 0 ..< familyQueueCount: | 
|  | 282       let hasGraphics = VK_QUEUE_GRAPHICS_BIT in vkQueueFamilyProperties[i].queueFlags | 
|  | 283       let hasCompute = VK_QUEUE_COMPUTE_BIT in vkQueueFamilyProperties[i].queueFlags | 
|  | 284       let hasPresentation = VK_FALSE | 
| 1490 | 285       checkVkResult dev.vkPhysicalDevice.vkGetPhysicalDeviceSurfaceSupportKHR( | 
|  | 286         i, instance.vkSurface, addr hasPresentation | 
|  | 287       ) | 
| 1489 | 288 | 
|  | 289       if hasGraphics and hasCompute and bool(hasPresentation): | 
|  | 290         dev.queueFamily = i | 
|  | 291         break | 
|  | 292     if dev.queueFamily == high(uint32): | 
| 1490 | 293       raise newException( | 
|  | 294         Exception, | 
|  | 295         "Did not find queue family with graphics, compute and presentation support!", | 
|  | 296       ) | 
| 1489 | 297 | 
|  | 298     result.add dev | 
|  | 299 | 
| 1490 | 300 # ============================================================================= | 
|  | 301 # DEVICES ===================================================================== | 
|  | 302 # ============================================================================= | 
|  | 303 | 
|  | 304 type SVkDevice* = object | 
|  | 305   vkDevice: VkDevice | 
|  | 306   vkQueue: VkQueue | 
|  | 307 | 
|  | 308 proc `=copy`(a: var SVkDevice, b: SVkDevice) {.error.} | 
|  | 309 | 
|  | 310 proc `=destroy`(a: SVkDevice) = | 
|  | 311   debugAssert a.vkDevice.pointer == nil | 
|  | 312   debugAssert a.vkQueue.pointer == nil | 
|  | 313 | 
|  | 314 proc destroy*(a: var SVkDevice) = | 
|  | 315   if a.vkDevice.pointer != nil: | 
|  | 316     vkDestroyDevice(a.vkDevice, nil) | 
|  | 317     a.vkDevice = VkDevice(nil) | 
|  | 318     a.vkQueue = VkQueue(nil) | 
|  | 319 | 
|  | 320 proc svkCreateDevice*( | 
|  | 321     instance: SVkInstance, physicalDevice: SVkPhysicalDevice | 
|  | 322 ): SVkDevice = | 
|  | 323   let | 
|  | 324     priority = cfloat(1) | 
|  | 325     queueInfo = VkDeviceQueueCreateInfo( | 
|  | 326       queueFamilyIndex: physicalDevice.queueFamily, | 
|  | 327       queueCount: 1, | 
|  | 328       pQueuePriorities: addr priority, | 
|  | 329     ) | 
|  | 330     deviceExtensions = @["VK_KHR_swapchain"] | 
|  | 331     deviceExtensionsC = allocCStringArray(deviceExtensions) | 
|  | 332     enabledFeatures = VkPhysicalDeviceFeatures( | 
|  | 333       fillModeNonSolid: VK_TRUE, | 
|  | 334       depthClamp: VK_TRUE, | 
|  | 335       wideLines: VK_TRUE, | 
|  | 336       largePoints: VK_TRUE, | 
|  | 337     ) | 
|  | 338     vk8bitExt = VkPhysicalDevice8BitStorageFeatures( | 
|  | 339       storageBuffer8BitAccess: VK_TRUE, | 
|  | 340       uniformAndStorageBuffer8BitAccess: VK_TRUE, | 
|  | 341       storagePushConstant8: VK_TRUE, | 
|  | 342     ) | 
|  | 343     vk16bitExt = VkPhysicalDevice16BitStorageFeatures( | 
|  | 344       storageBuffer16BitAccess: VK_TRUE, | 
|  | 345       uniformAndStorageBuffer16BitAccess: VK_TRUE, | 
|  | 346       storagePushConstant16: VK_TRUE, | 
|  | 347       storageInputOutput16: VK_FALSE, | 
|  | 348       pNext: addr vk8bitExt, | 
|  | 349     ) | 
|  | 350     createDeviceInfo = VkDeviceCreateInfo( | 
|  | 351       queueCreateInfoCount: 1, | 
|  | 352       pQueueCreateInfos: addr queueInfo, | 
|  | 353       enabledLayerCount: 0, | 
|  | 354       ppEnabledLayerNames: nil, | 
|  | 355       enabledExtensionCount: 1, | 
|  | 356       ppEnabledExtensionNames: deviceExtensionsC, | 
|  | 357       pEnabledFeatures: addr enabledFeatures, | 
|  | 358       pNext: addr vk16bitExt, | 
|  | 359     ) | 
|  | 360   checkVkResult vkCreateDevice( | 
|  | 361     physicalDevice = physicalDevice.vkPhysicalDevice, | 
|  | 362     pCreateInfo = addr createDeviceInfo, | 
|  | 363     pAllocator = nil, | 
|  | 364     pDevice = addr result.vkDevice, | 
|  | 365   ) | 
|  | 366   deviceExtensionsC.deallocCStringArray() | 
|  | 367   result.vkDevice.vkGetDeviceQueue(physicalDevice.queueFamily, 0, addr result.vkQueue) | 
|  | 368 | 
|  | 369 # ============================================================================= | 
|  | 370 # PLATFORM UTILS ============================================================== | 
|  | 371 # ============================================================================= | 
|  | 372 | 
| 1489 | 373 when defined(windows): | 
|  | 374   proc createWindow(title: string): NativeWindow = | 
|  | 375     result.hInstance = HINSTANCE(GetModuleHandle(nil)) | 
|  | 376     var | 
|  | 377       windowClassName = T"EngineWindowClass" | 
|  | 378       windowName = T(title) | 
|  | 379       windowClass = WNDCLASSEX( | 
|  | 380         cbSize: UINT(WNDCLASSEX.sizeof), | 
|  | 381         lpfnWndProc: windowHandler, | 
|  | 382         hInstance: result.hInstance, | 
|  | 383         lpszClassName: windowClassName, | 
|  | 384         hcursor: currentCursor, | 
|  | 385       ) | 
|  | 386 | 
|  | 387     if (RegisterClassEx(addr(windowClass)) == 0): | 
|  | 388       raise newException(Exception, "Unable to register window class") | 
|  | 389 | 
|  | 390     result.hwnd = CreateWindowEx( | 
|  | 391       DWORD(0), | 
|  | 392       windowClassName, | 
|  | 393       windowName, | 
|  | 394       DWORD(WS_OVERLAPPEDWINDOW), | 
|  | 395       CW_USEDEFAULT, | 
|  | 396       CW_USEDEFAULT, | 
|  | 397       CW_USEDEFAULT, | 
|  | 398       CW_USEDEFAULT, | 
|  | 399       HMENU(0), | 
|  | 400       HINSTANCE(0), | 
|  | 401       result.hInstance, | 
|  | 402       nil, | 
|  | 403     ) | 
|  | 404 | 
|  | 405     result.g_wpPrev.length = UINT(sizeof(WINDOWPLACEMENT)) | 
|  | 406     discard result.hwnd.ShowWindow(SW_SHOW) | 
|  | 407     discard result.hwnd.SetForegroundWindow() | 
|  | 408     discard result.hwnd.SetFocus() | 
|  | 409 | 
|  | 410   proc destroyWindow*(window: NativeWindow) = | 
|  | 411     DestroyWindow(window.hwnd) | 
|  | 412 | 
|  | 413 else: | 
|  | 414   import ../semicongine/thirdparty/x11/xkblib | 
|  | 415   import ../semicongine/thirdparty/x11/xutil | 
|  | 416 | 
|  | 417   var deleteMessage* {.hint[GlobalVar]: off.}: x.Atom # one internal use, not serious | 
|  | 418 | 
|  | 419   proc XErrorLogger(display: PDisplay, event: PXErrorEvent): cint {.cdecl.} = | 
|  | 420     logging.error "Xlib: " & $event[] | 
|  | 421 | 
|  | 422   proc createWindow(title: string): NativeWindow = | 
|  | 423     doAssert XInitThreads() != 0 | 
|  | 424     let display = XOpenDisplay(nil) | 
|  | 425     if display == nil: | 
|  | 426       quit "Failed to open display" | 
|  | 427     discard XSetErrorHandler(XErrorLogger) | 
|  | 428 | 
|  | 429     let screen = display.XDefaultScreen() | 
|  | 430     let rootWindow = display.XRootWindow(screen) | 
|  | 431     let vis = display.XDefaultVisual(screen) | 
|  | 432     discard display.XkbSetDetectableAutoRepeat(true, nil) | 
|  | 433     var | 
|  | 434       attribs: XWindowAttributes | 
|  | 435       width = cuint(800) | 
|  | 436       height = cuint(600) | 
|  | 437     doAssert display.XGetWindowAttributes(rootWindow, addr(attribs)) != 0 | 
|  | 438 | 
|  | 439     var attrs = XSetWindowAttributes( | 
|  | 440       event_mask: | 
|  | 441         FocusChangeMask or KeyPressMask or KeyReleaseMask or ExposureMask or | 
| 1490 | 442         VisibilityChangeMask or StructureNotifyMask or ButtonMotionMask or | 
|  | 443         ButtonPressMask or ButtonReleaseMask | 
| 1489 | 444     ) | 
|  | 445     let window = display.XCreateWindow( | 
|  | 446       rootWindow, | 
|  | 447       (attribs.width - cint(width)) div 2, | 
|  | 448       (attribs.height - cint(height)) div 2, | 
|  | 449       width, | 
|  | 450       height, | 
|  | 451       0, | 
|  | 452       display.XDefaultDepth(screen), | 
|  | 453       InputOutput, | 
|  | 454       vis, | 
|  | 455       CWEventMask, | 
|  | 456       addr(attrs), | 
|  | 457     ) | 
|  | 458     doAssert display.XSetStandardProperties(window, title, "window", 0, nil, 0, nil) != 0 | 
|  | 459     # get an input context, to allow encoding of key-events to characters | 
|  | 460 | 
|  | 461     let im = XOpenIM(display, nil, nil, nil) | 
|  | 462     assert im != nil | 
|  | 463     let ic = im.XCreateIC(XNInputStyle, XIMPreeditNothing or XIMStatusNothing, nil) | 
|  | 464     assert ic != nil | 
|  | 465 | 
|  | 466     doAssert display.XMapWindow(window) != 0 | 
|  | 467 | 
|  | 468     deleteMessage = display.XInternAtom("WM_DELETE_WINDOW", XBool(false)) | 
|  | 469     doAssert display.XSetWMProtocols(window, addr(deleteMessage), 1) != 0 | 
|  | 470 | 
|  | 471     var data = "\0".cstring | 
|  | 472     var pixmap = display.XCreateBitmapFromData(window, data, 1, 1) | 
|  | 473     var color: XColor | 
|  | 474     var empty_cursor = | 
|  | 475       display.XCreatePixmapCursor(pixmap, pixmap, addr(color), addr(color), 0, 0) | 
|  | 476     doAssert display.XFreePixmap(pixmap) != 0 | 
|  | 477 | 
|  | 478     discard display.XSync(0) | 
|  | 479 | 
|  | 480     # wait until window is shown | 
|  | 481     var ev: XEvent | 
|  | 482     while ev.theType != MapNotify: | 
|  | 483       discard display.XNextEvent(addr(ev)) | 
|  | 484 | 
| 1490 | 485     result = | 
|  | 486       NativeWindow(display: display, window: window, emptyCursor: empty_cursor, ic: ic) | 
| 1489 | 487 | 
|  | 488   proc destroyWindow*(window: NativeWindow) = | 
|  | 489     doAssert XDestroyWindow(window.display, window.window) != 0 |