1 /************************************************************************
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 ************************************************************************/
21 #define _WM_EXPOSE_ALL
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
,
48 static XIMStyle
style_shoose_best(XIMStyle style1
, XIMStyle style2
)
51 XIMStyle preedit
= XIMPreeditArea
| XIMPreeditCallbacks
|
52 XIMPreeditPosition
| XIMPreeditNothing
| XIMPreeditNone
;
53 XIMStyle status
= XIMStatusArea
| XIMStatusCallbacks
|
54 XIMStatusNothing
| XIMStatusNone
;
60 if ((style1
& (preedit
| status
)) == (style2
& (preedit
| status
)))
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 */
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
;
89 /* initialise the game window */
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
;
110 if (XSetLocaleModifiers("") == NULL
)
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
);
127 /* exit the game window */
131 XFree(wm_data
.modes
);
132 XCloseDisplay(wm_data
.dpy
);
135 /* create a window */
141 unsigned int borderDummy
;
148 glXCreateContextAttribsARBProc glXCreateContextAttribsARB
= 0;
149 XIMStyles
*im_supported_styles
;
150 XIMStyle app_supported_styles
;
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
)
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
);
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
,
181 wm_data
.ctx
= glXCreateContextAttribsARB(wm_data
.dpy
, wm_data
.fb_cfg
, 0, True
, context_attribs
);
187 /* set best mode to current */
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
)) {
197 /* create a color map */
198 cmap
= XCreateColormap(
200 RootWindow(wm_data
.dpy
, wm_data
.vi
->screen
),
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(
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
,
226 CWBorderPixel
| CWColormap
| CWEventMask
| CWOverrideRedirect
,
230 /* create a window */
231 wm_data
.win
= XCreateWindow(
233 RootWindow(wm_data
.dpy
, wm_data
.vi
->screen
),
242 CWBorderPixel
| CWColormap
| CWEventMask
,
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
)
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 */
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
);
271 if (wm_data
.style
== 0)
274 /* create an IC from the IM, this is needed for unicode support */
275 list
= XVaCreateNestedList(0,NULL
);
276 wm_data
.ic
= XCreateIC(
278 XNInputStyle
, wm_data
.style
,
279 XNClientWindow
, wm_data
.win
,
284 if (wm_data
.ic
== NULL
)
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
);
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
);
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
);
333 wm_data
.size
.width
= (int)w
;
334 wm_data
.size
.height
= (int)h
;
336 if (glXIsDirect(wm_data
.dpy
, wm_data
.ctx
)) {
340 glGetIntegerv(GL_MAJOR_VERSION
, &major
);
341 glGetIntegerv(GL_MINOR_VERSION
, &minor
);
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
);
355 /* resize the screen, fullscreen or windowed */
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
);
369 /* flush graphics through and flip buffers */
373 /* update the screen */
375 glXSwapBuffers(wm_data
.dpy
, wm_data
.glxwin
);
379 /* destroy the current window */
383 glXMakeContextCurrent(wm_data
.dpy
, None
, None
, NULL
);
385 glXDestroyContext(wm_data
.dpy
, wm_data
.ctx
);
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
)
403 if (!wm_data
.isinit
) {
404 wm_data
.fullscreen
= fs
;
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 */
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
;
427 static char noData
[] = { 0,0,0,0,0,0,0,0 };
430 if (!wm_data
.cursor
.mat
)
433 wm_data
.cursor
.mat
= NULL
;
434 XUndefineCursor(wm_data
.dpy
, wm_data
.win
);
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
)
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
);
461 Cursor invisibleCursor
;
464 static char noData
[] = { 0,0,0,0,0,0,0,0 };
466 if (events_get_mousegrab())
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 */
487 if (!events_get_mousegrab())
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
)
503 wm_data
.title
= strdup(title
);
508 XStoreName(wm_data
.dpy
, wm_data
.win
, wm_data
.title
);