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
|