some meshgen and map rendering updates
[voxelands-alt.git] / src / graphics / wm_x11.c
blobb5940d8d7028a1d2d4a9122ece07017bc105cf79
1 /************************************************************************
2 * wm_x11.c
3 * voxelands - 3d voxel world sandbox game
4 * Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>
18 ************************************************************************/
20 #include "common.h"
21 #define _WM_EXPOSE_ALL
22 #include "wm.h"
23 #include "graphics.h"
25 #ifndef WIN32
27 #include <X11/Xatom.h>
29 #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
30 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
31 typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
33 /* attributes for a double buffered visual in RGBA format with at least
34 * 4 bits per color and a 16 bit depth buffer */
35 static int attr_list[] = {
36 GLX_RENDER_TYPE, GLX_RGBA_BIT,
37 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
38 GLX_DOUBLEBUFFER, True,
39 GLX_X_RENDERABLE, True,
40 GLX_RED_SIZE, 4,
41 GLX_GREEN_SIZE, 4,
42 GLX_BLUE_SIZE, 4,
43 GLX_ALPHA_SIZE, 4,
44 GLX_DEPTH_SIZE, 16,
45 None
48 static XIMStyle style_shoose_best(XIMStyle style1, XIMStyle style2)
50 XIMStyle s,t;
51 XIMStyle preedit = XIMPreeditArea | XIMPreeditCallbacks |
52 XIMPreeditPosition | XIMPreeditNothing | XIMPreeditNone;
53 XIMStyle status = XIMStatusArea | XIMStatusCallbacks |
54 XIMStatusNothing | XIMStatusNone;
56 if (style1 == 0)
57 return style2;
58 if (style2 == 0)
59 return style1;
60 if ((style1 & (preedit | status)) == (style2 & (preedit | status)))
61 return style1;
63 s = style1 & preedit;
64 t = style2 & preedit;
65 if (s != t) {
66 if (s | t | XIMPreeditCallbacks) {
67 return (s == XIMPreeditCallbacks)?style1:style2;
68 }else if (s | t | XIMPreeditPosition) {
69 return (s == XIMPreeditPosition)?style1:style2;
70 }else if (s | t | XIMPreeditArea) {
71 return (s == XIMPreeditArea)?style1:style2;
72 }else if (s | t | XIMPreeditNothing) {
73 return (s == XIMPreeditNothing)?style1:style2;
75 }else{ /* if preedit flags are the same, compare status flags */
76 s = style1 & status;
77 t = style2 & status;
78 if (s | t | XIMStatusCallbacks) {
79 return (s == XIMStatusCallbacks)?style1:style2;
80 }else if (s | t | XIMStatusArea) {
81 return (s == XIMStatusArea)?style1:style2;
82 }else if (s | t | XIMStatusNothing) {
83 return (s == XIMStatusNothing)?style1:style2;
86 return style1;
89 /* initialise the game window */
90 int wm_init()
92 int min;
93 int max;
94 char* glXv;
96 wm_data.isinit = 1;
98 /* get a connection */
99 wm_data.dpy = XOpenDisplay(0);
100 wm_data.screen = DefaultScreen(wm_data.dpy);
101 wm_data.fb_cfg = NULL;
102 wm_data.cursor.mat = NULL;
104 wm_data.lfps_i = 0;
105 wm_data.lfps[0] = 0;
106 wm_data.lfps[1] = 0;
107 wm_data.lfps[2] = 0;
108 wm_data.lfps[3] = 0;
110 if (XSetLocaleModifiers("") == NULL)
111 return 1;
113 /* get a connection */
114 XF86VidModeQueryVersion(wm_data.dpy, &max, &min);
115 vlprintf(CN_INFO, "XF86VidModeExtension version: %d.%d", max, min);
116 XF86VidModeGetAllModeLines(wm_data.dpy, wm_data.screen, &wm_data.mode_count, &wm_data.modes);
118 /* save desktop resolution before switching modes */
119 wm_data.deskMode = *wm_data.modes[0];
121 glXv = (char*)glXGetClientString(wm_data.dpy, GLX_VERSION);
122 vlprintf(CN_INFO, "glX version: %s",glXv,max,min);
124 return wm_create();
127 /* exit the game window */
128 void wm_exit()
130 wm_destroy();
131 XFree(wm_data.modes);
132 XCloseDisplay(wm_data.dpy);
135 /* create a window */
136 int wm_create()
138 Colormap cmap;
139 Atom wmDelete;
140 Window winDummy;
141 unsigned int borderDummy;
142 int x;
143 int y;
144 unsigned int w;
145 unsigned int h;
146 unsigned int d;
147 int i;
148 glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
149 XIMStyles *im_supported_styles;
150 XIMStyle app_supported_styles;
151 XIMStyle style;
152 XVaNestedList list;
153 int im_event_mask;
155 if (!wm_data.fb_cfg) {
156 GLXFBConfig *fb_configs = NULL;
157 int num_fb_configs = 0;
159 fb_configs = glXChooseFBConfig(wm_data.dpy, wm_data.screen, attr_list, &num_fb_configs);
160 if (!fb_configs || !num_fb_configs) {
161 fb_configs = glXGetFBConfigs(wm_data.dpy, wm_data.screen, &num_fb_configs);
162 if (!fb_configs || !num_fb_configs)
163 return 1;
165 wm_data.fb_cfg = fb_configs[0];
166 glXGetFBConfigAttrib(wm_data.dpy, wm_data.fb_cfg, GLX_DOUBLEBUFFER, &wm_data.dblbuff);
167 wm_data.vi = glXGetVisualFromFBConfig(wm_data.dpy, wm_data.fb_cfg);
169 glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );
171 if (!glXCreateContextAttribsARB) {
172 wm_data.ctx = glXCreateNewContext(wm_data.dpy, wm_data.fb_cfg, GLX_RGBA_TYPE, 0, True);
173 }else{
174 int context_attribs[] = {
175 GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
176 GLX_CONTEXT_MINOR_VERSION_ARB, 3,
177 GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
178 GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
179 None
181 wm_data.ctx = glXCreateContextAttribsARB(wm_data.dpy, wm_data.fb_cfg, 0, True, context_attribs);
183 if (!wm_data.ctx)
184 return 1;
187 /* set best mode to current */
188 wm_data.mode = 0;
189 /* look for mode with requested resolution */
190 for (i=0; i<wm_data.mode_count; i++) {
191 if ((wm_data.modes[i]->hdisplay == wm_data.size.width) && (wm_data.modes[i]->vdisplay == wm_data.size.height)) {
192 wm_data.mode = i;
193 break;
197 /* create a color map */
198 cmap = XCreateColormap(
199 wm_data.dpy,
200 RootWindow(wm_data.dpy, wm_data.vi->screen),
201 wm_data.vi->visual,
202 AllocNone
205 wm_data.attr.colormap = cmap;
206 wm_data.attr.border_pixel = 0;
207 wm_data.attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask;
209 if (wm_data.fullscreen) {
210 XF86VidModeSwitchToMode(wm_data.dpy, wm_data.screen, wm_data.modes[wm_data.mode]);
211 XF86VidModeSetViewPort(wm_data.dpy, wm_data.screen, 0, 0);
213 /* create a fullscreen window */
214 wm_data.attr.override_redirect = True;
215 wm_data.win = XCreateWindow(
216 wm_data.dpy,
217 RootWindow(wm_data.dpy, wm_data.vi->screen),
220 wm_data.modes[wm_data.mode]->hdisplay,
221 wm_data.modes[wm_data.mode]->vdisplay,
223 wm_data.vi->depth,
224 InputOutput,
225 wm_data.vi->visual,
226 CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect,
227 &wm_data.attr
229 }else{
230 /* create a window */
231 wm_data.win = XCreateWindow(
232 wm_data.dpy,
233 RootWindow(wm_data.dpy, wm_data.vi->screen),
236 wm_data.size.width,
237 wm_data.size.height,
239 wm_data.vi->depth,
240 InputOutput,
241 wm_data.vi->visual,
242 CWBorderPixel | CWColormap | CWEventMask,
243 &wm_data.attr
246 /* handle wm_delete_events */
247 wmDelete = XInternAtom(wm_data.dpy, "WM_DELETE_WINDOW", True);
248 XSetWMProtocols(wm_data.dpy, wm_data.win, &wmDelete, 1);
251 wm_data.im = XOpenIM(wm_data.dpy, NULL, NULL, NULL);
252 if (wm_data.im == NULL)
253 return 1;
255 app_supported_styles = XIMPreeditNone | XIMPreeditNothing | XIMPreeditArea;
256 app_supported_styles |= XIMStatusNone | XIMStatusNothing | XIMStatusArea;
258 /* figure out which styles the IM can support */
259 XGetIMValues(wm_data.im, XNQueryInputStyle, &im_supported_styles, NULL);
261 /* find the best IM */
262 wm_data.style = 0;
263 for(i=0; i < im_supported_styles->count_styles; i++) {
264 style = im_supported_styles->supported_styles[i];
265 if ((style & app_supported_styles) == style) /* if we can handle it */
266 wm_data.style = style_shoose_best(style, wm_data.style);
270 /* ... or not */
271 if (wm_data.style == 0)
272 return 1;
274 /* create an IC from the IM, this is needed for unicode support */
275 list = XVaCreateNestedList(0,NULL);
276 wm_data.ic = XCreateIC(
277 wm_data.im,
278 XNInputStyle, wm_data.style,
279 XNClientWindow, wm_data.win,
280 NULL
282 XFree(list);
284 if (wm_data.ic == NULL)
285 return 1;
287 XGetICValues(wm_data.ic, XNFilterEvents, &im_event_mask, NULL);
288 XSelectInput(wm_data.dpy, wm_data.win, ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | im_event_mask);
290 XSetICFocus(wm_data.ic);
292 if (wm_data.fullscreen) {
293 XWarpPointer(wm_data.dpy, None, wm_data.win, 0, 0, 0, 0, 0, 0);
294 XMapRaised(wm_data.dpy, wm_data.win);
295 XGrabKeyboard(wm_data.dpy, wm_data.win, True, GrabModeAsync, GrabModeAsync, CurrentTime);
296 XGrabPointer(wm_data.dpy, wm_data.win, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, wm_data.win, None, CurrentTime);
297 }else{
298 XSizeHints *hints;
299 XMapRaised(wm_data.dpy, wm_data.win);
301 /* set window title */
302 XStoreName(wm_data.dpy, wm_data.win, wm_data.title);
304 /* prevent window resizing */
305 hints = XAllocSizeHints();
306 hints->min_width = hints->max_width = wm_data.size.width;
307 hints->min_height = hints->max_height = wm_data.size.height;
308 hints->flags = PMinSize | PMaxSize;
309 XSetWMNormalHints(wm_data.dpy, wm_data.win, hints);
310 XFree(hints);
313 wm_data.glxwin = glXCreateWindow(wm_data.dpy, wm_data.fb_cfg, wm_data.win, NULL);
315 /* make OpenGL context current */
316 if (!glXMakeContextCurrent(wm_data.dpy, wm_data.glxwin, wm_data.glxwin, wm_data.ctx)) {
317 glXDestroyContext(wm_data.dpy, wm_data.ctx);
318 return 1;
321 XGetGeometry(
322 wm_data.dpy,
323 wm_data.win,
324 &winDummy,
329 &borderDummy,
333 wm_data.size.width = (int)w;
334 wm_data.size.height = (int)h;
336 if (glXIsDirect(wm_data.dpy, wm_data.ctx)) {
337 int major;
338 int minor;
339 char* db = "";
340 glGetIntegerv(GL_MAJOR_VERSION, &major);
341 glGetIntegerv(GL_MINOR_VERSION, &minor);
343 if (wm_data.dblbuff)
344 db = "Double Buffered ";
346 vlprintf(CN_INFO, "%sDirect Rendering: OpenGL %d.%d",db,major,minor);
349 /* MUST be done, else rendering gets messed up */
350 render_set_projection_matrix(NULL);
352 return 0;
355 /* resize the screen, fullscreen or windowed */
356 int wm_resize()
358 if (!wm_data.isinit)
359 return 0;
361 XResizeWindow(wm_data.dpy, wm_data.win, wm_data.size.width, wm_data.size.height);
363 /* MUST be done after a resize, else rendering gets messed up */
364 render_set_projection_matrix(NULL);
366 return 0;
369 /* flush graphics through and flip buffers */
370 int wm_update()
372 glFlush();
373 /* update the screen */
374 if (wm_data.dblbuff)
375 glXSwapBuffers(wm_data.dpy, wm_data.glxwin);
376 return 0;
379 /* destroy the current window */
380 void wm_destroy()
382 if (wm_data.ctx) {
383 glXMakeContextCurrent(wm_data.dpy, None, None, NULL);
385 glXDestroyContext(wm_data.dpy, wm_data.ctx);
386 wm_data.ctx = NULL;
387 glXDestroyWindow(wm_data.dpy,wm_data.glxwin);
389 /* switch back to original desktop resolution if we were in fs */
390 if (wm_data.fullscreen) {
391 XF86VidModeSwitchToMode(wm_data.dpy, wm_data.screen, &wm_data.deskMode);
392 XF86VidModeSetViewPort(wm_data.dpy, wm_data.screen, 0, 0);
397 /* set fullscreen on/off */
398 void wm_toggle_fullscreen(int fs)
400 if (fs == wm_data.fullscreen)
401 return;
403 if (!wm_data.isinit) {
404 wm_data.fullscreen = fs;
405 return;
407 if (wm_data.ctx) {
408 glXMakeContextCurrent(wm_data.dpy, None, None, NULL);
409 glXDestroyWindow(wm_data.dpy,wm_data.glxwin);
411 /* switch back to original desktop resolution if we were in fs */
412 if (wm_data.fullscreen) {
413 XF86VidModeSwitchToMode(wm_data.dpy, wm_data.screen, &wm_data.deskMode);
414 XF86VidModeSetViewPort(wm_data.dpy, wm_data.screen, 0, 0);
416 wm_data.fullscreen = fs;
417 /* TODO: this isn't working in glX */
418 wm_create();
421 /* use file as a cursor texture */
422 void wm_cursor(char* file, int width, int height, int offset_x, int offset_y)
424 Cursor invisibleCursor;
425 Pixmap bitmapNoData;
426 XColor black;
427 static char noData[] = { 0,0,0,0,0,0,0,0 };
429 if (!file) {
430 if (!wm_data.cursor.mat)
431 return;
433 wm_data.cursor.mat = NULL;
434 XUndefineCursor(wm_data.dpy, wm_data.win);
436 return;
439 wm_data.cursor.mat = mat_from_image("texture",file);
440 wm_data.cursor.w = width;
441 wm_data.cursor.h = height;
442 wm_data.cursor.x = offset_x;
443 wm_data.cursor.y = offset_y;
445 if (!wm_data.cursor.mat)
446 return;
448 black.red = 0;
449 black.green = 0;
450 black.blue = 0;
452 bitmapNoData = XCreateBitmapFromData(wm_data.dpy, wm_data.win, noData, 8, 8);
453 invisibleCursor = XCreatePixmapCursor(wm_data.dpy, bitmapNoData, bitmapNoData, &black, &black, 0, 0);
454 XDefineCursor(wm_data.dpy, wm_data.win, invisibleCursor);
455 XFreeCursor(wm_data.dpy, invisibleCursor);
458 /* grab the mouse */
459 void wm_grab()
461 Cursor invisibleCursor;
462 Pixmap bitmapNoData;
463 XColor black;
464 static char noData[] = { 0,0,0,0,0,0,0,0 };
466 if (events_get_mousegrab())
467 return;
469 black.red = 0;
470 black.green = 0;
471 black.blue = 0;
473 bitmapNoData = XCreateBitmapFromData(wm_data.dpy, wm_data.win, noData, 8, 8);
474 invisibleCursor = XCreatePixmapCursor(wm_data.dpy, bitmapNoData, bitmapNoData, &black, &black, 0, 0);
475 XDefineCursor(wm_data.dpy, wm_data.win, invisibleCursor);
476 XFreeCursor(wm_data.dpy, invisibleCursor);
478 if (!wm_data.fullscreen)
479 XGrabPointer(wm_data.dpy, wm_data.win, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, wm_data.win, None, CurrentTime);
481 events_set_mousegrab(1);
484 /* stop grabbing the mouse */
485 void wm_ungrab()
487 if (!events_get_mousegrab())
488 return;
490 XUndefineCursor(wm_data.dpy, wm_data.win);
491 if (!wm_data.fullscreen)
492 XUngrabPointer(wm_data.dpy, CurrentTime);
494 events_set_mousegrab(0);
497 /* set the window title */
498 void wm_title(char* title)
500 if (title) {
501 if (wm_data.title)
502 free(wm_data.title);
503 wm_data.title = strdup(title);
505 if (!wm_data.isinit)
506 return;
508 XStoreName(wm_data.dpy, wm_data.win, wm_data.title);
511 #endif