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
;
104 if (XSetLocaleModifiers("") == NULL
)
107 /* get a connection */
108 XF86VidModeQueryVersion(wm_data
.dpy
, &max
, &min
);
109 vlprintf(CN_INFO
"XF86VidModeExtension version: %d.%d", max
, min
);
110 XF86VidModeGetAllModeLines(wm_data
.dpy
, wm_data
.screen
, &wm_data
.mode_count
, &wm_data
.modes
);
112 /* save desktop resolution before switching modes */
113 wm_data
.deskMode
= *wm_data
.modes
[0];
115 glXv
= (char*)glXGetClientString(wm_data
.dpy
, GLX_VERSION
);
116 vlprintf(CN_INFO
"glX version: %s",glXv
,max
,min
);
121 /* exit the game window */
125 XFree(wm_data
.modes
);
126 XCloseDisplay(wm_data
.dpy
);
129 /* create a window */
135 unsigned int borderDummy
;
142 glXCreateContextAttribsARBProc glXCreateContextAttribsARB
= 0;
143 XIMStyles
*im_supported_styles
;
144 XIMStyle app_supported_styles
;
149 if (!wm_data
.fb_cfg
) {
150 GLXFBConfig
*fb_configs
= NULL
;
151 int num_fb_configs
= 0;
153 fb_configs
= glXChooseFBConfig(wm_data
.dpy
, wm_data
.screen
, attr_list
, &num_fb_configs
);
154 if (!fb_configs
|| !num_fb_configs
) {
155 fb_configs
= glXGetFBConfigs(wm_data
.dpy
, wm_data
.screen
, &num_fb_configs
);
156 if (!fb_configs
|| !num_fb_configs
)
159 wm_data
.fb_cfg
= fb_configs
[0];
160 glXGetFBConfigAttrib(wm_data
.dpy
, wm_data
.fb_cfg
, GLX_DOUBLEBUFFER
, &wm_data
.dblbuff
);
161 wm_data
.vi
= glXGetVisualFromFBConfig(wm_data
.dpy
, wm_data
.fb_cfg
);
163 glXCreateContextAttribsARB
= (glXCreateContextAttribsARBProc
)glXGetProcAddressARB( (const GLubyte
*) "glXCreateContextAttribsARB" );
165 if (!glXCreateContextAttribsARB
) {
166 wm_data
.ctx
= glXCreateNewContext(wm_data
.dpy
, wm_data
.fb_cfg
, GLX_RGBA_TYPE
, 0, True
);
168 int context_attribs
[] = {
169 GLX_CONTEXT_MAJOR_VERSION_ARB
, 3,
170 GLX_CONTEXT_MINOR_VERSION_ARB
, 3,
171 GLX_CONTEXT_FLAGS_ARB
, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
,
172 GLX_CONTEXT_FLAGS_ARB
, GLX_CONTEXT_CORE_PROFILE_BIT_ARB
,
175 wm_data
.ctx
= glXCreateContextAttribsARB(wm_data
.dpy
, wm_data
.fb_cfg
, 0, True
, context_attribs
);
181 /* set best mode to current */
183 /* look for mode with requested resolution */
184 for (i
=0; i
<wm_data
.mode_count
; i
++) {
185 if ((wm_data
.modes
[i
]->hdisplay
== wm_data
.size
.width
) && (wm_data
.modes
[i
]->vdisplay
== wm_data
.size
.height
)) {
191 /* create a color map */
192 cmap
= XCreateColormap(
194 RootWindow(wm_data
.dpy
, wm_data
.vi
->screen
),
199 wm_data
.attr
.colormap
= cmap
;
200 wm_data
.attr
.border_pixel
= 0;
201 wm_data
.attr
.event_mask
= ExposureMask
| KeyPressMask
| KeyReleaseMask
| ButtonPressMask
| ButtonReleaseMask
| PointerMotionMask
| StructureNotifyMask
;
203 if (wm_data
.fullscreen
) {
204 XF86VidModeSwitchToMode(wm_data
.dpy
, wm_data
.screen
, wm_data
.modes
[wm_data
.mode
]);
205 XF86VidModeSetViewPort(wm_data
.dpy
, wm_data
.screen
, 0, 0);
207 /* create a fullscreen window */
208 wm_data
.attr
.override_redirect
= True
;
209 wm_data
.win
= XCreateWindow(
211 RootWindow(wm_data
.dpy
, wm_data
.vi
->screen
),
214 wm_data
.modes
[wm_data
.mode
]->hdisplay
,
215 wm_data
.modes
[wm_data
.mode
]->vdisplay
,
220 CWBorderPixel
| CWColormap
| CWEventMask
| CWOverrideRedirect
,
224 /* create a window */
225 wm_data
.win
= XCreateWindow(
227 RootWindow(wm_data
.dpy
, wm_data
.vi
->screen
),
236 CWBorderPixel
| CWColormap
| CWEventMask
,
240 /* handle wm_delete_events */
241 wmDelete
= XInternAtom(wm_data
.dpy
, "WM_DELETE_WINDOW", True
);
242 XSetWMProtocols(wm_data
.dpy
, wm_data
.win
, &wmDelete
, 1);
245 wm_data
.im
= XOpenIM(wm_data
.dpy
, NULL
, NULL
, NULL
);
246 if (wm_data
.im
== NULL
)
249 app_supported_styles
= XIMPreeditNone
| XIMPreeditNothing
| XIMPreeditArea
;
250 app_supported_styles
|= XIMStatusNone
| XIMStatusNothing
| XIMStatusArea
;
252 /* figure out which styles the IM can support */
253 XGetIMValues(wm_data
.im
, XNQueryInputStyle
, &im_supported_styles
, NULL
);
255 /* find the best IM */
257 for(i
=0; i
< im_supported_styles
->count_styles
; i
++) {
258 style
= im_supported_styles
->supported_styles
[i
];
259 if ((style
& app_supported_styles
) == style
) /* if we can handle it */
260 wm_data
.style
= style_shoose_best(style
, wm_data
.style
);
265 if (wm_data
.style
== 0)
268 /* create an IC from the IM, this is needed for unicode support */
269 list
= XVaCreateNestedList(0,NULL
);
270 wm_data
.ic
= XCreateIC(
272 XNInputStyle
, wm_data
.style
,
273 XNClientWindow
, wm_data
.win
,
278 if (wm_data
.ic
== NULL
)
281 XGetICValues(wm_data
.ic
, XNFilterEvents
, &im_event_mask
, NULL
);
282 XSelectInput(wm_data
.dpy
, wm_data
.win
, ExposureMask
| KeyPressMask
| KeyReleaseMask
| ButtonPressMask
| ButtonReleaseMask
| PointerMotionMask
| StructureNotifyMask
| im_event_mask
);
284 XSetICFocus(wm_data
.ic
);
286 if (wm_data
.fullscreen
) {
287 XWarpPointer(wm_data
.dpy
, None
, wm_data
.win
, 0, 0, 0, 0, 0, 0);
288 XMapRaised(wm_data
.dpy
, wm_data
.win
);
289 XGrabKeyboard(wm_data
.dpy
, wm_data
.win
, True
, GrabModeAsync
, GrabModeAsync
, CurrentTime
);
290 XGrabPointer(wm_data
.dpy
, wm_data
.win
, True
, ButtonPressMask
, GrabModeAsync
, GrabModeAsync
, wm_data
.win
, None
, CurrentTime
);
293 XMapRaised(wm_data
.dpy
, wm_data
.win
);
295 /* set window title */
296 XStoreName(wm_data
.dpy
, wm_data
.win
, wm_data
.title
);
298 /* prevent window resizing */
299 hints
= XAllocSizeHints();
300 hints
->min_width
= hints
->max_width
= wm_data
.size
.width
;
301 hints
->min_height
= hints
->max_height
= wm_data
.size
.height
;
302 hints
->flags
= PMinSize
| PMaxSize
;
303 XSetWMNormalHints(wm_data
.dpy
, wm_data
.win
, hints
);
307 wm_data
.glxwin
= glXCreateWindow(wm_data
.dpy
, wm_data
.fb_cfg
, wm_data
.win
, NULL
);
309 /* make OpenGL context current */
310 if (!glXMakeContextCurrent(wm_data
.dpy
, wm_data
.glxwin
, wm_data
.glxwin
, wm_data
.ctx
)) {
311 glXDestroyContext(wm_data
.dpy
, wm_data
.ctx
);
327 wm_data
.size
.width
= (int)w
;
328 wm_data
.size
.height
= (int)h
;
330 if (glXIsDirect(wm_data
.dpy
, wm_data
.ctx
)) {
334 glGetIntegerv(GL_MAJOR_VERSION
, &major
);
335 glGetIntegerv(GL_MINOR_VERSION
, &minor
);
338 db
= "Double Buffered ";
340 vlprintf(CN_INFO
"%sDirect Rendering: OpenGL %d.%d",db
,major
,minor
);
343 /* MUST be done, else rendering gets messed up */
344 render3d_set_projection_matrix();
349 /* resize the screen, fullscreen or windowed */
355 XResizeWindow(wm_data
.dpy
, wm_data
.win
, wm_data
.size
.width
, wm_data
.size
.height
);
357 /* MUST be done after a resize, else rendering gets messed up */
358 render3d_set_projection_matrix();
363 /* flush graphics through and flip buffers */
367 /* update the screen */
369 glXSwapBuffers(wm_data
.dpy
, wm_data
.glxwin
);
373 /* destroy the current window */
377 glXMakeContextCurrent(wm_data
.dpy
, None
, None
, NULL
);
379 glXDestroyContext(wm_data
.dpy
, wm_data
.ctx
);
381 glXDestroyWindow(wm_data
.dpy
,wm_data
.glxwin
);
383 /* switch back to original desktop resolution if we were in fs */
384 if (wm_data
.fullscreen
) {
385 XF86VidModeSwitchToMode(wm_data
.dpy
, wm_data
.screen
, &wm_data
.deskMode
);
386 XF86VidModeSetViewPort(wm_data
.dpy
, wm_data
.screen
, 0, 0);
391 /* set fullscreen on/off */
392 void wm_toggle_fullscreen(int fs
)
394 if (fs
== wm_data
.fullscreen
)
397 if (!wm_data
.isinit
) {
398 wm_data
.fullscreen
= fs
;
402 glXMakeContextCurrent(wm_data
.dpy
, None
, None
, NULL
);
403 glXDestroyWindow(wm_data
.dpy
,wm_data
.glxwin
);
405 /* switch back to original desktop resolution if we were in fs */
406 if (wm_data
.fullscreen
) {
407 XF86VidModeSwitchToMode(wm_data
.dpy
, wm_data
.screen
, &wm_data
.deskMode
);
408 XF86VidModeSetViewPort(wm_data
.dpy
, wm_data
.screen
, 0, 0);
410 wm_data
.fullscreen
= fs
;
411 /* TODO: this isn't working in glX */
415 /* use file as a cursor texture */
416 void wm_cursor(char* file
, int width
, int height
, int offset_x
, int offset_y
)
418 Cursor invisibleCursor
;
421 static char noData
[] = { 0,0,0,0,0,0,0,0 };
424 if (!wm_data
.cursor
.mat
)
427 wm_data
.cursor
.mat
= NULL
;
428 XUndefineCursor(wm_data
.dpy
, wm_data
.win
);
433 wm_data
.cursor
.mat
= mat_from_image("texture",file
);
434 wm_data
.cursor
.w
= width
;
435 wm_data
.cursor
.h
= height
;
436 wm_data
.cursor
.x
= offset_x
;
437 wm_data
.cursor
.y
= offset_y
;
439 if (!wm_data
.cursor
.mat
)
446 bitmapNoData
= XCreateBitmapFromData(wm_data
.dpy
, wm_data
.win
, noData
, 8, 8);
447 invisibleCursor
= XCreatePixmapCursor(wm_data
.dpy
, bitmapNoData
, bitmapNoData
, &black
, &black
, 0, 0);
448 XDefineCursor(wm_data
.dpy
, wm_data
.win
, invisibleCursor
);
449 XFreeCursor(wm_data
.dpy
, invisibleCursor
);
455 Cursor invisibleCursor
;
458 static char noData
[] = { 0,0,0,0,0,0,0,0 };
460 if (events_get_mousegrab())
467 bitmapNoData
= XCreateBitmapFromData(wm_data
.dpy
, wm_data
.win
, noData
, 8, 8);
468 invisibleCursor
= XCreatePixmapCursor(wm_data
.dpy
, bitmapNoData
, bitmapNoData
, &black
, &black
, 0, 0);
469 XDefineCursor(wm_data
.dpy
, wm_data
.win
, invisibleCursor
);
470 XFreeCursor(wm_data
.dpy
, invisibleCursor
);
472 if (!wm_data
.fullscreen
)
473 XGrabPointer(wm_data
.dpy
, wm_data
.win
, True
, ButtonPressMask
, GrabModeAsync
, GrabModeAsync
, wm_data
.win
, None
, CurrentTime
);
475 events_set_mousegrab(1);
478 /* stop grabbing the mouse */
481 if (!events_get_mousegrab())
484 XUndefineCursor(wm_data
.dpy
, wm_data
.win
);
485 if (!wm_data
.fullscreen
)
486 XUngrabPointer(wm_data
.dpy
, CurrentTime
);
488 events_set_mousegrab(0);
491 /* set the window title */
492 void wm_title(char* title
)
497 wm_data
.title
= strdup(title
);
502 XStoreName(wm_data
.dpy
, wm_data
.win
, wm_data
.title
);