1191
|
1 import ../../thirdparty/x11/xlib
|
|
2 import ../../thirdparty/x11/xutil
|
|
3 import ../../thirdparty/x11/keysym
|
|
4 import ../../thirdparty/x11/x as x11
|
|
5 import ../../thirdparty/x11/xkblib
|
|
6
|
|
7 include ./symkey_map
|
|
8
|
|
9 var deleteMessage*: Atom
|
|
10
|
|
11 type
|
|
12 NativeWindow* = object
|
|
13 display*: ptr xlib.Display
|
|
14 window*: x11.Window
|
|
15 emptyCursor: Cursor
|
|
16
|
|
17 template checkXlibResult(call: untyped) =
|
|
18 let value = call
|
|
19 if value == 0:
|
|
20 raise newException(Exception, "Xlib error: " & astToStr(call) &
|
|
21 " returned " & $value)
|
|
22
|
|
23 proc XErrorLogger(display: PDisplay, event: PXErrorEvent): cint {.cdecl.} =
|
1192
|
24 logging.error &"Xlib: {event[]}"
|
1191
|
25
|
|
26 proc CreateWindow*(title: string): NativeWindow =
|
|
27 checkXlibResult XInitThreads()
|
|
28 let display = XOpenDisplay(nil)
|
|
29 if display == nil:
|
|
30 quit "Failed to open display"
|
|
31 discard XSetErrorHandler(XErrorLogger)
|
|
32
|
|
33 let rootWindow = display.XDefaultRootWindow()
|
|
34 discard display.XkbSetDetectableAutoRepeat(true, nil)
|
|
35 var
|
|
36 attribs: XWindowAttributes
|
|
37 width = cuint(800)
|
|
38 height = cuint(600)
|
|
39 checkXlibResult display.XGetWindowAttributes(rootWindow, addr(attribs))
|
|
40
|
|
41 var attrs = XSetWindowAttributes()
|
|
42 let window = XCreateWindow(
|
|
43 display,
|
|
44 rootWindow,
|
|
45 (attribs.width - cint(width)) div 2, (attribs.height - cint(height)) div 2,
|
|
46 width, height,
|
|
47 0,
|
|
48 CopyFromParent,
|
|
49 InputOutput,
|
|
50 cast[PVisual](CopyFromParent),
|
|
51 0, # CWOverrideRedirect,
|
|
52 addr attrs,
|
|
53 # foregroundColor, backgroundColor
|
|
54 )
|
|
55 checkXlibResult XSetStandardProperties(display, window, title, "window", 0, nil, 0, nil)
|
|
56 checkXlibResult XSelectInput(display, window, PointerMotionMask or ButtonPressMask or ButtonReleaseMask or KeyPressMask or KeyReleaseMask or ExposureMask)
|
|
57 checkXlibResult XMapWindow(display, window)
|
|
58
|
|
59 deleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", XBool(false))
|
|
60 checkXlibResult XSetWMProtocols(display, window, addr(deleteMessage), 1)
|
|
61
|
|
62 var data = "\0".cstring
|
|
63 var pixmap = display.XCreateBitmapFromData(window, data, 1, 1)
|
|
64 var color: XColor
|
|
65 var empty_cursor = display.XCreatePixmapCursor(pixmap, pixmap, addr(color), addr(color), 0, 0)
|
|
66 checkXlibResult display.XFreePixmap(pixmap)
|
|
67 return NativeWindow(display: display, window: window, emptyCursor: empty_cursor)
|
|
68
|
|
69 proc SetTitle*(window: NativeWindow, title: string) =
|
|
70 checkXlibResult XSetStandardProperties(window.display, window.window, title, "window", 0, nil, 0, nil)
|
|
71
|
|
72 proc Fullscreen*(window: var NativeWindow, enable: bool) =
|
|
73 var
|
|
74 wm_state = window.display.XInternAtom("_NET_WM_STATE", 0)
|
|
75 wm_fullscreen = window.display.XInternAtom("_NET_WM_STATE_FULLSCREEN", 0)
|
|
76 var
|
|
77 xev: XEvent
|
|
78 xev.xclient = XClientMessageEvent(
|
|
79 message_type: wm_state,
|
|
80 format: 32,
|
|
81 window: window.window,
|
|
82 data: XClientMessageData(
|
|
83 l: [
|
|
84 int(not enable) xor 1,
|
|
85 clong(wm_fullscreen),
|
|
86 0,
|
|
87 0,
|
|
88 0
|
|
89 ]
|
|
90 )
|
|
91 )
|
|
92 xev.theType = ClientMessage
|
|
93
|
|
94 checkXlibResult window.display.XSendEvent(
|
|
95 window.display.DefaultRootWindow(),
|
|
96 0,
|
|
97 SubstructureRedirectMask or SubstructureNotifyMask,
|
|
98 addr xev
|
|
99 )
|
|
100 checkXlibResult window.display.XFlush()
|
|
101
|
|
102 proc HideSystemCursor*(window: NativeWindow) =
|
|
103 checkXlibResult XDefineCursor(window.display, window.window, window.emptyCursor)
|
|
104 checkXlibResult window.display.XFlush()
|
|
105
|
|
106 proc ShowSystemCursor*(window: NativeWindow) =
|
|
107 checkXlibResult XUndefineCursor(window.display, window.window)
|
|
108 checkXlibResult window.display.XFlush()
|
|
109
|
|
110 proc Destroy*(window: NativeWindow) =
|
|
111 checkXlibResult window.display.XFreeCursor(window.emptyCursor)
|
|
112 checkXlibResult window.display.XDestroyWindow(window.window)
|
|
113 discard window.display.XCloseDisplay() # always returns 0
|
|
114
|
|
115 proc Size*(window: NativeWindow): (int, int) =
|
|
116 var attribs: XWindowAttributes
|
|
117 checkXlibResult XGetWindowAttributes(window.display, window.window, addr(attribs))
|
|
118 return (int(attribs.width), int(attribs.height))
|
|
119
|
|
120 proc PendingEvents*(window: NativeWindow): seq[Event] =
|
|
121 var event: XEvent
|
|
122 while window.display.XPending() > 0:
|
|
123 discard window.display.XNextEvent(addr(event))
|
|
124 case event.theType
|
|
125 of ClientMessage:
|
|
126 if cast[Atom](event.xclient.data.l[0]) == deleteMessage:
|
|
127 result.add(Event(eventType: Quit))
|
|
128 of KeyPress:
|
|
129 let keyevent = cast[PXKeyEvent](addr(event))
|
|
130 let xkey = int(keyevent.keycode)
|
|
131 result.add Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN))
|
|
132 of KeyRelease:
|
|
133 let keyevent = cast[PXKeyEvent](addr(event))
|
|
134 let xkey = int(keyevent.keycode)
|
|
135 result.add Event(eventType: KeyReleased, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN))
|
|
136 of ButtonPress:
|
|
137 let button = int(cast[PXButtonEvent](addr(event)).button)
|
|
138 if button == Button4:
|
|
139 result.add Event(eventType: MouseWheel, amount: 1'f32)
|
|
140 elif button == Button5:
|
|
141 result.add Event(eventType: MouseWheel, amount: -1'f32)
|
|
142 else:
|
|
143 result.add Event(eventType: MousePressed, button: MouseButtonTypeMap.getOrDefault(button, MouseButton.UNKNOWN))
|
|
144 of ButtonRelease:
|
|
145 let button = int(cast[PXButtonEvent](addr(event)).button)
|
|
146 result.add Event(eventType: MouseReleased, button: MouseButtonTypeMap.getOrDefault(button, MouseButton.UNKNOWN))
|
|
147 of MotionNotify:
|
|
148 let motion = cast[PXMotionEvent](addr(event))
|
|
149 result.add Event(eventType: MouseMoved, x: motion.x, y: motion.y)
|
|
150 of ConfigureNotify, Expose:
|
|
151 result.add Event(eventType: ResizedWindow)
|
|
152 else:
|
|
153 discard
|
|
154
|
|
155
|
|
156 proc GetMousePosition*(window: NativeWindow): Option[Vec2f] =
|
|
157 var
|
|
158 root: x11.Window
|
|
159 win: x11.Window
|
|
160 rootX: cint
|
|
161 rootY: cint
|
|
162 winX: cint
|
|
163 winY: cint
|
|
164 mask: cuint
|
|
165 onscreen = XQueryPointer(
|
|
166 window.display,
|
|
167 window.window,
|
|
168 addr(root),
|
|
169 addr(win),
|
|
170 addr(rootX),
|
|
171 addr(rootY),
|
|
172 addr(winX),
|
|
173 addr(winY),
|
|
174 addr(mask),
|
|
175 )
|
|
176 if onscreen != 0:
|
|
177 result = some(Vec2f([float32(winX), float32(winY)]))
|
|
178
|