rev |
line source |
nuclear@4
|
1 #include <stdio.h>
|
nuclear@4
|
2 #include <stdlib.h>
|
nuclear@4
|
3 #include <X11/Xlib.h>
|
nuclear@4
|
4 #include <X11/Xutil.h>
|
nuclear@4
|
5 #include "wsys.h"
|
nuclear@4
|
6 #include "vku.h"
|
nuclear@4
|
7
|
nuclear@4
|
8 struct callbacks {
|
nuclear@4
|
9 void (*display)(void);
|
nuclear@4
|
10 void (*reshape)(int, int);
|
nuclear@4
|
11 void (*keyboard)(int, int);
|
nuclear@4
|
12 void (*mouse)(int, int, int, int);
|
nuclear@4
|
13 void (*motion)(int, int);
|
nuclear@4
|
14 void (*passive)(int, int);
|
nuclear@4
|
15 };
|
nuclear@4
|
16
|
nuclear@4
|
17 enum {
|
nuclear@4
|
18 QUIT = 1,
|
nuclear@4
|
19 RESHAPE = 2,
|
nuclear@4
|
20 REDISPLAY = 4
|
nuclear@4
|
21 };
|
nuclear@4
|
22
|
nuclear@17
|
23 static int reshape(int x, int y);
|
nuclear@4
|
24 static void proc_event(XEvent *ev);
|
nuclear@4
|
25
|
nuclear@4
|
26 static Display *dpy;
|
nuclear@4
|
27 static Window win;
|
nuclear@5
|
28 static VkSurfaceKHR surf;
|
nuclear@5
|
29 static VkSwapchainKHR swapchain;
|
nuclear@4
|
30 static Atom xa_wm_delete;
|
nuclear@4
|
31 static int win_width, win_height;
|
nuclear@4
|
32 static int win_mapped;
|
nuclear@4
|
33 static unsigned int evmask = StructureNotifyMask | ExposureMask;
|
nuclear@4
|
34 static unsigned int pending;
|
nuclear@4
|
35 static struct callbacks cb;
|
nuclear@4
|
36
|
nuclear@4
|
37 int wsys_create_window(int xsz, int ysz)
|
nuclear@4
|
38 {
|
nuclear@4
|
39 int i, scr, num_visuals;
|
nuclear@4
|
40 Window root_win;
|
nuclear@4
|
41 Visual *vis = 0;
|
nuclear@4
|
42 XSetWindowAttributes xattr;
|
nuclear@4
|
43 unsigned int xattr_mask;
|
nuclear@4
|
44 XVisualInfo *vinf, vinf_match;
|
nuclear@4
|
45
|
nuclear@4
|
46 if(!(dpy = XOpenDisplay(0))) {
|
nuclear@4
|
47 fprintf(stderr, "failed to open connection to the X server\n");
|
nuclear@4
|
48 return -1;
|
nuclear@4
|
49 }
|
nuclear@4
|
50 scr = DefaultScreen(dpy);
|
nuclear@4
|
51 root_win = RootWindow(dpy, scr);
|
nuclear@4
|
52
|
nuclear@4
|
53 xa_wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
|
nuclear@4
|
54
|
nuclear@4
|
55 vinf_match.screen = scr;
|
nuclear@4
|
56 vinf_match.depth = 24;
|
nuclear@4
|
57 vinf_match.class = TrueColor;
|
nuclear@4
|
58
|
nuclear@4
|
59 if(!(vinf = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask | VisualClassMask, &vinf_match, &num_visuals))) {
|
nuclear@4
|
60 fprintf(stderr, "failed to retrieve matching visuals\n");
|
nuclear@4
|
61 XCloseDisplay(dpy);
|
nuclear@4
|
62 return -1;
|
nuclear@4
|
63 }
|
nuclear@4
|
64
|
nuclear@4
|
65 for(i=0; i<num_visuals; i++) {
|
nuclear@4
|
66 if(vku_xlib_usable_visual(dpy, vinf[i].visualid)) {
|
nuclear@4
|
67 vis = vinf[i].visual;
|
nuclear@4
|
68 break;
|
nuclear@4
|
69 }
|
nuclear@4
|
70 }
|
nuclear@4
|
71 if(!vis) {
|
nuclear@4
|
72 fprintf(stderr, "failed to find approprate visual\n");
|
nuclear@4
|
73 XFree(vinf);
|
nuclear@4
|
74 XCloseDisplay(dpy);
|
nuclear@4
|
75 return -1;
|
nuclear@4
|
76 }
|
nuclear@4
|
77
|
nuclear@4
|
78 xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
|
nuclear@4
|
79 xattr.colormap = XCreateColormap(dpy, root_win, vis, AllocNone);
|
nuclear@4
|
80 xattr_mask = CWBackPixel | CWBorderPixel | CWColormap;
|
nuclear@4
|
81
|
nuclear@4
|
82 if(!(win = XCreateWindow(dpy, root_win, 0, 0, xsz, ysz, 0, 24, InputOutput, vis, xattr_mask, &xattr))) {
|
nuclear@4
|
83 fprintf(stderr, "failed to create X window\n");
|
nuclear@4
|
84 XFree(vinf);
|
nuclear@4
|
85 XCloseDisplay(dpy);
|
nuclear@4
|
86 return -1;
|
nuclear@4
|
87 }
|
nuclear@4
|
88 XFree(vinf);
|
nuclear@4
|
89
|
nuclear@4
|
90 XSelectInput(dpy, win, evmask);
|
nuclear@4
|
91 XSetWMProtocols(dpy, win, &xa_wm_delete, 1);
|
nuclear@4
|
92
|
nuclear@5
|
93 if(!(surf = vku_xlib_create_surface(dpy, win))) {
|
nuclear@5
|
94 fprintf(stderr, "failed to create vulkan surface for the window\n");
|
nuclear@5
|
95 XDestroyWindow(dpy, win);
|
nuclear@5
|
96 XCloseDisplay(dpy);
|
nuclear@5
|
97 return -1;
|
nuclear@5
|
98 }
|
nuclear@5
|
99 /* swapchain gets created before the first reshape invocation */
|
nuclear@5
|
100
|
nuclear@4
|
101 wsys_set_window_title("X11 window");
|
nuclear@4
|
102 XMapWindow(dpy, win);
|
nuclear@4
|
103
|
nuclear@4
|
104 win_width = xsz;
|
nuclear@4
|
105 win_height = ysz;
|
nuclear@17
|
106
|
nuclear@17
|
107 if(reshape(xsz, ysz) == -1) {
|
nuclear@17
|
108 XDestroyWindow(dpy, win);
|
nuclear@17
|
109 XCloseDisplay(dpy);
|
nuclear@17
|
110 return -1;
|
nuclear@17
|
111 }
|
nuclear@17
|
112
|
nuclear@17
|
113 pending = REDISPLAY;
|
nuclear@4
|
114
|
nuclear@4
|
115 return 0;
|
nuclear@4
|
116 }
|
nuclear@4
|
117
|
nuclear@4
|
118 void wsys_destroy_window(void)
|
nuclear@4
|
119 {
|
nuclear@4
|
120 if(dpy) {
|
nuclear@4
|
121 if(win) {
|
nuclear@4
|
122 XDestroyWindow(dpy, win);
|
nuclear@4
|
123 win = 0;
|
nuclear@4
|
124 }
|
nuclear@4
|
125 XCloseDisplay(dpy);
|
nuclear@4
|
126 dpy = 0;
|
nuclear@4
|
127 }
|
nuclear@4
|
128 }
|
nuclear@4
|
129
|
nuclear@4
|
130 void wsys_get_window_size(int *xsz, int *ysz)
|
nuclear@4
|
131 {
|
nuclear@4
|
132 *xsz = win_width;
|
nuclear@4
|
133 *ysz = win_height;
|
nuclear@4
|
134 }
|
nuclear@4
|
135
|
nuclear@4
|
136 void wsys_set_window_title(const char *title)
|
nuclear@4
|
137 {
|
nuclear@4
|
138 XTextProperty text;
|
nuclear@4
|
139 XStringListToTextProperty((char**)&title, 1, &text);
|
nuclear@4
|
140 XSetWMName(dpy, win, &text);
|
nuclear@4
|
141 XSetWMIconName(dpy, win, &text);
|
nuclear@4
|
142 XFree(text.value);
|
nuclear@4
|
143 }
|
nuclear@4
|
144
|
nuclear@4
|
145 void wsys_display_callback(void (*func)(void))
|
nuclear@4
|
146 {
|
nuclear@4
|
147 cb.display = func;
|
nuclear@4
|
148 }
|
nuclear@4
|
149
|
nuclear@4
|
150 void wsys_reshape_callback(void (*func)(int, int))
|
nuclear@4
|
151 {
|
nuclear@4
|
152 cb.reshape = func;
|
nuclear@4
|
153 }
|
nuclear@4
|
154
|
nuclear@4
|
155 void wsys_keyboard_callback(void (*func)(int, int))
|
nuclear@4
|
156 {
|
nuclear@4
|
157 cb.keyboard = func;
|
nuclear@4
|
158 if(func) {
|
nuclear@4
|
159 evmask |= KeyPressMask | KeyReleaseMask;
|
nuclear@4
|
160 } else {
|
nuclear@4
|
161 evmask &= ~(KeyPressMask | KeyReleaseMask);
|
nuclear@4
|
162 }
|
nuclear@4
|
163 if(win) {
|
nuclear@4
|
164 XSelectInput(dpy, win, evmask);
|
nuclear@4
|
165 }
|
nuclear@4
|
166 }
|
nuclear@4
|
167
|
nuclear@4
|
168 void wsys_mouse_callback(void (*func)(int, int, int, int))
|
nuclear@4
|
169 {
|
nuclear@4
|
170 cb.mouse = func;
|
nuclear@4
|
171 if(func) {
|
nuclear@4
|
172 evmask |= ButtonPressMask | ButtonReleaseMask;
|
nuclear@4
|
173 } else {
|
nuclear@4
|
174 evmask &= ~(ButtonPressMask | ButtonReleaseMask);
|
nuclear@4
|
175 }
|
nuclear@4
|
176 if(win) {
|
nuclear@4
|
177 XSelectInput(dpy, win, evmask);
|
nuclear@4
|
178 }
|
nuclear@4
|
179 }
|
nuclear@4
|
180
|
nuclear@4
|
181 void wsys_motion_callback(void (*func)(int, int))
|
nuclear@4
|
182 {
|
nuclear@4
|
183 cb.motion = func;
|
nuclear@4
|
184 if(func) {
|
nuclear@4
|
185 evmask |= ButtonMotionMask;
|
nuclear@4
|
186 } else {
|
nuclear@4
|
187 evmask &= ~ButtonMotionMask;
|
nuclear@4
|
188 }
|
nuclear@4
|
189 if(win) {
|
nuclear@4
|
190 XSelectInput(dpy, win, evmask);
|
nuclear@4
|
191 }
|
nuclear@4
|
192 }
|
nuclear@4
|
193
|
nuclear@4
|
194 void wsys_passive_motion_callback(void (*func)(int, int))
|
nuclear@4
|
195 {
|
nuclear@4
|
196 cb.passive = func;
|
nuclear@4
|
197 if(func) {
|
nuclear@4
|
198 evmask |= PointerMotionMask;
|
nuclear@4
|
199 } else {
|
nuclear@4
|
200 evmask &= ~PointerMotionMask;
|
nuclear@4
|
201 }
|
nuclear@4
|
202 if(win) {
|
nuclear@4
|
203 XSelectInput(dpy, win, evmask);
|
nuclear@4
|
204 }
|
nuclear@4
|
205 }
|
nuclear@4
|
206
|
nuclear@17
|
207 void wsys_swap_buffers(VkSemaphore sem_wait)
|
nuclear@4
|
208 {
|
nuclear@17
|
209 vku_present(swapchain, next_swapchain_image, sem_wait);
|
nuclear@17
|
210 next_swapchain_image = vku_get_next_image(swapchain, swapchain_getimg_sem);
|
nuclear@4
|
211 }
|
nuclear@4
|
212
|
nuclear@4
|
213 void wsys_redisplay(void)
|
nuclear@4
|
214 {
|
nuclear@4
|
215 pending |= REDISPLAY;
|
nuclear@4
|
216 }
|
nuclear@4
|
217
|
nuclear@4
|
218 void wsys_quit(void)
|
nuclear@4
|
219 {
|
nuclear@4
|
220 pending |= QUIT;
|
nuclear@4
|
221 }
|
nuclear@4
|
222
|
nuclear@17
|
223 static int reshape(int x, int y)
|
nuclear@17
|
224 {
|
nuclear@17
|
225 int i;
|
nuclear@17
|
226 VkSwapchainKHR sc;
|
nuclear@17
|
227 VkFormat fmt = VK_FORMAT_B8G8R8A8_UNORM; /* TODO enumerate and choose */
|
nuclear@17
|
228
|
nuclear@17
|
229 printf("DBG reshape\n");
|
nuclear@17
|
230
|
nuclear@17
|
231 if(!vkrpass) {
|
nuclear@17
|
232 if(!(vkrpass = vku_create_renderpass(fmt, VK_FORMAT_UNDEFINED))) {
|
nuclear@17
|
233 abort();
|
nuclear@17
|
234 }
|
nuclear@17
|
235 }
|
nuclear@17
|
236
|
nuclear@17
|
237 if(!(sc = vku_create_swapchain(surf, x, y, 2, fmt,
|
nuclear@17
|
238 VK_PRESENT_MODE_FIFO_KHR, swapchain))) {
|
nuclear@17
|
239 fprintf(stderr, "Failed to create %dx%d double-buffered swapchain\n", x, y);
|
nuclear@17
|
240 return -1;
|
nuclear@17
|
241 }
|
nuclear@17
|
242 swapchain = sc;
|
nuclear@17
|
243
|
nuclear@17
|
244 free(swapchain_images);
|
nuclear@17
|
245 swapchain_size = 2;
|
nuclear@17
|
246 swapchain_images = vku_get_swapchain_images(sc, 0);
|
nuclear@17
|
247 next_swapchain_image = vku_get_next_image(swapchain, swapchain_getimg_sem);
|
nuclear@17
|
248
|
nuclear@17
|
249 if(!swapchain_views) {
|
nuclear@17
|
250 if(!(swapchain_views = calloc(swapchain_size, sizeof *swapchain_views))) {
|
nuclear@17
|
251 fprintf(stderr, "Failed to allocate image views\n");
|
nuclear@17
|
252 return -1;
|
nuclear@17
|
253 }
|
nuclear@17
|
254 }
|
nuclear@17
|
255
|
nuclear@17
|
256 for(i=0; i<swapchain_size; i++) {
|
nuclear@17
|
257 if(swapchain_views[i]) {
|
nuclear@17
|
258 vku_destroy_view(swapchain_views[i]);
|
nuclear@17
|
259 }
|
nuclear@17
|
260 swapchain_views[i] = vku_create_view(swapchain_images[i], fmt);
|
nuclear@17
|
261 }
|
nuclear@17
|
262
|
nuclear@17
|
263 if(!swapchain_framebuf) {
|
nuclear@17
|
264 if(!(swapchain_framebuf = calloc(swapchain_size, sizeof *swapchain_framebuf))) {
|
nuclear@17
|
265 fprintf(stderr, "Failed to allocate framebuffers\n");
|
nuclear@17
|
266 return -1;
|
nuclear@17
|
267 }
|
nuclear@17
|
268 }
|
nuclear@17
|
269
|
nuclear@17
|
270 for(i=0; i<swapchain_size; i++) {
|
nuclear@17
|
271 if(swapchain_framebuf[i]) {
|
nuclear@17
|
272 vku_destroy_framebuffer(swapchain_framebuf[i]);
|
nuclear@17
|
273 }
|
nuclear@17
|
274 swapchain_framebuf[i] = vku_create_framebuffer(swapchain_views[i], x, y, vkrpass);
|
nuclear@17
|
275 }
|
nuclear@17
|
276
|
nuclear@17
|
277 if(cb.reshape) {
|
nuclear@17
|
278 cb.reshape(x, y);
|
nuclear@17
|
279 }
|
nuclear@17
|
280 return 0;
|
nuclear@17
|
281 }
|
nuclear@17
|
282
|
nuclear@4
|
283 int wsys_process_events(int mode)
|
nuclear@4
|
284 {
|
nuclear@4
|
285 XEvent xev;
|
nuclear@4
|
286
|
nuclear@4
|
287 if(pending & RESHAPE) {
|
nuclear@17
|
288 if(reshape(win_width, win_height) == -1) {
|
nuclear@5
|
289 return -1;
|
nuclear@5
|
290 }
|
nuclear@4
|
291 pending &= ~RESHAPE;
|
nuclear@4
|
292 }
|
nuclear@4
|
293
|
nuclear@4
|
294 if(mode == WSYS_BLOCKING) {
|
nuclear@4
|
295 XNextEvent(dpy, &xev);
|
nuclear@4
|
296 proc_event(&xev);
|
nuclear@4
|
297 if(pending & QUIT) return -1;
|
nuclear@4
|
298 }
|
nuclear@4
|
299
|
nuclear@4
|
300 while(XPending(dpy)) {
|
nuclear@4
|
301 XNextEvent(dpy, &xev);
|
nuclear@4
|
302 proc_event(&xev);
|
nuclear@4
|
303 if(pending & QUIT) return -1;
|
nuclear@4
|
304 }
|
nuclear@4
|
305
|
nuclear@4
|
306 if(pending & REDISPLAY && win_mapped) {
|
nuclear@4
|
307 pending &= ~REDISPLAY;
|
nuclear@4
|
308 if(cb.display) {
|
nuclear@4
|
309 cb.display();
|
nuclear@4
|
310 }
|
nuclear@4
|
311 }
|
nuclear@4
|
312 return 0;
|
nuclear@4
|
313 }
|
nuclear@4
|
314
|
nuclear@4
|
315 static void proc_event(XEvent *ev)
|
nuclear@4
|
316 {
|
nuclear@4
|
317 switch(ev->type) {
|
nuclear@4
|
318 case MapNotify:
|
nuclear@4
|
319 win_mapped = 1;
|
nuclear@4
|
320 break;
|
nuclear@4
|
321
|
nuclear@4
|
322 case UnmapNotify:
|
nuclear@4
|
323 win_mapped = 0;
|
nuclear@4
|
324 break;
|
nuclear@4
|
325
|
nuclear@4
|
326 case ClientMessage:
|
nuclear@4
|
327 if(ev->xclient.data.l[0] == xa_wm_delete) {
|
nuclear@4
|
328 pending |= QUIT;
|
nuclear@4
|
329 }
|
nuclear@4
|
330 break;
|
nuclear@4
|
331
|
nuclear@4
|
332 case ConfigureNotify:
|
nuclear@4
|
333 if(ev->xconfigure.width != win_width || ev->xconfigure.height != win_height) {
|
nuclear@4
|
334 win_width = ev->xconfigure.width;
|
nuclear@4
|
335 win_height = ev->xconfigure.height;
|
nuclear@4
|
336 pending |= RESHAPE;
|
nuclear@4
|
337 }
|
nuclear@4
|
338 break;
|
nuclear@4
|
339
|
nuclear@4
|
340 case Expose:
|
nuclear@4
|
341 if(ev->xexpose.count == 0) {
|
nuclear@4
|
342 pending |= REDISPLAY;
|
nuclear@4
|
343 }
|
nuclear@4
|
344 break;
|
nuclear@4
|
345
|
nuclear@4
|
346 case KeyPress:
|
nuclear@4
|
347 case KeyRelease:
|
nuclear@4
|
348 if(cb.keyboard) {
|
nuclear@4
|
349 KeySym sym;
|
nuclear@4
|
350 char str[16];
|
nuclear@4
|
351 XLookupString(&ev->xkey, str, sizeof str, &sym, 0);
|
nuclear@4
|
352 cb.keyboard(sym & 0xff, ev->type == KeyPress ? 1 : 0);
|
nuclear@4
|
353 }
|
nuclear@4
|
354 break;
|
nuclear@4
|
355
|
nuclear@4
|
356 case ButtonPress:
|
nuclear@4
|
357 case ButtonRelease:
|
nuclear@4
|
358 if(cb.mouse) {
|
nuclear@4
|
359 int bn = ev->xbutton.button - Button1;
|
nuclear@4
|
360 int pressed = ev->type == ButtonPress ? 1 : 0;
|
nuclear@4
|
361 cb.mouse(bn, pressed, ev->xbutton.x, ev->xbutton.y);
|
nuclear@4
|
362 }
|
nuclear@4
|
363 break;
|
nuclear@4
|
364
|
nuclear@4
|
365 case MotionNotify:
|
nuclear@4
|
366 if(ev->xmotion.state & 0x1f00) {
|
nuclear@4
|
367 if(cb.motion) {
|
nuclear@4
|
368 cb.motion(ev->xmotion.x, ev->xmotion.y);
|
nuclear@4
|
369 }
|
nuclear@4
|
370 } else {
|
nuclear@4
|
371 if(cb.passive) {
|
nuclear@4
|
372 cb.passive(ev->xmotion.x, ev->xmotion.y);
|
nuclear@4
|
373 }
|
nuclear@4
|
374 }
|
nuclear@4
|
375 break;
|
nuclear@4
|
376 }
|
nuclear@4
|
377 }
|