You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
710 lines
23 KiB
710 lines
23 KiB
/* The MIT License |
|
* |
|
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient |
|
* |
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
* of this software and associated documentation files (the "Software"), to deal |
|
* in the Software without restriction, including without limitation the rights |
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
* copies of the Software, and to permit persons to whom the Software is |
|
* furnished to do so, subject to the following conditions: |
|
* |
|
* The above copyright notice and this permission notice shall be included in |
|
* all copies or substantial portions of the Software. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
* THE SOFTWARE. |
|
*/ |
|
|
|
#include "platform.h" |
|
#include "engine.h" |
|
#include "input.h" |
|
#include "logger.h" |
|
|
|
#include <cstring> |
|
|
|
#include <string> |
|
#include <algorithm> |
|
#include <map> |
|
|
|
#include <time.h> |
|
#include <sys/time.h> |
|
#include <X11/Xlib.h> |
|
#include <X11/Xatom.h> |
|
#include <GL/glx.h> |
|
|
|
struct X11PlatformPrivate { |
|
Display *display; |
|
XVisualInfo *visual; |
|
GLXContext glxContext; |
|
XIM xim; |
|
XIC xic; |
|
Colormap colormap; |
|
Window window; |
|
Cursor cursor; |
|
Atom atomDeleteWindow; |
|
Atom atomClipboard; |
|
Atom atomTargets; |
|
Atom atomText; |
|
Atom atomCompoundText; |
|
Atom atomUTF8String; |
|
bool visible; |
|
bool focused; |
|
int width; |
|
int height; |
|
std::string clipboardText; |
|
std::map<int, unsigned char> keyMap; |
|
} x11; |
|
|
|
void Platform::init() |
|
{ |
|
x11.display = NULL; |
|
x11.visual = NULL; |
|
x11.glxContext = NULL; |
|
x11.xim = NULL; |
|
x11.xic = NULL; |
|
x11.colormap = None; |
|
x11.window = None; |
|
x11.cursor = None; |
|
x11.visible = false; |
|
x11.focused = false; |
|
x11.width = 0; |
|
x11.height = 0; |
|
|
|
// setup keymap |
|
x11.keyMap[XK_1] = KC_1; |
|
x11.keyMap[XK_2] = KC_2; |
|
x11.keyMap[XK_3] = KC_3; |
|
x11.keyMap[XK_4] = KC_4; |
|
x11.keyMap[XK_5] = KC_5; |
|
x11.keyMap[XK_6] = KC_6; |
|
x11.keyMap[XK_7] = KC_7; |
|
x11.keyMap[XK_8] = KC_8; |
|
x11.keyMap[XK_9] = KC_9; |
|
x11.keyMap[XK_0] = KC_0; |
|
|
|
x11.keyMap[XK_BackSpace] = KC_BACK; |
|
|
|
x11.keyMap[XK_minus] = KC_MINUS; |
|
x11.keyMap[XK_equal] = KC_EQUALS; |
|
x11.keyMap[XK_space] = KC_SPACE; |
|
x11.keyMap[XK_comma] = KC_COMMA; |
|
x11.keyMap[XK_period] = KC_PERIOD; |
|
|
|
x11.keyMap[XK_backslash] = KC_BACKSLASH; |
|
x11.keyMap[XK_slash] = KC_SLASH; |
|
x11.keyMap[XK_bracketleft] = KC_LBRACKET; |
|
x11.keyMap[XK_bracketright] = KC_RBRACKET; |
|
|
|
x11.keyMap[XK_Escape] = KC_ESCAPE; |
|
x11.keyMap[XK_Caps_Lock] = KC_CAPITAL; |
|
|
|
x11.keyMap[XK_Tab] = KC_TAB; |
|
x11.keyMap[XK_Return] = KC_RETURN; |
|
x11.keyMap[XK_Control_L] = KC_LCONTROL; |
|
x11.keyMap[XK_Control_R] = KC_RCONTROL; |
|
|
|
x11.keyMap[XK_colon] = KC_COLON; |
|
x11.keyMap[XK_semicolon] = KC_SEMICOLON; |
|
x11.keyMap[XK_apostrophe] = KC_APOSTROPHE; |
|
x11.keyMap[XK_grave] = KC_GRAVE; |
|
|
|
x11.keyMap[XK_b] = KC_B; |
|
x11.keyMap[XK_a] = KC_A; |
|
x11.keyMap[XK_c] = KC_C; |
|
x11.keyMap[XK_d] = KC_D; |
|
x11.keyMap[XK_e] = KC_E; |
|
x11.keyMap[XK_f] = KC_F; |
|
x11.keyMap[XK_g] = KC_G; |
|
x11.keyMap[XK_h] = KC_H; |
|
x11.keyMap[XK_i] = KC_I; |
|
x11.keyMap[XK_j] = KC_J; |
|
x11.keyMap[XK_k] = KC_K; |
|
x11.keyMap[XK_l] = KC_L; |
|
x11.keyMap[XK_m] = KC_M; |
|
x11.keyMap[XK_n] = KC_N; |
|
x11.keyMap[XK_o] = KC_O; |
|
x11.keyMap[XK_p] = KC_P; |
|
x11.keyMap[XK_q] = KC_Q; |
|
x11.keyMap[XK_r] = KC_R; |
|
x11.keyMap[XK_s] = KC_S; |
|
x11.keyMap[XK_t] = KC_T; |
|
x11.keyMap[XK_u] = KC_U; |
|
x11.keyMap[XK_v] = KC_V; |
|
x11.keyMap[XK_w] = KC_W; |
|
x11.keyMap[XK_x] = KC_X; |
|
x11.keyMap[XK_y] = KC_Y; |
|
x11.keyMap[XK_z] = KC_Z; |
|
|
|
x11.keyMap[XK_F1] = KC_F1; |
|
x11.keyMap[XK_F2] = KC_F2; |
|
x11.keyMap[XK_F3] = KC_F3; |
|
x11.keyMap[XK_F4] = KC_F4; |
|
x11.keyMap[XK_F5] = KC_F5; |
|
x11.keyMap[XK_F6] = KC_F6; |
|
x11.keyMap[XK_F7] = KC_F7; |
|
x11.keyMap[XK_F8] = KC_F8; |
|
x11.keyMap[XK_F9] = KC_F9; |
|
x11.keyMap[XK_F10] = KC_F10; |
|
x11.keyMap[XK_F11] = KC_F11; |
|
x11.keyMap[XK_F12] = KC_F12; |
|
x11.keyMap[XK_F13] = KC_F13; |
|
x11.keyMap[XK_F14] = KC_F14; |
|
x11.keyMap[XK_F15] = KC_F15; |
|
|
|
// keypad |
|
x11.keyMap[XK_KP_0] = KC_NUMPAD0; |
|
x11.keyMap[XK_KP_1] = KC_NUMPAD1; |
|
x11.keyMap[XK_KP_2] = KC_NUMPAD2; |
|
x11.keyMap[XK_KP_3] = KC_NUMPAD3; |
|
x11.keyMap[XK_KP_4] = KC_NUMPAD4; |
|
x11.keyMap[XK_KP_5] = KC_NUMPAD5; |
|
x11.keyMap[XK_KP_6] = KC_NUMPAD6; |
|
x11.keyMap[XK_KP_7] = KC_NUMPAD7; |
|
x11.keyMap[XK_KP_8] = KC_NUMPAD8; |
|
x11.keyMap[XK_KP_9] = KC_NUMPAD9; |
|
x11.keyMap[XK_KP_Add] = KC_ADD; |
|
x11.keyMap[XK_KP_Subtract] = KC_SUBTRACT; |
|
x11.keyMap[XK_KP_Decimal] = KC_DECIMAL; |
|
x11.keyMap[XK_KP_Equal] = KC_NUMPADEQUALS; |
|
x11.keyMap[XK_KP_Divide] = KC_DIVIDE; |
|
x11.keyMap[XK_KP_Multiply] = KC_MULTIPLY; |
|
x11.keyMap[XK_KP_Enter] = KC_NUMPADENTER; |
|
|
|
// keypad with numlock off |
|
x11.keyMap[XK_KP_Home] = KC_NUMPAD7; |
|
x11.keyMap[XK_KP_Up] = KC_NUMPAD8; |
|
x11.keyMap[XK_KP_Page_Up] = KC_NUMPAD9; |
|
x11.keyMap[XK_KP_Left] = KC_NUMPAD4; |
|
x11.keyMap[XK_KP_Begin] = KC_NUMPAD5; |
|
x11.keyMap[XK_KP_Right] = KC_NUMPAD6; |
|
x11.keyMap[XK_KP_End] = KC_NUMPAD1; |
|
x11.keyMap[XK_KP_Down] = KC_NUMPAD2; |
|
x11.keyMap[XK_KP_Page_Down] = KC_NUMPAD3; |
|
x11.keyMap[XK_KP_Insert] = KC_NUMPAD0; |
|
x11.keyMap[XK_KP_Delete] = KC_DECIMAL; |
|
|
|
x11.keyMap[XK_Up] = KC_UP; |
|
x11.keyMap[XK_Down] = KC_DOWN; |
|
x11.keyMap[XK_Left] = KC_LEFT; |
|
x11.keyMap[XK_Right] = KC_RIGHT; |
|
|
|
x11.keyMap[XK_Page_Up] = KC_PGUP; |
|
x11.keyMap[XK_Page_Down] = KC_PGDOWN; |
|
x11.keyMap[XK_Home] = KC_HOME; |
|
x11.keyMap[XK_End] = KC_END; |
|
|
|
x11.keyMap[XK_Num_Lock] = KC_NUMLOCK; |
|
x11.keyMap[XK_Print] = KC_SYSRQ; |
|
x11.keyMap[XK_Scroll_Lock] = KC_SCROLL; |
|
x11.keyMap[XK_Pause] = KC_PAUSE; |
|
|
|
x11.keyMap[XK_Shift_R] = KC_RSHIFT; |
|
x11.keyMap[XK_Shift_L] = KC_LSHIFT; |
|
x11.keyMap[XK_Alt_R] = KC_RALT; |
|
x11.keyMap[XK_Alt_L] = KC_LALT; |
|
|
|
x11.keyMap[XK_Insert] = KC_INSERT; |
|
x11.keyMap[XK_Delete] = KC_DELETE; |
|
|
|
x11.keyMap[XK_Super_L] = KC_LWIN; |
|
x11.keyMap[XK_Super_R] = KC_RWIN; |
|
x11.keyMap[XK_Menu] = KC_APPS; |
|
|
|
// try to set a latin1 locales otherwise fallback to standard C locale |
|
static char locales[][32] = { "en_US.iso88591", "iso88591", "en_US", "C" }; |
|
for(int i=0;i<4;++i) { |
|
if(setlocale(LC_ALL, locales[i])) |
|
break; |
|
} |
|
|
|
// open display |
|
x11.display = XOpenDisplay(0); |
|
if(!x11.display) |
|
fatal("Failed to open X display"); |
|
|
|
// check if GLX is supported on this display |
|
if(!glXQueryExtension(x11.display, 0, 0)) |
|
fatal("GLX not supported"); |
|
|
|
// retrieve GLX version |
|
int glxMajor; |
|
int glxMinor; |
|
if(!glXQueryVersion(x11.display, &glxMajor, &glxMinor)) |
|
fatal("Unable to query GLX version"); |
|
notice("GLX version %d.%d", glxMajor, glxMinor); |
|
|
|
// clipboard related atoms |
|
x11.atomClipboard = XInternAtom(x11.display, "CLIPBOARD", False); |
|
x11.atomTargets = XInternAtom(x11.display, "TARGETS", False); |
|
x11.atomUTF8String = XInternAtom(x11.display, "UTF8_STRING", False); |
|
x11.atomText = XInternAtom(x11.display, "TEXT", False); |
|
x11.atomCompoundText = XInternAtom(x11.display, "COMPOUND_TEXT", False); |
|
} |
|
|
|
void Platform::terminate() |
|
{ |
|
if(x11.window) { |
|
destroyWindow(); |
|
x11.window = None; |
|
} |
|
|
|
// close display |
|
if(x11.display) { |
|
XCloseDisplay(x11.display); |
|
x11.display = NULL; |
|
} |
|
} |
|
|
|
void Platform::poll() |
|
{ |
|
XEvent event, peekevent; |
|
static InputEvent inputEvent; |
|
while(XPending(x11.display) > 0) { |
|
XNextEvent(x11.display, &event); |
|
|
|
// call filter because xim will discard KeyPress events when keys still composing |
|
if(XFilterEvent(&event, x11.window)) |
|
continue; |
|
|
|
// discard events of repeated key releases |
|
if(event.type == KeyRelease && XPending(x11.display)) { |
|
XPeekEvent(x11.display, &peekevent); |
|
if((peekevent.type == KeyPress) && |
|
(peekevent.xkey.keycode == event.xkey.keycode) && |
|
((peekevent.xkey.time-event.xkey.time) < 2)) |
|
continue; |
|
} |
|
|
|
switch(event.type) { |
|
case ConfigureNotify: |
|
// window resize |
|
if(x11.width != event.xconfigure.width || x11.height != event.xconfigure.height) { |
|
x11.width = event.xconfigure.width; |
|
x11.height = event.xconfigure.height; |
|
g_engine.onResize(x11.width, x11.height); |
|
} |
|
break; |
|
|
|
case KeyPress: |
|
case KeyRelease: { |
|
KeySym keysym; |
|
char buf[32]; |
|
int len; |
|
|
|
inputEvent.key.ctrl = (event.xkey.state & ControlMask); |
|
inputEvent.key.shift = (event.xkey.state & ShiftMask); |
|
inputEvent.key.alt = (event.xkey.state & Mod1Mask); |
|
|
|
// fire enter text event |
|
if(event.type == KeyPress && !inputEvent.key.ctrl && !inputEvent.key.alt) { |
|
if(x11.xic) { // with xim we can get latin1 input correctly |
|
Status status; |
|
len = XmbLookupString(x11.xic, &event.xkey, buf, sizeof(buf), &keysym, &status); |
|
} else { // otherwise use XLookupString, but it doesn't work right with dead keys often |
|
static XComposeStatus compose = {NULL, 0}; |
|
len = XLookupString(&event.xkey, buf, sizeof(buf), &keysym, &compose); |
|
} |
|
if(len > 0 && |
|
// for some reason these keys produces characters and we don't want that |
|
keysym != XK_BackSpace && |
|
keysym != XK_Return && |
|
keysym != XK_Delete && |
|
keysym != XK_Escape |
|
) { |
|
//debug("char: %c code: %d", buf[0], (unsigned char)buf[0]); |
|
inputEvent.type = EV_TEXT_ENTER; |
|
inputEvent.key.keychar = buf[0]; |
|
inputEvent.key.keycode = KC_UNKNOWN; |
|
g_engine.onInputEvent(&inputEvent); |
|
} |
|
} |
|
|
|
// unmask Shift/Lock to get expected results |
|
event.xkey.state &= ~(ShiftMask | LockMask); |
|
len = XLookupString(&event.xkey, buf, sizeof(buf), &keysym, 0); |
|
|
|
// fire key up/down event |
|
if(x11.keyMap.find(keysym) != x11.keyMap.end()) { |
|
inputEvent.key.keycode = x11.keyMap[keysym]; |
|
inputEvent.type = (event.type == KeyPress) ? EV_KEY_DOWN : EV_KEY_UP; |
|
inputEvent.key.keychar = (len > 0) ? buf[0] : 0; |
|
g_engine.onInputEvent(&inputEvent); |
|
} |
|
break; |
|
} |
|
case ButtonPress: |
|
case ButtonRelease: |
|
switch(event.xbutton.button) { |
|
case Button1: |
|
inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_LDOWN : EV_MOUSE_LUP; |
|
break; |
|
case Button3: |
|
inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_RDOWN : EV_MOUSE_RUP; |
|
break; |
|
case Button2: |
|
inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_MDOWN : EV_MOUSE_MUP; |
|
break; |
|
case Button4: |
|
inputEvent.type = EV_MOUSE_WHEEL_UP; |
|
break; |
|
case Button5: |
|
inputEvent.type = EV_MOUSE_WHEEL_DOWN; |
|
break; |
|
} |
|
g_engine.onInputEvent(&inputEvent); |
|
break; |
|
|
|
case MotionNotify: |
|
inputEvent.type = EV_MOUSE_MOVE; |
|
inputEvent.mouse.x = event.xbutton.x; |
|
inputEvent.mouse.y = event.xbutton.y; |
|
g_engine.onInputEvent(&inputEvent); |
|
break; |
|
|
|
case MapNotify: |
|
x11.visible = true; |
|
break; |
|
|
|
case UnmapNotify: |
|
x11.visible = false; |
|
break; |
|
|
|
case FocusIn: |
|
x11.focused = true; |
|
break; |
|
|
|
case FocusOut: |
|
x11.focused = false; |
|
break; |
|
|
|
// clipboard data request |
|
case SelectionRequest: |
|
{ |
|
XEvent respond; |
|
XSelectionRequestEvent *req = &(event.xselectionrequest); |
|
|
|
if(req->target == x11.atomTargets ) { |
|
Atom typeList[] = {x11.atomText, x11.atomCompoundText, x11.atomUTF8String, XA_STRING}; |
|
|
|
XChangeProperty(x11.display, req->requestor, |
|
req->property, req->target, |
|
8, PropModeReplace, |
|
(unsigned char *) &typeList, |
|
sizeof(typeList)); |
|
respond.xselection.property = req->property; |
|
} else { |
|
XChangeProperty(x11.display, |
|
req->requestor, |
|
req->property, req->target, |
|
8, |
|
PropModeReplace, |
|
(unsigned char*) x11.clipboardText.c_str(), |
|
x11.clipboardText.size()); |
|
respond.xselection.property = req->property; |
|
} |
|
|
|
respond.xselection.type = SelectionNotify; |
|
respond.xselection.display = req->display; |
|
respond.xselection.requestor = req->requestor; |
|
respond.xselection.selection = req->selection; |
|
respond.xselection.target = req->target; |
|
respond.xselection.time = req->time; |
|
XSendEvent(x11.display, req->requestor, 0, 0, &respond); |
|
XFlush(x11.display); |
|
break; |
|
} |
|
|
|
case ClientMessage: |
|
{ |
|
if((Atom)event.xclient.data.l[0] == x11.atomDeleteWindow) |
|
g_engine.onClose(); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
unsigned long Platform::getTicks() |
|
{ |
|
static timeval tv; |
|
static unsigned long firstTick = 0; |
|
|
|
gettimeofday(&tv, 0); |
|
if(!firstTick) |
|
firstTick = tv.tv_sec; |
|
|
|
return ((tv.tv_sec - firstTick) * 1000) + (tv.tv_usec / 1000); |
|
} |
|
|
|
void Platform::sleep(unsigned long miliseconds) |
|
{ |
|
timespec tv; |
|
tv.tv_sec = miliseconds / 1000; |
|
tv.tv_nsec = (miliseconds % 1000) * 1000000; |
|
nanosleep(&tv, NULL); |
|
} |
|
|
|
bool Platform::createWindow(int width, int height, int minWidth, int minHeight) |
|
{ |
|
static int attrList[] = { |
|
GLX_USE_GL, |
|
GLX_RGBA, |
|
GLX_DOUBLEBUFFER, |
|
None |
|
}; |
|
|
|
// choose OpenGL, RGBA, double buffered, visual |
|
x11.visual = glXChooseVisual(x11.display, DefaultScreen(x11.display), attrList); |
|
if(!x11.visual) |
|
fatal("RGBA/Double buffered visual not supported"); |
|
|
|
// create GLX context |
|
x11.glxContext = glXCreateContext(x11.display, x11.visual, 0, GL_TRUE); |
|
if(!x11.glxContext) |
|
fatal("Unable to create GLX context"); |
|
|
|
// color map |
|
x11.colormap = XCreateColormap(x11.display, |
|
RootWindow(x11.display, x11.visual->screen), |
|
x11.visual->visual, |
|
AllocNone); |
|
|
|
// setup window attributes |
|
XSetWindowAttributes wa; |
|
wa.colormap = x11.colormap; |
|
wa.border_pixel = 0; |
|
wa.event_mask = KeyPressMask | KeyReleaseMask | |
|
ButtonPressMask | ButtonReleaseMask | PointerMotionMask | |
|
ExposureMask | VisibilityChangeMask | |
|
StructureNotifyMask | FocusChangeMask; |
|
|
|
// calculate center position |
|
int x = (XDisplayHeight(x11.display, DefaultScreen(x11.display)) - width) / 2; |
|
int y = (XDisplayHeight(x11.display, DefaultScreen(x11.display)) - height) / 2; |
|
|
|
// create the window |
|
x11.window = XCreateWindow(x11.display, |
|
RootWindow(x11.display, x11.visual->screen), |
|
x, y, |
|
width, height, |
|
0, |
|
x11.visual->depth, |
|
InputOutput, |
|
x11.visual->visual, |
|
CWBorderPixel | CWColormap | CWEventMask, |
|
&wa); |
|
|
|
if(!x11.window) |
|
fatal("Unable to create X window"); |
|
|
|
// create input context (to have better key input handling) |
|
if(XSupportsLocale()) { |
|
XSetLocaleModifiers(""); |
|
x11.xim = XOpenIM(x11.display, NULL, NULL, NULL); |
|
if(x11.xim) { |
|
x11.xic = XCreateIC(x11.xim, |
|
XNInputStyle, |
|
XIMPreeditNothing | XIMStatusNothing, |
|
XNClientWindow, x11.window, NULL); |
|
if(!x11.xic) |
|
error("Unable to create the input context"); |
|
} else |
|
error("Failed to open an input method"); |
|
} else |
|
error("X11 does not support the current locale"); |
|
|
|
if(!x11.xic) |
|
warning("Input of special keys maybe messed up because we couldn't create an input context"); |
|
|
|
|
|
// set window minimum size |
|
XSizeHints xsizehints; |
|
xsizehints.flags = PMinSize; |
|
xsizehints.min_width = minWidth; |
|
xsizehints.min_height= minHeight; |
|
XSetWMSizeHints(x11.display, x11.window, &xsizehints, XA_WM_NORMAL_HINTS); |
|
|
|
// handle delete window event |
|
x11.atomDeleteWindow = XInternAtom(x11.display, "WM_DELETE_WINDOW", True); |
|
XSetWMProtocols(x11.display, x11.window, &x11.atomDeleteWindow , 1); |
|
|
|
// connect the GLX-context to the window |
|
glXMakeCurrent(x11.display, x11.window, x11.glxContext); |
|
|
|
x11.width = width; |
|
x11.height = height; |
|
return true; |
|
} |
|
|
|
void Platform::destroyWindow() |
|
{ |
|
if(x11.glxContext) { |
|
glXMakeCurrent(x11.display, None, NULL); |
|
glXDestroyContext(x11.display, x11.glxContext); |
|
x11.glxContext = NULL; |
|
} |
|
|
|
if(x11.visual) { |
|
XFree(x11.visual); |
|
x11.visual = NULL; |
|
} |
|
|
|
if(x11.colormap != None) { |
|
XFreeColormap(x11.display, x11.colormap); |
|
x11.colormap = 0; |
|
} |
|
|
|
if(x11.window != None) { |
|
XUnmapWindow(x11.display, x11.window); |
|
XDestroyWindow(x11.display, x11.window); |
|
x11.window = None; |
|
} |
|
|
|
if(x11.xic) { |
|
XDestroyIC(x11.xic); |
|
x11.xic = NULL; |
|
} |
|
|
|
if(x11.xim) { |
|
XCloseIM(x11.xim); |
|
x11.xim = NULL; |
|
} |
|
} |
|
|
|
void Platform::showWindow() |
|
{ |
|
XMapWindow(x11.display, x11.window); |
|
} |
|
|
|
void Platform::setWindowTitle(const char *title) |
|
{ |
|
XStoreName(x11.display, x11.window, title); |
|
XSetIconName(x11.display, x11.window, title); |
|
} |
|
|
|
void *Platform::getExtensionProcAddress(const char *ext) |
|
{ |
|
return (void*)glXGetProcAddressARB((const GLubyte*)ext); |
|
} |
|
|
|
bool Platform::isExtensionSupported(const char *ext) |
|
{ |
|
const char *exts = glXQueryExtensionsString(x11.display, DefaultScreen(x11.display)); |
|
if(strstr(exts, ext)) |
|
return true; |
|
return true; |
|
} |
|
|
|
const char *Platform::getTextFromClipboard() |
|
{ |
|
Window ownerWindow = XGetSelectionOwner(x11.display, x11.atomClipboard); |
|
if(ownerWindow == x11.window) |
|
return x11.clipboardText.c_str(); |
|
|
|
std::string clipboard = ""; |
|
if(ownerWindow != None) { |
|
XConvertSelection(x11.display, x11.atomClipboard, XA_STRING, 0, ownerWindow, CurrentTime); |
|
XFlush(x11.display); |
|
|
|
// hack to wait SelectioNotify event, otherwise we will get wrong clipboard pastes |
|
sleep(100); |
|
|
|
// check for data |
|
Atom type; |
|
int format; |
|
unsigned long numItems, bytesLeft, dummy; |
|
unsigned char *data; |
|
XGetWindowProperty(x11.display, ownerWindow, |
|
XA_STRING, |
|
0, 0, 0, |
|
AnyPropertyType, |
|
&type, |
|
&format, |
|
&numItems, |
|
&bytesLeft, |
|
&data); |
|
if(bytesLeft > 0) { |
|
// get the data get |
|
int result = XGetWindowProperty(x11.display, ownerWindow, |
|
XA_STRING, |
|
0, |
|
bytesLeft, |
|
0, |
|
AnyPropertyType, |
|
&type, |
|
&format, |
|
&numItems, |
|
&dummy, |
|
&data); |
|
if(result == Success) |
|
clipboard = (const char*)data; |
|
XFree(data); |
|
} |
|
} |
|
return clipboard.c_str(); |
|
} |
|
|
|
void Platform::copyToClipboard(const char *text) |
|
{ |
|
x11.clipboardText = text; |
|
XSetSelectionOwner(x11.display, x11.atomClipboard, x11.window, CurrentTime); |
|
XFlush(x11.display); |
|
} |
|
|
|
void Platform::hideMouseCursor() |
|
{ |
|
if(x11.cursor == None) { |
|
char bm[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; |
|
Pixmap pix = XCreateBitmapFromData(x11.display, x11.window, bm, 8, 8); |
|
XColor black; |
|
memset(&black, 0, sizeof(XColor)); |
|
black.flags = DoRed | DoGreen | DoBlue; |
|
x11.cursor = XCreatePixmapCursor(x11.display, pix, pix, &black, &black, 0, 0); |
|
XFreePixmap(x11.display, pix); |
|
} |
|
XDefineCursor(x11.display, x11.window, x11.cursor); |
|
} |
|
|
|
void Platform::showMouseCursor() |
|
{ |
|
XUndefineCursor(x11.display, x11.window); |
|
if(x11.cursor != None) { |
|
XFreeCursor(x11.display, x11.cursor); |
|
x11.cursor = None; |
|
} |
|
} |
|
|
|
void Platform::setVsync(bool enable) |
|
{ |
|
typedef GLint (*glSwapIntervalProc)(GLint); |
|
glSwapIntervalProc glSwapInterval = NULL; |
|
|
|
if(isExtensionSupported("GLX_MESA_swap_control")) |
|
glSwapInterval = (glSwapIntervalProc)getExtensionProcAddress("glXSwapIntervalMESA"); |
|
else if(isExtensionSupported("GLX_SGI_swap_control")) |
|
glSwapInterval = (glSwapIntervalProc)getExtensionProcAddress("glXSwapIntervalSGI"); |
|
|
|
if(glSwapInterval) |
|
glSwapInterval(enable ? 1 : 0); |
|
} |
|
|
|
void Platform::swapBuffers() |
|
{ |
|
glXSwapBuffers(x11.display, x11.window); |
|
} |
|
|
|
bool Platform::isWindowFocused() |
|
{ |
|
return x11.focused; |
|
} |
|
|
|
bool Platform::isWindowVisible() |
|
{ |
|
return x11.visible; |
|
}
|
|
|