flycast/core/linux-dist/x11.cpp

808 lines
20 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#if defined(SUPPORT_X11)
#include <map>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#if !defined(GLES)
#include <GL/gl.h>
#include <GL/glx.h>
#endif
#include "types.h"
#include "cfg/cfg.h"
#include "linux-dist/x11.h"
#include "linux-dist/main.h"
#if FEAT_HAS_NIXPROF
#include "profiler/profiler.h"
#endif
#if defined(TARGET_PANDORA)
#define DEFAULT_FULLSCREEN 1
#define DEFAULT_WINDOW_WIDTH 800
#else
#define DEFAULT_FULLSCREEN 0
#define DEFAULT_WINDOW_WIDTH 640
#endif
#define DEFAULT_WINDOW_HEIGHT 480
map<int, int> x11_keymap;
int x11_dc_buttons = 0xFFFF;
int x11_keyboard_input = 0;
int x11_width;
int x11_height;
int ndcid = 0;
void* x11_glc = NULL;
bool x11_fullscreen = false;
Atom wmDeleteMessage;
void* x11_vis;
extern bool dump_frame_switch;
extern bool naomi_test_button;
void dc_stop(void);
bool dc_loadstate(void);
bool dc_savestate(void);
enum
{
_NET_WM_STATE_REMOVE =0,
_NET_WM_STATE_ADD = 1,
_NET_WM_STATE_TOGGLE =2
};
void x11_window_set_fullscreen(bool fullscreen)
{
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.window = (Window)x11_win;
xev.xclient.message_type = XInternAtom((Display*)x11_disp, "_NET_WM_STATE", False);
xev.xclient.format = 32;
xev.xclient.data.l[0] = 2; // _NET_WM_STATE_TOGGLE
xev.xclient.data.l[1] = XInternAtom((Display*)x11_disp, "_NET_WM_STATE_FULLSCREEN", True);
xev.xclient.data.l[2] = 0; // no second property to toggle
xev.xclient.data.l[3] = 1;
xev.xclient.data.l[4] = 0;
printf("x11: setting fullscreen to %d\n", fullscreen);
XSendEvent((Display*)x11_disp, DefaultRootWindow((Display*)x11_disp), False, SubstructureNotifyMask, &xev);
}
void event_x11_handle()
{
XEvent event;
while(XPending((Display *)x11_disp))
{
XNextEvent((Display *)x11_disp, &event);
if (event.type == ClientMessage &&
event.xclient.data.l[0] == wmDeleteMessage)
dc_stop();
else if (event.type == ConfigureNotify)
{
x11_width = event.xconfigure.width;
x11_height = event.xconfigure.height;
}
}
}
u8 kb_map[256];
static void init_kb_map()
{
//04-1D Letter keys A-Z (in alphabetic order)
kb_map[KEY_A] = 0x04;
kb_map[KEY_B] = 0x05;
kb_map[KEY_C] = 0x06;
kb_map[KEY_D] = 0x07;
kb_map[KEY_E] = 0x08;
kb_map[KEY_F] = 0x09;
kb_map[KEY_G] = 0x0A;
kb_map[KEY_H] = 0x0B;
kb_map[KEY_I] = 0x0C;
kb_map[KEY_J] = 0x0D;
kb_map[KEY_K] = 0x0E;
kb_map[KEY_L] = 0x0F;
kb_map[KEY_M] = 0x10;
kb_map[KEY_N] = 0x11;
kb_map[KEY_O] = 0x12;
kb_map[KEY_P] = 0x13;
kb_map[KEY_Q] = 0x14;
kb_map[KEY_R] = 0x15;
kb_map[KEY_S] = 0x16;
kb_map[KEY_T] = 0x17;
kb_map[KEY_U] = 0x18;
kb_map[KEY_V] = 0x19;
kb_map[KEY_W] = 0x1A;
kb_map[KEY_X] = 0x1B;
kb_map[KEY_Y] = 0x1C;
kb_map[KEY_Z] = 0x1D;
//1E-27 Number keys 1-0
kb_map[KEY_1] = 0x1E;
kb_map[KEY_2] = 0x1F;
kb_map[KEY_3] = 0x20;
kb_map[KEY_4] = 0x21;
kb_map[KEY_5] = 0x22;
kb_map[KEY_6] = 0x23;
kb_map[KEY_7] = 0x24;
kb_map[KEY_8] = 0x25;
kb_map[KEY_9] = 0x26;
kb_map[KEY_0] = 0x27;
kb_map[KEY_RETURN] = 0x28;
kb_map[KEY_ESC] = 0x29;
kb_map[KEY_BACKSPACE] = 0x2A;
kb_map[KEY_TAB] = 0x2B;
kb_map[KEY_SPACE] = 0x2C;
kb_map[20] = 0x2D; // -
kb_map[21] = 0x2E; // =
kb_map[34] = 0x2F; // [
kb_map[35] = 0x30; // ]
kb_map[94] = 0x31; // \ (US) unsure of keycode
//32-34 "]", ";" and ":" (the 3 keys right of L)
kb_map[51] = 0x32; // ~ (non-US) *,µ in FR layout
kb_map[47] = 0x33; // ;
kb_map[48] = 0x34; // '
//35 hankaku/zenkaku / kanji (top left)
kb_map[49] = 0x35; // `~ (US)
//36-38 ",", "." and "/" (the 3 keys right of M)
kb_map[59] = 0x36;
kb_map[60] = 0x37;
kb_map[61] = 0x38;
// CAPSLOCK
kb_map[66] = 0x39;
//3A-45 Function keys F1-F12
for (int i = 0;i < 10; i++)
kb_map[KEY_F1 + i] = 0x3A + i;
kb_map[KEY_F11] = 0x44;
kb_map[KEY_F12] = 0x45;
//46-4E Control keys above cursor keys
kb_map[107] = 0x46; // Print Screen
kb_map[78] = 0x47; // Scroll Lock
kb_map[127] = 0x48; // Pause
kb_map[KEY_INS] = 0x49;
kb_map[KEY_HOME] = 0x4A;
kb_map[KEY_PGUP] = 0x4B;
kb_map[KEY_DEL] = 0x4C;
kb_map[KEY_END] = 0x4D;
kb_map[KEY_PGDOWN] = 0x4E;
//4F-52 Cursor keys
kb_map[KEY_RIGHT] = 0x4F;
kb_map[KEY_LEFT] = 0x50;
kb_map[KEY_DOWN] = 0x51;
kb_map[KEY_UP] = 0x52;
//53 Num Lock (Numeric keypad)
kb_map[77] = 0x53;
//54 "/" (Numeric keypad)
kb_map[106] = 0x54;
//55 "*" (Numeric keypad)
kb_map[63] = 0x55;
//56 "-" (Numeric keypad)
kb_map[82] = 0x56;
//57 "+" (Numeric keypad)
kb_map[86] = 0x57;
//58 Enter (Numeric keypad)
kb_map[104] = 0x58;
//59-62 Number keys 1-0 (Numeric keypad)
kb_map[87] = 0x59;
kb_map[88] = 0x5A;
kb_map[89] = 0x5B;
kb_map[83] = 0x5C;
kb_map[84] = 0x5D;
kb_map[85] = 0x5E;
kb_map[79] = 0x5F;
kb_map[80] = 0x60;
kb_map[81] = 0x61;
kb_map[90] = 0x62;
//63 "." (Numeric keypad)
kb_map[91] = 0x63;
//64 #| (non-US)
//kb_map[94] = 0x64;
//65 S3 key
//66-A4 Not used
//A5-DF Reserved
//E0 Left Control
//E1 Left Shift
//E2 Left Alt
//E3 Left S1
//E4 Right Control
//E5 Right Shift
//E6 Right Alt
//E7 Right S3
//E8-FF Reserved
}
static u32 kb_used = 0;
extern u8 kb_shift; // shift keys pressed (bitmask)
extern u8 kb_led; // leds currently lit
extern u8 kb_key[6]; // normal keys pressed
extern u32 mo_buttons;
extern f32 mo_x_delta;
extern f32 mo_y_delta;
extern f32 mo_wheel_delta;
extern s32 mo_x_abs;
extern s32 mo_y_abs;
static bool capturing_mouse;
static Cursor empty_cursor = None;
extern bool coin_chute;
static Cursor create_empty_cursor()
{
if (empty_cursor == None)
{
Display *display = (Display*)x11_disp;
char data[] = { 0 };
XColor color;
color.red = color.green = color.blue = 0;
Pixmap pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
data, 1, 1);
if (pixmap)
{
empty_cursor = XCreatePixmapCursor(display, pixmap, pixmap, &color, &color, 0, 0);
XFreePixmap(display, pixmap);
}
}
return empty_cursor;
}
static void destroy_empty_cursor()
{
if (empty_cursor != None)
{
XFreeCursor((Display*)x11_disp, empty_cursor);
empty_cursor = None;
}
}
static void x11_capture_mouse()
{
x11_window_set_text("Reicast - mouse capture");
capturing_mouse = true;
Cursor cursor = create_empty_cursor();
Display *display = (Display*)x11_disp;
Window window = (Window)x11_win;
XDefineCursor(display, window, cursor);
XGrabPointer(display, window, False,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask,
GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
}
static void x11_uncapture_mouse()
{
x11_window_set_text("Reicast");
capturing_mouse = false;
Display *display = (Display*)x11_disp;
Window window = (Window)x11_win;
XUndefineCursor(display, window);
XUngrabPointer(display, CurrentTime);
}
void input_x11_handle()
{
//Handle X11
static int prev_x = -1;
static int prev_y = -1;
bool mouse_moved = false;
XEvent e;
Display *display = (Display*)x11_disp;
while (XCheckWindowEvent(display, (Window)x11_win,
KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask
| PointerMotionMask | FocusChangeMask,
&e))
{
switch(e.type)
{
case KeyPress:
case KeyRelease:
{
if (e.type == KeyRelease && XEventsQueued(display, QueuedAfterReading))
{
XEvent nev;
XPeekEvent(display, &nev);
if (nev.type == KeyPress && nev.xkey.time == e.xkey.time &&
nev.xkey.keycode == e.xkey.keycode)
// Key wasnt actually released: auto repeat
continue;
}
// Dreamcast keyboard emulation
if (e.xkey.keycode == KEY_LSHIFT || e.xkey.keycode == KEY_RSHIFT)
if (e.type == KeyRelease)
kb_shift &= ~(0x02 | 0x20);
else
kb_shift |= (0x02 | 0x20);
if (e.xkey.keycode == KEY_LCTRL || e.xkey.keycode == KEY_RCTRL)
if (e.type == KeyRelease)
kb_shift &= ~(0x01 | 0x10);
else
kb_shift |= (0x01 | 0x10);
u8 dc_keycode = kb_map[e.xkey.keycode & 0xFF];
if (dc_keycode != 0)
{
if (e.type == KeyPress)
{
if (kb_used < 6)
{
bool found = false;
for (int i = 0; !found && i < 6; i++)
{
if (kb_key[i] == dc_keycode)
found = true;
}
if (!found)
{
kb_key[kb_used] = dc_keycode;
kb_used++;
}
}
}
else
{
if (kb_used > 0)
{
for (int i = 0; i < 6; i++)
{
if (kb_key[i] == dc_keycode)
{
kb_used--;
for (int j = i; j < 5; j++)
kb_key[j] = kb_key[j + 1];
kb_key[5] = 0;
}
}
}
}
}
if (settings.input.DCMouse)
{
// Start/stop mouse capture with Left Ctrl + Left Alt
if (e.type == KeyPress
&& ((e.xkey.keycode == KEY_LALT && (e.xkey.state & ControlMask))
|| (e.xkey.keycode == KEY_LCTRL && (e.xkey.state & Mod1Mask))))
{
capturing_mouse = !capturing_mouse;
if (capturing_mouse)
x11_capture_mouse();
else
x11_uncapture_mouse();
}
}
if (x11_keyboard_input)
{
// Normal keyboard handling
if (e.type == KeyRelease && e.xkey.keycode == KEY_ESC
&& !(e.xkey.state & (ControlMask | ShiftMask | Mod1Mask)))
{
dc_stop();
}
#ifndef RELEASE
else if (e.xkey.keycode == KEY_F10)
{
// Dump the next frame into a file
dump_frame_switch = e.type == KeyPress;
}
#elif FEAT_HAS_NIXPROF
else if (e.type == KeyRelease && e.xkey.keycode == KEY_F10)
{
if (sample_Switch(3000)) {
printf("Starting profiling\n");
} else {
printf("Stopping profiling\n");
}
}
#endif
else if (e.type == KeyRelease && e.xkey.keycode == KEY_F11)
{
x11_fullscreen = !x11_fullscreen;
x11_window_set_fullscreen(x11_fullscreen);
}
else if (e.type == KeyRelease && e.xkey.keycode == KEY_F2)
{
dc_savestate() ;
}
else if (e.type == KeyRelease && e.xkey.keycode == KEY_F4)
{
dc_loadstate() ;
}
else if (e.type == KeyRelease && e.xkey.keycode == KEY_F12)
{
extern bool renderer_changed;
settings.pvr.rend = settings.pvr.rend == 0 ? 3 : 0; // Alternate between per-pixel and per-triangle
printf("OpenGL: renderer changed to %d\n", settings.pvr.rend);
renderer_changed = true;
}
#if DC_PLATFORM == DC_PLATFORM_NAOMI
else if (e.type == KeyRelease && e.xkey.keycode == KEY_F8)
{
coin_chute = true;
}
else if (e.xkey.keycode == KEY_F7)
{
naomi_test_button = e.type == KeyPress;
}
#endif
else
{
int dc_key = x11_keymap[e.xkey.keycode];
if (dc_key == DC_AXIS_LT)
{
if (e.type == KeyPress)
lt[0] = 255;
else
lt[0] = 0;
}
else if (dc_key == DC_AXIS_RT)
{
if (e.type == KeyPress)
rt[0] = 255;
else
rt[0] = 0;
}
if (e.type == KeyPress)
{
kcode[0] &= ~dc_key;
}
else
{
kcode[0] |= dc_key;
}
#if defined(_DEBUG)
printf("KEY: %d -> %d: %d\n", e.xkey.keycode, dc_key, x11_dc_buttons );
#endif
}
}
}
break;
case FocusOut:
{
if (capturing_mouse)
x11_uncapture_mouse();
capturing_mouse = false;
}
break;
case ButtonPress:
case ButtonRelease:
{
u32 button_mask = 0;
if (e.xbutton.button == Button1) // Left button
button_mask = 1 << 2;
else if (e.xbutton.button == Button2) // Middle button
button_mask = 1 << 3;
else if (e.xbutton.button == Button3) // Right button
button_mask = 1 << 1;
if (button_mask)
{
if (e.type == ButtonPress)
mo_buttons &= ~button_mask;
else
mo_buttons |= button_mask;
}
}
// FALL THROUGH
case MotionNotify:
// For Light gun
mo_x_abs = (e.xmotion.x - (x11_width - x11_height * 640 / 480) / 2) * 480 / x11_height;
mo_y_abs = e.xmotion.y * 480 / x11_height;
// For mouse
if (settings.input.DCMouse)
{
mouse_moved = true;
if (prev_x != -1)
mo_x_delta += (f32)(e.xmotion.x - prev_x) * settings.input.MouseSensitivity / 100.f;
if (prev_y != -1)
mo_y_delta += (f32)(e.xmotion.y - prev_y) * settings.input.MouseSensitivity / 100.f;
prev_x = e.xmotion.x;
prev_y = e.xmotion.y;
}
break;
}
}
if (capturing_mouse && mouse_moved)
{
prev_x = x11_width / 2;
prev_y = x11_height / 2;
XWarpPointer(display, None, (Window)x11_win, 0, 0, 0, 0,
prev_x, prev_y);
XSync(display, true);
}
}
void input_x11_init()
{
x11_keymap[KEY_LEFT] = DC_DPAD_LEFT;
x11_keymap[KEY_RIGHT] = DC_DPAD_RIGHT;
x11_keymap[KEY_UP] = DC_DPAD_UP;
x11_keymap[KEY_DOWN] = DC_DPAD_DOWN;
// Layout on a real DC controller
// Y
// X B
// A
x11_keymap[KEY_S] = DC_BTN_X;
x11_keymap[KEY_X] = DC_BTN_A;
x11_keymap[KEY_D] = DC_BTN_Y;
x11_keymap[KEY_C] = DC_BTN_B;
// Used by some "arcade" controllers
x11_keymap[KEY_Q] = DC_BTN_Z;
x11_keymap[KEY_W] = DC_BTN_C;
x11_keymap[KEY_E] = DC_BTN_D;
// Start button (triangle)
x11_keymap[KEY_RETURN] = DC_BTN_START;
// Shoulder trigger
x11_keymap[KEY_F] = DC_AXIS_LT;
x11_keymap[KEY_V] = DC_AXIS_RT;
x11_keyboard_input = (cfgLoadInt("input", "enable_x11_keyboard", 1) >= 1);
if (!x11_keyboard_input)
printf("X11 Keyboard input disabled by config.\n");
init_kb_map();
}
void x11_window_create()
{
if (cfgLoadInt("pvr", "nox11", 0) == 0)
{
XInitThreads();
// X11 variables
Window x11Window = 0;
Display* x11Display = 0;
long x11Screen = 0;
XVisualInfo* x11Visual = 0;
Colormap x11Colormap = 0;
/*
Step 0 - Create a NativeWindowType that we can use it for OpenGL ES output
*/
Window sRootWindow;
XSetWindowAttributes sWA;
unsigned int ui32Mask;
int i32Depth;
// Initializes the display and screen
x11Display = XOpenDisplay(NULL);
if (!x11Display && !(x11Display = XOpenDisplay(":0")))
{
printf("Error: Unable to open X display\n");
return;
}
x11Screen = XDefaultScreen(x11Display);
// Gets the window parameters
sRootWindow = RootWindow(x11Display, x11Screen);
int depth = CopyFromParent;
#if !defined(GLES)
// Get a matching FB config
static int visual_attribs[] =
{
GLX_X_RENDERABLE , True,
GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT,
GLX_RENDER_TYPE , GLX_RGBA_BIT,
GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
GLX_RED_SIZE , 8,
GLX_GREEN_SIZE , 8,
GLX_BLUE_SIZE , 8,
GLX_ALPHA_SIZE , 8,
GLX_DEPTH_SIZE , 24,
GLX_STENCIL_SIZE , 8,
GLX_DOUBLEBUFFER , True,
//GLX_SAMPLE_BUFFERS , 1,
//GLX_SAMPLES , 4,
None
};
int glx_major, glx_minor;
// FBConfigs were added in GLX version 1.3.
if (!glXQueryVersion(x11Display, &glx_major, &glx_minor) ||
((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1))
{
printf("Invalid GLX version");
exit(1);
}
int fbcount;
GLXFBConfig* fbc = glXChooseFBConfig(x11Display, x11Screen, visual_attribs, &fbcount);
if (!fbc)
{
printf("Failed to retrieve a framebuffer config\n");
exit(1);
}
printf("Found %d matching FB configs.\n", fbcount);
GLXFBConfig bestFbc = fbc[0];
XFree(fbc);
// Get a visual
XVisualInfo *vi = glXGetVisualFromFBConfig(x11Display, bestFbc);
printf("Chosen visual ID = 0x%lx\n", vi->visualid);
depth = vi->depth;
x11Visual = vi;
x11Colormap = XCreateColormap(x11Display, RootWindow(x11Display, x11Screen), vi->visual, AllocNone);
#else
i32Depth = DefaultDepth(x11Display, x11Screen);
x11Visual = new XVisualInfo;
XMatchVisualInfo(x11Display, x11Screen, i32Depth, TrueColor, x11Visual);
if (!x11Visual)
{
printf("Error: Unable to acquire visual\n");
return;
}
x11Colormap = XCreateColormap(x11Display, sRootWindow, x11Visual->visual, AllocNone);
#endif
sWA.colormap = x11Colormap;
// Add to these for handling other events
sWA.event_mask = StructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask;
if (settings.input.DCMouse)
sWA.event_mask |= PointerMotionMask | FocusChangeMask;
ui32Mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap;
x11_width = cfgLoadInt("x11", "width", DEFAULT_WINDOW_WIDTH);
x11_height = cfgLoadInt("x11", "height", DEFAULT_WINDOW_HEIGHT);
x11_fullscreen = (cfgLoadInt("x11", "fullscreen", DEFAULT_FULLSCREEN) > 0);
if (x11_width < 0 || x11_height < 0)
{
x11_width = XDisplayWidth(x11Display, x11Screen);
x11_height = XDisplayHeight(x11Display, x11Screen);
}
// Creates the X11 window
x11Window = XCreateWindow(x11Display, RootWindow(x11Display, x11Screen), (ndcid%3)*640, (ndcid/3)*480, x11_width, x11_height,
0, depth, InputOutput, x11Visual->visual, ui32Mask, &sWA);
// Capture the close window event
wmDeleteMessage = XInternAtom(x11Display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(x11Display, x11Window, &wmDeleteMessage, 1);
if(x11_fullscreen)
{
// fullscreen
Atom wmState = XInternAtom(x11Display, "_NET_WM_STATE", False);
Atom wmFullscreen = XInternAtom(x11Display, "_NET_WM_STATE_FULLSCREEN", False);
XChangeProperty(x11Display, x11Window, wmState, XA_ATOM, 32, PropModeReplace, (unsigned char *)&wmFullscreen, 1);
XMapRaised(x11Display, x11Window);
}
else
{
XMapWindow(x11Display, x11Window);
}
#if !defined(GLES)
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");
verify(glXCreateContextAttribsARB != 0);
int context_attribs[] =
{
GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
GLX_CONTEXT_MINOR_VERSION_ARB, 3,
#ifndef RELEASE
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB,
#endif
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
None
};
x11_glc = glXCreateContextAttribsARB(x11Display, bestFbc, 0, True, context_attribs);
if (!x11_glc)
{
printf("Open GL 4.3 not supported\n");
// Try GL 3.1
context_attribs[1] = 3;
context_attribs[3] = 1;
x11_glc = glXCreateContextAttribsARB(x11Display, bestFbc, 0, True, context_attribs);
if (!x11_glc)
{
die("Open GL 3.1 not supported\n");
}
}
XSync(x11Display, False);
#endif
XFlush(x11Display);
//(EGLNativeDisplayType)x11Display;
x11_disp = (void*)x11Display;
x11_win = (void*)x11Window;
x11_vis = (void*)x11Visual->visual;
x11_window_set_text("Reicast");
}
else
{
printf("Not creating X11 window ..\n");
}
}
void x11_window_set_text(const char* text)
{
if (x11_win)
{
XChangeProperty((Display*)x11_disp, (Window)x11_win,
XInternAtom((Display*)x11_disp, "WM_NAME", False), //WM_NAME,
XInternAtom((Display*)x11_disp, "UTF8_STRING", False), //UTF8_STRING,
8, PropModeReplace, (const unsigned char *)text, strlen(text));
}
}
void x11_gl_context_destroy()
{
glXMakeCurrent((Display*)x11_disp, None, NULL);
glXDestroyContext((Display*)x11_disp, (GLXContext)x11_glc);
}
void x11_window_destroy()
{
// close XWindow
if (x11_win)
{
XDestroyWindow((Display*)x11_disp, (Window)x11_win);
x11_win = NULL;
}
if (x11_disp)
{
#if !defined(GLES)
if (x11_glc)
{
glXMakeCurrent((Display*)x11_disp, None, NULL);
glXDestroyContext((Display*)x11_disp, (GLXContext)x11_glc);
x11_glc = NULL;
}
#endif
XCloseDisplay((Display*)x11_disp);
x11_disp = NULL;
}
}
#endif