2015-08-18 22:43:30 +00:00
|
|
|
#include "boo/IWindow.hpp"
|
|
|
|
#include "boo/IGraphicsContext.hpp"
|
|
|
|
#include "boo/IApplication.hpp"
|
2015-10-31 04:28:21 +00:00
|
|
|
#include "boo/graphicsdev/GL.hpp"
|
2017-02-15 06:00:10 +00:00
|
|
|
#include "boo/audiodev/IAudioVoiceEngine.hpp"
|
2018-01-22 07:33:47 +00:00
|
|
|
#include "boo/graphicsdev/glew.h"
|
2015-05-09 05:33:48 +00:00
|
|
|
|
2016-02-23 01:13:03 +00:00
|
|
|
#if BOO_HAS_VULKAN
|
2016-02-22 02:47:45 +00:00
|
|
|
#include "boo/graphicsdev/Vulkan.hpp"
|
2016-02-23 01:13:03 +00:00
|
|
|
#include <X11/Xlib-xcb.h>
|
|
|
|
#endif
|
2016-02-22 02:47:45 +00:00
|
|
|
|
2015-08-18 19:40:26 +00:00
|
|
|
#include <limits.h>
|
2017-12-29 07:54:26 +00:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <cstring>
|
2016-06-30 04:54:29 +00:00
|
|
|
|
2015-11-17 04:20:11 +00:00
|
|
|
#include <thread>
|
|
|
|
#include <mutex>
|
|
|
|
#include <condition_variable>
|
2015-05-09 05:33:48 +00:00
|
|
|
|
2015-10-31 04:28:21 +00:00
|
|
|
#include <GL/glx.h>
|
|
|
|
|
2015-05-09 05:33:48 +00:00
|
|
|
#define XK_MISCELLANY
|
|
|
|
#define XK_XKB_KEYS
|
|
|
|
#define XK_LATIN1
|
|
|
|
#include <X11/keysymdef.h>
|
2015-12-23 06:10:46 +00:00
|
|
|
#include <X11/XKBlib.h>
|
2015-10-31 04:28:21 +00:00
|
|
|
#include <X11/extensions/XInput2.h>
|
|
|
|
#include <X11/Xatom.h>
|
2017-11-02 09:24:50 +00:00
|
|
|
#include <X11/extensions/Xrandr.h>
|
2016-03-04 23:02:18 +00:00
|
|
|
#include "logvisor/logvisor.hpp"
|
2015-05-09 05:33:48 +00:00
|
|
|
|
2015-11-30 00:20:20 +00:00
|
|
|
#include "XlibCommon.hpp"
|
|
|
|
|
2015-12-05 00:41:30 +00:00
|
|
|
#define REF_DPMM 3.78138
|
2015-05-09 05:33:48 +00:00
|
|
|
#define FS_ATOM "_NET_WM_STATE_FULLSCREEN"
|
2015-05-06 00:50:57 +00:00
|
|
|
|
2015-11-05 07:30:40 +00:00
|
|
|
#define MWM_HINTS_FUNCTIONS (1L << 0)
|
|
|
|
#define MWM_HINTS_DECORATIONS (1L << 1)
|
|
|
|
|
|
|
|
#define MWM_DECOR_BORDER (1L<<1)
|
|
|
|
#define MWM_DECOR_RESIZEH (1L<<2)
|
|
|
|
#define MWM_DECOR_TITLE (1L<<3)
|
|
|
|
#define MWM_DECOR_MENU (1L<<4)
|
|
|
|
#define MWM_DECOR_MINIMIZE (1L<<5)
|
|
|
|
#define MWM_DECOR_MAXIMIZE (1L<<6)
|
|
|
|
|
|
|
|
#define MWM_FUNC_RESIZE (1L<<1)
|
|
|
|
#define MWM_FUNC_MOVE (1L<<2)
|
|
|
|
#define MWM_FUNC_MINIMIZE (1L<<3)
|
|
|
|
#define MWM_FUNC_MAXIMIZE (1L<<4)
|
|
|
|
#define MWM_FUNC_CLOSE (1L<<5)
|
|
|
|
|
2015-11-21 01:12:22 +00:00
|
|
|
#undef None
|
2015-10-31 04:28:21 +00:00
|
|
|
|
2015-10-31 19:21:23 +00:00
|
|
|
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
|
2015-11-17 04:20:11 +00:00
|
|
|
static glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
|
|
|
|
typedef int (*glXWaitVideoSyncSGIProc)(int divisor, int remainder, unsigned int* count);
|
|
|
|
static glXWaitVideoSyncSGIProc glXWaitVideoSyncSGI = 0;
|
2016-02-15 17:53:54 +00:00
|
|
|
static bool s_glxError;
|
|
|
|
static int ctxErrorHandler(Display *dpy, XErrorEvent *ev)
|
2015-10-31 19:21:23 +00:00
|
|
|
{
|
2016-02-15 17:53:54 +00:00
|
|
|
s_glxError = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-23 01:13:03 +00:00
|
|
|
static const int ContextAttribList[7][7] =
|
2016-02-15 17:53:54 +00:00
|
|
|
{
|
|
|
|
{ GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 5,
|
|
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
|
|
0
|
|
|
|
},
|
|
|
|
{ GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 4,
|
|
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
2016-02-23 01:13:03 +00:00
|
|
|
0
|
2016-02-15 17:53:54 +00:00
|
|
|
},
|
|
|
|
{ GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 3,
|
|
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
2016-02-23 01:13:03 +00:00
|
|
|
0
|
2016-02-15 17:53:54 +00:00
|
|
|
},
|
|
|
|
{ GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 2,
|
|
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
|
|
0
|
|
|
|
},
|
|
|
|
{ GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 1,
|
|
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
|
|
0
|
|
|
|
},
|
|
|
|
{ GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 0,
|
|
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
|
|
0
|
|
|
|
},
|
|
|
|
{ GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
|
2015-10-31 19:21:23 +00:00
|
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 3,
|
2015-11-05 04:31:30 +00:00
|
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
2015-11-21 01:12:22 +00:00
|
|
|
0
|
2016-02-15 17:53:54 +00:00
|
|
|
},
|
2015-10-31 19:21:23 +00:00
|
|
|
};
|
|
|
|
|
2016-01-27 23:25:30 +00:00
|
|
|
extern "C" const uint8_t MAINICON_NETWM[];
|
|
|
|
extern "C" const size_t MAINICON_NETWM_SZ;
|
|
|
|
|
2015-05-06 00:50:57 +00:00
|
|
|
namespace boo
|
|
|
|
{
|
2016-03-04 23:02:18 +00:00
|
|
|
static logvisor::Module Log("boo::WindowXlib");
|
2018-05-20 06:11:49 +00:00
|
|
|
std::unique_ptr<IGraphicsCommandQueue> _NewGLCommandQueue(IGraphicsContext* parent, GLContext* glCtx);
|
|
|
|
std::unique_ptr<IGraphicsDataFactory> _NewGLDataFactory(IGraphicsContext* parent, GLContext* glCtx);
|
2016-02-22 02:47:45 +00:00
|
|
|
#if BOO_HAS_VULKAN
|
2018-05-20 06:11:49 +00:00
|
|
|
std::unique_ptr<IGraphicsCommandQueue> _NewVulkanCommandQueue(VulkanContext* ctx,
|
|
|
|
VulkanContext::Window* windowCtx,
|
|
|
|
IGraphicsContext* parent);
|
|
|
|
std::unique_ptr<IGraphicsDataFactory> _NewVulkanDataFactory(IGraphicsContext* parent, VulkanContext* ctx);
|
2016-02-22 02:47:45 +00:00
|
|
|
#endif
|
2015-11-02 10:07:15 +00:00
|
|
|
void _XlibUpdateLastGlxCtx(GLXContext lastGlxCtx);
|
2015-10-31 04:28:21 +00:00
|
|
|
void GLXExtensionCheck();
|
|
|
|
void GLXEnableVSync(Display* disp, GLXWindow drawable);
|
2015-05-09 05:33:48 +00:00
|
|
|
|
2015-05-13 08:51:18 +00:00
|
|
|
extern int XINPUT_OPCODE;
|
|
|
|
|
2015-12-24 20:55:23 +00:00
|
|
|
static std::string translateUTF8(XKeyEvent* ev, XIC xIC)
|
|
|
|
{
|
|
|
|
char chs[512];
|
|
|
|
KeySym ks;
|
|
|
|
Status stat;
|
|
|
|
int len = Xutf8LookupString(xIC, ev, chs, 512, &ks, &stat);
|
|
|
|
if (len > 1 && (stat == XLookupChars || stat == XLookupBoth))
|
|
|
|
return std::string(chs, len);
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
|
|
|
static char translateKeysym(XKeyEvent* ev, ESpecialKey& specialSym, EModifierKey& modifierSym)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-12-24 03:30:52 +00:00
|
|
|
KeySym sym = XLookupKeysym(ev, 0);
|
|
|
|
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::None;
|
|
|
|
modifierSym = EModifierKey::None;
|
2015-05-09 05:33:48 +00:00
|
|
|
if (sym >= XK_F1 && sym <= XK_F12)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey(int(ESpecialKey::F1) + sym - XK_F1);
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Escape)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::Esc;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Return)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::Enter;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_BackSpace)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::Backspace;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Insert)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::Insert;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Delete)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::Delete;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Home)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::Home;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_End)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::End;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Page_Up)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::PgUp;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Page_Down)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::PgDown;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Left)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::Left;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Right)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::Right;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Up)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::Up;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Down)
|
2015-11-21 01:12:22 +00:00
|
|
|
specialSym = ESpecialKey::Down;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Shift_L || sym == XK_Shift_R)
|
2015-11-21 01:12:22 +00:00
|
|
|
modifierSym = EModifierKey::Shift;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Control_L || sym == XK_Control_R)
|
2015-11-21 01:12:22 +00:00
|
|
|
modifierSym = EModifierKey::Ctrl;
|
2015-05-09 05:33:48 +00:00
|
|
|
else if (sym == XK_Alt_L || sym == XK_Alt_R)
|
2015-11-21 01:12:22 +00:00
|
|
|
modifierSym = EModifierKey::Alt;
|
2015-05-09 05:33:48 +00:00
|
|
|
else
|
2015-12-23 06:10:46 +00:00
|
|
|
{
|
2015-12-24 20:55:23 +00:00
|
|
|
char ch = 0;
|
|
|
|
KeySym ks;
|
|
|
|
XLookupString(ev, (char*)&ch, 1, &ks, nullptr);
|
|
|
|
return ch;
|
2015-12-23 06:10:46 +00:00
|
|
|
}
|
2015-05-09 05:33:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-21 01:12:22 +00:00
|
|
|
static EModifierKey translateModifiers(unsigned state)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-11-21 01:12:22 +00:00
|
|
|
EModifierKey retval = EModifierKey::None;
|
2015-10-31 04:28:21 +00:00
|
|
|
if (state & ShiftMask)
|
2015-11-21 01:12:22 +00:00
|
|
|
retval |= EModifierKey::Shift;
|
2015-10-31 04:28:21 +00:00
|
|
|
if (state & ControlMask)
|
2015-11-21 01:12:22 +00:00
|
|
|
retval |= EModifierKey::Ctrl;
|
2015-10-31 04:28:21 +00:00
|
|
|
if (state & Mod1Mask)
|
2015-11-21 01:12:22 +00:00
|
|
|
retval |= EModifierKey::Alt;
|
2015-05-09 05:33:48 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2015-11-21 01:12:22 +00:00
|
|
|
static EMouseButton translateButton(unsigned detail)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-11-21 01:12:22 +00:00
|
|
|
switch (detail)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
return EMouseButton::Primary;
|
|
|
|
case 3:
|
|
|
|
return EMouseButton::Secondary;
|
|
|
|
case 2:
|
|
|
|
return EMouseButton::Middle;
|
|
|
|
case 8:
|
|
|
|
return EMouseButton::Aux1;
|
|
|
|
case 9:
|
|
|
|
return EMouseButton::Aux2;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
return EMouseButton::None;
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
|
|
|
|
2015-12-24 03:30:52 +00:00
|
|
|
struct XlibAtoms
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
Atom m_wmProtocols = 0;
|
|
|
|
Atom m_wmDeleteWindow = 0;
|
2016-01-27 23:25:30 +00:00
|
|
|
Atom m_netSupported = 0;
|
|
|
|
Atom m_netwmIcon = 0;
|
|
|
|
Atom m_netwmIconName = 0;
|
2015-10-31 04:28:21 +00:00
|
|
|
Atom m_netwmState = 0;
|
|
|
|
Atom m_netwmStateFullscreen = 0;
|
|
|
|
Atom m_netwmStateAdd = 0;
|
|
|
|
Atom m_netwmStateRemove = 0;
|
2015-10-31 19:21:23 +00:00
|
|
|
Atom m_motifWmHints = 0;
|
2015-12-24 03:30:52 +00:00
|
|
|
Atom m_targets = 0;
|
|
|
|
Atom m_clipboard = 0;
|
|
|
|
Atom m_clipdata = 0;
|
|
|
|
Atom m_utf8String = 0;
|
|
|
|
Atom m_imagePng = 0;
|
|
|
|
XlibAtoms(Display* disp)
|
2015-10-31 04:28:21 +00:00
|
|
|
{
|
|
|
|
m_wmProtocols = XInternAtom(disp, "WM_PROTOCOLS", True);
|
|
|
|
m_wmDeleteWindow = XInternAtom(disp, "WM_DELETE_WINDOW", True);
|
2016-01-27 23:25:30 +00:00
|
|
|
m_netSupported = XInternAtom(disp, "_NET_SUPPORTED", True);
|
|
|
|
m_netwmIcon = XInternAtom(disp, "_NET_WM_ICON", False);
|
|
|
|
m_netwmIconName = XInternAtom(disp, "_NET_WM_ICON_NAME", False);
|
2015-10-31 04:28:21 +00:00
|
|
|
m_netwmState = XInternAtom(disp, "_NET_WM_STATE", False);
|
|
|
|
m_netwmStateFullscreen = XInternAtom(disp, "_NET_WM_STATE_FULLSCREEN", False);
|
|
|
|
m_netwmStateAdd = XInternAtom(disp, "_NET_WM_STATE_ADD", False);
|
|
|
|
m_netwmStateRemove = XInternAtom(disp, "_NET_WM_STATE_REMOVE", False);
|
2015-10-31 19:21:23 +00:00
|
|
|
m_motifWmHints = XInternAtom(disp, "_MOTIF_WM_HINTS", True);
|
2015-12-24 03:30:52 +00:00
|
|
|
m_targets = XInternAtom(disp, "TARGETS", False);
|
|
|
|
m_clipboard = XInternAtom(disp, "CLIPBOARD", False);
|
|
|
|
m_clipdata = XInternAtom(disp, "CLIPDATA", False);
|
|
|
|
m_utf8String = XInternAtom(disp, "UTF8_STRING", False);
|
|
|
|
m_imagePng = XInternAtom(disp, "image/png", False);
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
|
|
|
};
|
2015-12-24 03:30:52 +00:00
|
|
|
static XlibAtoms* S_ATOMS = NULL;
|
|
|
|
|
|
|
|
static Atom GetClipboardTypeAtom(EClipboardType t)
|
|
|
|
{
|
|
|
|
switch (t)
|
|
|
|
{
|
|
|
|
case EClipboardType::String:
|
|
|
|
return XA_STRING;
|
|
|
|
case EClipboardType::UTF8String:
|
|
|
|
return S_ATOMS->m_utf8String;
|
|
|
|
case EClipboardType::PNGImage:
|
|
|
|
return S_ATOMS->m_imagePng;
|
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
}
|
2015-05-09 05:33:48 +00:00
|
|
|
|
2015-10-31 19:21:23 +00:00
|
|
|
static void genFrameDefault(Screen* screen, int& xOut, int& yOut, int& wOut, int& hOut)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
float width = screen->width * 2.0 / 3.0;
|
|
|
|
float height = screen->height * 2.0 / 3.0;
|
2015-10-31 19:21:23 +00:00
|
|
|
xOut = (screen->width - width) / 2.0;
|
|
|
|
yOut = (screen->height - height) / 2.0;
|
|
|
|
wOut = width;
|
|
|
|
hOut = height;
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2016-02-22 02:47:45 +00:00
|
|
|
|
2017-11-02 09:24:50 +00:00
|
|
|
static void genFrameDefault(XRRMonitorInfo* screen, int& xOut, int& yOut, int& wOut, int& hOut)
|
|
|
|
{
|
|
|
|
float width = screen->width * 2.0 / 3.0;
|
|
|
|
float height = screen->height * 2.0 / 3.0;
|
|
|
|
xOut = (screen->width - width) / 2.0 + screen->x;
|
|
|
|
yOut = (screen->height - height) / 2.0 + screen->y;
|
|
|
|
wOut = width;
|
|
|
|
hOut = height;
|
|
|
|
}
|
|
|
|
|
2016-02-22 02:47:45 +00:00
|
|
|
struct GraphicsContextXlib : IGraphicsContext
|
2015-08-28 00:10:46 +00:00
|
|
|
{
|
|
|
|
EGraphicsAPI m_api;
|
|
|
|
EPixelFormat m_pf;
|
2016-02-24 03:11:58 +00:00
|
|
|
uint32_t m_drawSamples;
|
2015-08-28 00:10:46 +00:00
|
|
|
IWindow* m_parentWindow;
|
2018-01-07 09:25:30 +00:00
|
|
|
GLContext* m_glCtx;
|
2016-02-22 02:47:45 +00:00
|
|
|
Display* m_xDisp;
|
|
|
|
|
|
|
|
std::mutex m_vsyncmt;
|
|
|
|
std::condition_variable m_vsynccv;
|
|
|
|
|
2018-01-07 09:25:30 +00:00
|
|
|
GraphicsContextXlib(EGraphicsAPI api, EPixelFormat pf, IWindow* parentWindow, Display* disp, GLContext* glCtx)
|
2016-02-22 02:47:45 +00:00
|
|
|
: m_api(api),
|
|
|
|
m_pf(pf),
|
|
|
|
m_parentWindow(parentWindow),
|
2018-01-07 09:25:30 +00:00
|
|
|
m_glCtx(glCtx),
|
2016-02-22 02:47:45 +00:00
|
|
|
m_xDisp(disp) {}
|
|
|
|
virtual void destroy()=0;
|
2017-02-16 03:17:18 +00:00
|
|
|
virtual void resized(const SWindowRect& rect)=0;
|
2016-02-22 02:47:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct GraphicsContextXlibGLX : GraphicsContextXlib
|
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
GLXContext m_lastCtx = 0;
|
2015-08-28 00:10:46 +00:00
|
|
|
|
2015-10-31 04:28:21 +00:00
|
|
|
GLXFBConfig m_fbconfig = 0;
|
|
|
|
int m_visualid = 0;
|
2016-05-19 02:18:52 +00:00
|
|
|
int m_attribIdx = 0;
|
2015-10-31 04:28:21 +00:00
|
|
|
GLXWindow m_glxWindow = 0;
|
|
|
|
GLXContext m_glxCtx = 0;
|
2015-08-28 00:10:46 +00:00
|
|
|
|
2018-05-20 06:11:49 +00:00
|
|
|
std::unique_ptr<IGraphicsDataFactory> m_dataFactory;
|
|
|
|
std::unique_ptr<IGraphicsCommandQueue> m_commandQueue;
|
2015-11-17 06:41:32 +00:00
|
|
|
GLXContext m_mainCtx = 0;
|
2015-10-31 04:28:21 +00:00
|
|
|
GLXContext m_loadCtx = 0;
|
2015-10-30 00:00:56 +00:00
|
|
|
|
2015-11-17 04:20:11 +00:00
|
|
|
std::thread m_vsyncThread;
|
|
|
|
bool m_vsyncRunning;
|
|
|
|
|
2015-08-28 00:10:46 +00:00
|
|
|
public:
|
|
|
|
IWindowCallback* m_callback;
|
|
|
|
|
2016-02-22 02:47:45 +00:00
|
|
|
GraphicsContextXlibGLX(EGraphicsAPI api, IWindow* parentWindow,
|
|
|
|
Display* display, int defaultScreen,
|
2018-01-07 09:25:30 +00:00
|
|
|
GLXContext lastCtx, uint32_t& visualIdOut, GLContext* glCtx)
|
2018-01-22 07:33:47 +00:00
|
|
|
: GraphicsContextXlib(api, glCtx->m_deepColor ? EPixelFormat::RGBA16 : EPixelFormat::RGBA8,
|
|
|
|
parentWindow, display, glCtx), m_lastCtx(lastCtx)
|
2015-10-31 04:28:21 +00:00
|
|
|
{
|
2018-01-07 09:25:30 +00:00
|
|
|
m_dataFactory = _NewGLDataFactory(this, m_glCtx);
|
2015-11-12 04:31:59 +00:00
|
|
|
|
2015-10-31 04:28:21 +00:00
|
|
|
/* Query framebuffer configurations */
|
|
|
|
GLXFBConfig* fbConfigs = nullptr;
|
|
|
|
int numFBConfigs = 0;
|
|
|
|
fbConfigs = glXGetFBConfigs(display, defaultScreen, &numFBConfigs);
|
|
|
|
if (!fbConfigs || numFBConfigs == 0)
|
2015-08-28 00:10:46 +00:00
|
|
|
{
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "glXGetFBConfigs failed");
|
2015-10-31 04:28:21 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-08-28 00:10:46 +00:00
|
|
|
|
2015-10-31 04:28:21 +00:00
|
|
|
for (int i=0 ; i<numFBConfigs ; ++i)
|
2015-08-28 00:10:46 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
GLXFBConfig config = fbConfigs[i];
|
|
|
|
int visualId, depthSize, colorSize, doubleBuffer;
|
|
|
|
glXGetFBConfigAttrib(display, config, GLX_VISUAL_ID, &visualId);
|
|
|
|
glXGetFBConfigAttrib(display, config, GLX_DEPTH_SIZE, &depthSize);
|
|
|
|
glXGetFBConfigAttrib(display, config, GLX_BUFFER_SIZE, &colorSize);
|
|
|
|
glXGetFBConfigAttrib(display, config, GLX_DOUBLEBUFFER, &doubleBuffer);
|
2015-08-28 00:10:46 +00:00
|
|
|
|
|
|
|
/* Double-buffer only */
|
2018-05-06 22:44:31 +00:00
|
|
|
if (!doubleBuffer || !visualId)
|
2015-08-28 00:10:46 +00:00
|
|
|
continue;
|
|
|
|
|
2015-11-21 01:12:22 +00:00
|
|
|
if (m_pf == EPixelFormat::RGBA8 && colorSize >= 32)
|
2015-08-28 00:10:46 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
m_fbconfig = config;
|
2015-08-28 00:10:46 +00:00
|
|
|
m_visualid = visualId;
|
|
|
|
break;
|
|
|
|
}
|
2018-01-22 07:33:47 +00:00
|
|
|
else if (m_pf == EPixelFormat::RGBA16)
|
|
|
|
{
|
|
|
|
if (colorSize >= 64)
|
|
|
|
{
|
|
|
|
m_fbconfig = config;
|
|
|
|
m_visualid = visualId;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (!m_visualid && colorSize >= 32)
|
|
|
|
{
|
|
|
|
m_fbconfig = config;
|
|
|
|
m_visualid = visualId;
|
|
|
|
}
|
|
|
|
}
|
2015-11-21 01:12:22 +00:00
|
|
|
else if (m_pf == EPixelFormat::RGBA8_Z24 && colorSize >= 32 && depthSize >= 24)
|
2015-08-28 00:10:46 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
m_fbconfig = config;
|
2015-08-28 00:10:46 +00:00
|
|
|
m_visualid = visualId;
|
|
|
|
break;
|
|
|
|
}
|
2015-11-21 01:12:22 +00:00
|
|
|
else if (m_pf == EPixelFormat::RGBAF32 && colorSize >= 128)
|
2015-08-28 00:10:46 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
m_fbconfig = config;
|
2015-08-28 00:10:46 +00:00
|
|
|
m_visualid = visualId;
|
|
|
|
break;
|
|
|
|
}
|
2015-11-21 01:12:22 +00:00
|
|
|
else if (m_pf == EPixelFormat::RGBAF32_Z24 && colorSize >= 128 && depthSize >= 24)
|
2015-08-28 00:10:46 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
m_fbconfig = config;
|
2015-08-28 00:10:46 +00:00
|
|
|
m_visualid = visualId;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
XFree(fbConfigs);
|
2015-08-28 00:10:46 +00:00
|
|
|
|
|
|
|
if (!m_fbconfig)
|
|
|
|
{
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to find suitable pixel format");
|
2015-08-28 00:10:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
visualIdOut = m_visualid;
|
|
|
|
}
|
|
|
|
|
2015-12-06 01:25:43 +00:00
|
|
|
void destroy()
|
2015-08-28 00:10:46 +00:00
|
|
|
{
|
2015-10-28 01:47:55 +00:00
|
|
|
if (m_glxCtx)
|
2015-12-06 01:25:43 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
glXDestroyContext(m_xDisp, m_glxCtx);
|
2015-12-06 01:25:43 +00:00
|
|
|
m_glxCtx = nullptr;
|
|
|
|
}
|
2015-10-28 01:47:55 +00:00
|
|
|
if (m_glxWindow)
|
2015-12-06 01:25:43 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
glXDestroyWindow(m_xDisp, m_glxWindow);
|
2015-12-06 01:25:43 +00:00
|
|
|
m_glxWindow = 0;
|
|
|
|
}
|
2015-10-30 00:00:56 +00:00
|
|
|
if (m_loadCtx)
|
2015-12-06 01:25:43 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
glXDestroyContext(m_xDisp, m_loadCtx);
|
2015-12-06 01:25:43 +00:00
|
|
|
m_loadCtx = nullptr;
|
|
|
|
}
|
|
|
|
if (m_vsyncRunning)
|
|
|
|
{
|
|
|
|
m_vsyncRunning = false;
|
|
|
|
m_vsyncThread.join();
|
|
|
|
}
|
2015-08-28 00:10:46 +00:00
|
|
|
}
|
|
|
|
|
2016-02-22 02:47:45 +00:00
|
|
|
~GraphicsContextXlibGLX() {destroy();}
|
2015-12-06 01:25:43 +00:00
|
|
|
|
2017-02-16 03:17:18 +00:00
|
|
|
void resized(const SWindowRect& rect)
|
2016-06-27 04:33:01 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-08-28 00:10:46 +00:00
|
|
|
void _setCallback(IWindowCallback* cb)
|
|
|
|
{
|
|
|
|
m_callback = cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
EGraphicsAPI getAPI() const
|
|
|
|
{
|
|
|
|
return m_api;
|
|
|
|
}
|
|
|
|
|
|
|
|
EPixelFormat getPixelFormat() const
|
|
|
|
{
|
|
|
|
return m_pf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setPixelFormat(EPixelFormat pf)
|
|
|
|
{
|
2015-11-21 01:12:22 +00:00
|
|
|
if (pf > EPixelFormat::RGBAF32_Z24)
|
2015-08-28 00:10:46 +00:00
|
|
|
return;
|
|
|
|
m_pf = pf;
|
|
|
|
}
|
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
bool initializeContext(void*)
|
2015-08-28 00:10:46 +00:00
|
|
|
{
|
2015-11-17 04:20:11 +00:00
|
|
|
if (!glXCreateContextAttribsARB)
|
|
|
|
{
|
|
|
|
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
|
|
|
|
glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");
|
|
|
|
if (!glXCreateContextAttribsARB)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to resolve glXCreateContextAttribsARB");
|
2015-11-17 04:20:11 +00:00
|
|
|
}
|
|
|
|
if (!glXWaitVideoSyncSGI)
|
|
|
|
{
|
|
|
|
glXWaitVideoSyncSGI = (glXWaitVideoSyncSGIProc)
|
|
|
|
glXGetProcAddressARB((const GLubyte*)"glXWaitVideoSyncSGI");
|
|
|
|
if (!glXWaitVideoSyncSGI)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to resolve glXWaitVideoSyncSGI");
|
2015-11-17 04:20:11 +00:00
|
|
|
}
|
|
|
|
|
2016-02-15 17:53:54 +00:00
|
|
|
s_glxError = false;
|
2016-05-19 02:18:52 +00:00
|
|
|
XErrorHandler oldHandler = XSetErrorHandler(ctxErrorHandler);
|
|
|
|
for (m_attribIdx=0 ; m_attribIdx<std::extent<decltype(ContextAttribList)>::value ; ++m_attribIdx)
|
2016-02-15 17:53:54 +00:00
|
|
|
{
|
2016-05-19 02:18:52 +00:00
|
|
|
m_glxCtx = glXCreateContextAttribsARB(m_xDisp, m_fbconfig, m_lastCtx, True, ContextAttribList[m_attribIdx]);
|
2016-02-15 17:53:54 +00:00
|
|
|
if (m_glxCtx)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
XSetErrorHandler(oldHandler);
|
2015-10-31 04:28:21 +00:00
|
|
|
if (!m_glxCtx)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make new GLX context");
|
2015-10-31 04:28:21 +00:00
|
|
|
m_glxWindow = glXCreateWindow(m_xDisp, m_fbconfig, m_parentWindow->getPlatformHandle(), nullptr);
|
|
|
|
if (!m_glxWindow)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make new GLX window");
|
2015-11-02 10:07:15 +00:00
|
|
|
_XlibUpdateLastGlxCtx(m_glxCtx);
|
2015-10-31 04:28:21 +00:00
|
|
|
|
2018-01-22 07:33:47 +00:00
|
|
|
if (!glXMakeCurrent(m_xDisp, DefaultRootWindow(m_xDisp), m_glxCtx))
|
|
|
|
Log.report(logvisor::Fatal, "unable to make GLX context current");
|
|
|
|
if (glewInit() != GLEW_OK)
|
|
|
|
Log.report(logvisor::Fatal, "glewInit failed");
|
|
|
|
glXMakeCurrent(m_xDisp, 0, 0);
|
|
|
|
|
2015-11-17 04:20:11 +00:00
|
|
|
/* Spawn vsync thread */
|
|
|
|
m_vsyncRunning = true;
|
|
|
|
std::mutex initmt;
|
|
|
|
std::condition_variable initcv;
|
|
|
|
std::unique_lock<std::mutex> outerLk(initmt);
|
|
|
|
m_vsyncThread = std::thread([&]()
|
|
|
|
{
|
|
|
|
Display* vsyncDisp;
|
|
|
|
GLXContext vsyncCtx;
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> innerLk(initmt);
|
|
|
|
|
|
|
|
vsyncDisp = XOpenDisplay(0);
|
|
|
|
if (!vsyncDisp)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to open new vsync display");
|
2015-12-04 01:33:43 +00:00
|
|
|
XLockDisplay(vsyncDisp);
|
2015-11-17 04:20:11 +00:00
|
|
|
|
2015-11-21 01:12:22 +00:00
|
|
|
static int attributeList[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, 0 };
|
2016-02-21 06:27:54 +00:00
|
|
|
XVisualInfo *vi = glXChooseVisual(vsyncDisp, DefaultScreen(vsyncDisp), attributeList);
|
2015-11-18 05:58:37 +00:00
|
|
|
|
|
|
|
vsyncCtx = glXCreateContext(vsyncDisp, vi, nullptr, True);
|
2015-11-17 04:20:11 +00:00
|
|
|
if (!vsyncCtx)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make new vsync GLX context");
|
2015-11-17 04:20:11 +00:00
|
|
|
|
|
|
|
if (!glXMakeCurrent(vsyncDisp, DefaultRootWindow(vsyncDisp), vsyncCtx))
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make vsync context current");
|
2015-11-17 04:20:11 +00:00
|
|
|
}
|
|
|
|
initcv.notify_one();
|
|
|
|
|
|
|
|
while (m_vsyncRunning)
|
|
|
|
{
|
2016-05-19 02:18:52 +00:00
|
|
|
{
|
|
|
|
unsigned int sync;
|
|
|
|
int err = glXWaitVideoSyncSGI(1, 0, &sync);
|
|
|
|
if (err)
|
|
|
|
Log.report(logvisor::Fatal, "wait err");
|
|
|
|
}
|
2015-11-17 04:20:11 +00:00
|
|
|
m_vsynccv.notify_one();
|
|
|
|
}
|
|
|
|
|
2015-11-21 01:12:22 +00:00
|
|
|
glXMakeCurrent(vsyncDisp, 0, nullptr);
|
2015-11-17 04:20:11 +00:00
|
|
|
glXDestroyContext(vsyncDisp, vsyncCtx);
|
2015-12-04 01:33:43 +00:00
|
|
|
XUnlockDisplay(vsyncDisp);
|
2015-11-17 04:20:11 +00:00
|
|
|
XCloseDisplay(vsyncDisp);
|
|
|
|
});
|
|
|
|
initcv.wait(outerLk);
|
2015-11-12 04:31:59 +00:00
|
|
|
|
|
|
|
XUnlockDisplay(m_xDisp);
|
2018-01-07 09:25:30 +00:00
|
|
|
m_commandQueue = _NewGLCommandQueue(this, m_glCtx);
|
2018-01-22 07:33:47 +00:00
|
|
|
m_commandQueue->startRenderer();
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2016-07-20 17:14:18 +00:00
|
|
|
|
|
|
|
return true;
|
2015-10-28 01:47:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void makeCurrent()
|
|
|
|
{
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-31 04:28:21 +00:00
|
|
|
if (!glXMakeContextCurrent(m_xDisp, m_glxWindow, m_glxWindow, m_glxCtx))
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make GLX context current");
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-10-31 04:28:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void postInit()
|
|
|
|
{
|
|
|
|
GLXExtensionCheck();
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-31 04:28:21 +00:00
|
|
|
GLXEnableVSync(m_xDisp, m_glxWindow);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-08-28 00:10:46 +00:00
|
|
|
}
|
|
|
|
|
2015-10-30 00:00:56 +00:00
|
|
|
IGraphicsCommandQueue* getCommandQueue()
|
|
|
|
{
|
2018-05-20 06:11:49 +00:00
|
|
|
return m_commandQueue.get();
|
2015-10-30 00:00:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
IGraphicsDataFactory* getDataFactory()
|
2015-10-29 04:44:38 +00:00
|
|
|
{
|
2018-05-20 06:11:49 +00:00
|
|
|
return m_dataFactory.get();
|
2015-10-29 04:44:38 +00:00
|
|
|
}
|
|
|
|
|
2015-11-17 06:41:32 +00:00
|
|
|
IGraphicsDataFactory* getMainContextDataFactory()
|
|
|
|
{
|
|
|
|
XLockDisplay(m_xDisp);
|
|
|
|
if (!m_mainCtx)
|
|
|
|
{
|
2016-02-15 17:53:54 +00:00
|
|
|
s_glxError = false;
|
2016-05-19 02:18:52 +00:00
|
|
|
XErrorHandler oldHandler = XSetErrorHandler(ctxErrorHandler);
|
|
|
|
m_mainCtx = glXCreateContextAttribsARB(m_xDisp, m_fbconfig, m_glxCtx, True, ContextAttribList[m_attribIdx]);
|
2016-02-15 17:53:54 +00:00
|
|
|
XSetErrorHandler(oldHandler);
|
2015-11-17 06:41:32 +00:00
|
|
|
if (!m_mainCtx)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make main GLX context");
|
2015-11-17 06:41:32 +00:00
|
|
|
}
|
|
|
|
if (!glXMakeContextCurrent(m_xDisp, m_glxWindow, m_glxWindow, m_mainCtx))
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make main GLX context current");
|
2015-11-17 06:41:32 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
return getDataFactory();
|
|
|
|
}
|
|
|
|
|
2015-10-30 00:00:56 +00:00
|
|
|
IGraphicsDataFactory* getLoadContextDataFactory()
|
2015-10-29 04:44:38 +00:00
|
|
|
{
|
2015-11-13 02:11:32 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-30 00:00:56 +00:00
|
|
|
if (!m_loadCtx)
|
|
|
|
{
|
2016-02-15 17:53:54 +00:00
|
|
|
s_glxError = false;
|
2016-05-19 02:18:52 +00:00
|
|
|
XErrorHandler oldHandler = XSetErrorHandler(ctxErrorHandler);
|
|
|
|
m_loadCtx = glXCreateContextAttribsARB(m_xDisp, m_fbconfig, m_glxCtx, True, ContextAttribList[m_attribIdx]);
|
2016-02-15 17:53:54 +00:00
|
|
|
XSetErrorHandler(oldHandler);
|
2015-10-31 04:28:21 +00:00
|
|
|
if (!m_loadCtx)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make load GLX context");
|
2015-10-30 00:00:56 +00:00
|
|
|
}
|
2015-11-04 01:02:05 +00:00
|
|
|
if (!glXMakeContextCurrent(m_xDisp, m_glxWindow, m_glxWindow, m_loadCtx))
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make load GLX context current");
|
2015-11-13 02:11:32 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-10-30 00:00:56 +00:00
|
|
|
return getDataFactory();
|
2015-10-29 04:44:38 +00:00
|
|
|
}
|
|
|
|
|
2015-10-31 04:28:21 +00:00
|
|
|
void present()
|
2016-01-15 00:24:11 +00:00
|
|
|
{ glXSwapBuffers(m_xDisp, m_glxWindow); }
|
2015-10-31 04:28:21 +00:00
|
|
|
|
2015-08-28 00:10:46 +00:00
|
|
|
};
|
2015-05-06 00:50:57 +00:00
|
|
|
|
2016-02-22 02:47:45 +00:00
|
|
|
#if BOO_HAS_VULKAN
|
|
|
|
struct GraphicsContextXlibVulkan : GraphicsContextXlib
|
|
|
|
{
|
2016-02-23 01:13:03 +00:00
|
|
|
xcb_connection_t* m_xcbConn;
|
2016-02-22 02:47:45 +00:00
|
|
|
VulkanContext* m_ctx;
|
2016-02-23 01:13:03 +00:00
|
|
|
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
2017-02-16 04:50:21 +00:00
|
|
|
VkFormat m_format = VK_FORMAT_UNDEFINED;
|
2016-07-02 03:44:57 +00:00
|
|
|
VkColorSpaceKHR m_colorspace;
|
2016-02-22 02:47:45 +00:00
|
|
|
|
|
|
|
GLXFBConfig m_fbconfig = 0;
|
|
|
|
int m_visualid = 0;
|
|
|
|
|
2018-05-20 06:11:49 +00:00
|
|
|
std::unique_ptr<IGraphicsDataFactory> m_dataFactory;
|
|
|
|
std::unique_ptr<IGraphicsCommandQueue> m_commandQueue;
|
2016-02-22 02:47:45 +00:00
|
|
|
|
|
|
|
std::thread m_vsyncThread;
|
|
|
|
bool m_vsyncRunning;
|
|
|
|
|
|
|
|
static void ThrowIfFailed(VkResult res)
|
|
|
|
{
|
|
|
|
if (res != VK_SUCCESS)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "%d\n", res);
|
2016-02-22 02:47:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
IWindowCallback* m_callback;
|
|
|
|
|
|
|
|
GraphicsContextXlibVulkan(IWindow* parentWindow,
|
2016-02-23 01:13:03 +00:00
|
|
|
Display* display, xcb_connection_t* xcbConn, int defaultScreen,
|
2018-01-07 09:25:30 +00:00
|
|
|
VulkanContext* ctx, uint32_t& visualIdOut, GLContext* glCtx)
|
2018-01-22 07:33:47 +00:00
|
|
|
: GraphicsContextXlib(EGraphicsAPI::Vulkan, ctx->m_deepColor ? EPixelFormat::RGBA16 : EPixelFormat::RGBA8,
|
|
|
|
parentWindow, display, glCtx),
|
2016-02-23 01:13:03 +00:00
|
|
|
m_xcbConn(xcbConn), m_ctx(ctx)
|
2016-02-22 02:47:45 +00:00
|
|
|
{
|
2016-07-02 03:44:57 +00:00
|
|
|
Screen* screen = ScreenOfDisplay(display, defaultScreen);
|
|
|
|
m_visualid = screen->root_visual->visualid;
|
|
|
|
visualIdOut = screen->root_visual->visualid;
|
2016-02-22 02:47:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void destroy()
|
|
|
|
{
|
|
|
|
VulkanContext::Window& m_windowCtx = *m_ctx->m_windows[m_parentWindow];
|
2016-06-27 04:33:01 +00:00
|
|
|
m_windowCtx.m_swapChains[0].destroy(m_ctx->m_dev);
|
|
|
|
m_windowCtx.m_swapChains[1].destroy(m_ctx->m_dev);
|
2018-05-20 22:37:26 +00:00
|
|
|
if (m_surface)
|
|
|
|
{
|
|
|
|
vk::DestroySurfaceKHR(m_ctx->m_instance, m_surface, nullptr);
|
|
|
|
m_surface = VK_NULL_HANDLE;
|
|
|
|
}
|
2016-02-22 02:47:45 +00:00
|
|
|
|
|
|
|
if (m_vsyncRunning)
|
|
|
|
{
|
|
|
|
m_vsyncRunning = false;
|
2018-05-20 22:37:26 +00:00
|
|
|
if (m_vsyncThread.joinable())
|
|
|
|
m_vsyncThread.join();
|
2016-02-22 02:47:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~GraphicsContextXlibVulkan() {destroy();}
|
|
|
|
|
2016-06-27 04:33:01 +00:00
|
|
|
VulkanContext::Window* m_windowCtx = nullptr;
|
|
|
|
|
2017-02-16 03:17:18 +00:00
|
|
|
void resized(const SWindowRect& rect)
|
2016-06-27 04:33:01 +00:00
|
|
|
{
|
|
|
|
if (m_windowCtx)
|
2017-02-16 03:17:18 +00:00
|
|
|
m_ctx->resizeSwapChain(*m_windowCtx, m_surface, m_format, m_colorspace, rect);
|
2016-06-27 04:33:01 +00:00
|
|
|
}
|
|
|
|
|
2016-02-22 02:47:45 +00:00
|
|
|
void _setCallback(IWindowCallback* cb)
|
|
|
|
{
|
|
|
|
m_callback = cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
EGraphicsAPI getAPI() const
|
|
|
|
{
|
|
|
|
return m_api;
|
|
|
|
}
|
|
|
|
|
|
|
|
EPixelFormat getPixelFormat() const
|
|
|
|
{
|
|
|
|
return m_pf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setPixelFormat(EPixelFormat pf)
|
|
|
|
{
|
|
|
|
if (pf > EPixelFormat::RGBAF32_Z24)
|
|
|
|
return;
|
|
|
|
m_pf = pf;
|
|
|
|
}
|
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
bool initializeContext(void* getVkProc)
|
2016-02-22 02:47:45 +00:00
|
|
|
{
|
|
|
|
if (!glXWaitVideoSyncSGI)
|
|
|
|
{
|
|
|
|
glXWaitVideoSyncSGI = (glXWaitVideoSyncSGIProc)
|
|
|
|
glXGetProcAddressARB((const GLubyte*)"glXWaitVideoSyncSGI");
|
|
|
|
if (!glXWaitVideoSyncSGI)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to resolve glXWaitVideoSyncSGI");
|
2016-02-22 02:47:45 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 01:13:03 +00:00
|
|
|
if (m_ctx->m_instance == VK_NULL_HANDLE)
|
2018-05-22 07:48:13 +00:00
|
|
|
m_ctx->initVulkan(APP->getUniqueName(), PFN_vkGetInstanceProcAddr(getVkProc));
|
2016-02-23 01:13:03 +00:00
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
if (!m_ctx->enumerateDevices())
|
|
|
|
return false;
|
2016-06-30 04:54:29 +00:00
|
|
|
|
2016-06-27 04:33:01 +00:00
|
|
|
m_windowCtx =
|
|
|
|
m_ctx->m_windows.emplace(std::make_pair(m_parentWindow,
|
|
|
|
std::make_unique<VulkanContext::Window>())).first->second.get();
|
2016-02-23 01:13:03 +00:00
|
|
|
|
|
|
|
VkXcbSurfaceCreateInfoKHR surfaceInfo = {};
|
|
|
|
surfaceInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
|
|
|
|
surfaceInfo.connection = m_xcbConn;
|
|
|
|
surfaceInfo.window = m_parentWindow->getPlatformHandle();
|
2016-06-30 04:54:29 +00:00
|
|
|
ThrowIfFailed(vk::CreateXcbSurfaceKHR(m_ctx->m_instance, &surfaceInfo, nullptr, &m_surface));
|
2016-02-23 01:13:03 +00:00
|
|
|
|
|
|
|
/* Iterate over each queue to learn whether it supports presenting */
|
|
|
|
VkBool32 *supportsPresent = (VkBool32*)malloc(m_ctx->m_queueCount * sizeof(VkBool32));
|
|
|
|
for (uint32_t i=0 ; i<m_ctx->m_queueCount ; ++i)
|
2016-06-30 04:54:29 +00:00
|
|
|
vk::GetPhysicalDeviceSurfaceSupportKHR(m_ctx->m_gpus[0], i, m_surface, &supportsPresent[i]);
|
2016-02-23 01:13:03 +00:00
|
|
|
|
|
|
|
/* Search for a graphics queue and a present queue in the array of queue
|
|
|
|
* families, try to find one that supports both */
|
|
|
|
if (m_ctx->m_graphicsQueueFamilyIndex == UINT32_MAX)
|
|
|
|
{
|
|
|
|
/* First window, init device */
|
|
|
|
for (uint32_t i=0 ; i<m_ctx->m_queueCount; ++i)
|
|
|
|
{
|
|
|
|
if ((m_ctx->m_queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
|
|
|
|
{
|
|
|
|
if (supportsPresent[i] == VK_TRUE)
|
|
|
|
{
|
|
|
|
m_ctx->m_graphicsQueueFamilyIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate error if could not find a queue that supports both a graphics
|
|
|
|
* and present */
|
|
|
|
if (m_ctx->m_graphicsQueueFamilyIndex == UINT32_MAX)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal,
|
2016-02-23 01:13:03 +00:00
|
|
|
"Could not find a queue that supports both graphics and present");
|
|
|
|
|
|
|
|
m_ctx->initDevice();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Subsequent window, verify present */
|
|
|
|
if (supportsPresent[m_ctx->m_graphicsQueueFamilyIndex] == VK_FALSE)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "subsequent surface doesn't support present");
|
2016-02-23 01:13:03 +00:00
|
|
|
}
|
|
|
|
free(supportsPresent);
|
|
|
|
|
2016-06-30 04:54:29 +00:00
|
|
|
if (!vk::GetPhysicalDeviceXcbPresentationSupportKHR(m_ctx->m_gpus[0], m_ctx->m_graphicsQueueFamilyIndex, m_xcbConn, m_visualid))
|
2016-02-23 01:13:03 +00:00
|
|
|
{
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "XCB visual doesn't support vulkan present");
|
2016-07-20 17:14:18 +00:00
|
|
|
return false;
|
2016-02-23 01:13:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the list of VkFormats that are supported */
|
|
|
|
uint32_t formatCount;
|
2016-06-30 04:54:29 +00:00
|
|
|
ThrowIfFailed(vk::GetPhysicalDeviceSurfaceFormatsKHR(m_ctx->m_gpus[0], m_surface, &formatCount, nullptr));
|
2018-05-20 22:37:26 +00:00
|
|
|
std::vector<VkSurfaceFormatKHR> surfFormats(formatCount);
|
|
|
|
ThrowIfFailed(vk::GetPhysicalDeviceSurfaceFormatsKHR(m_ctx->m_gpus[0], m_surface, &formatCount, surfFormats.data()));
|
2016-02-23 01:13:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* If the format list includes just one entry of VK_FORMAT_UNDEFINED,
|
|
|
|
* the surface has no preferred format. Otherwise, at least one
|
|
|
|
* supported format will be returned. */
|
|
|
|
if (formatCount >= 1)
|
|
|
|
{
|
2018-01-22 07:33:47 +00:00
|
|
|
if (m_ctx->m_deepColor)
|
2017-02-16 04:47:31 +00:00
|
|
|
{
|
2018-01-22 07:33:47 +00:00
|
|
|
for (int i=0 ; i<formatCount ; ++i)
|
2017-02-16 04:47:31 +00:00
|
|
|
{
|
2018-01-22 07:33:47 +00:00
|
|
|
if (surfFormats[i].format == VK_FORMAT_R16G16B16A16_UNORM)
|
|
|
|
{
|
|
|
|
m_format = surfFormats[i].format;
|
|
|
|
m_colorspace = surfFormats[i].colorSpace;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_format == VK_FORMAT_UNDEFINED)
|
|
|
|
{
|
|
|
|
for (int i=0 ; i<formatCount ; ++i)
|
|
|
|
{
|
|
|
|
if (surfFormats[i].format == VK_FORMAT_B8G8R8A8_UNORM ||
|
|
|
|
surfFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM)
|
|
|
|
{
|
|
|
|
m_format = surfFormats[i].format;
|
|
|
|
m_colorspace = surfFormats[i].colorSpace;
|
|
|
|
break;
|
|
|
|
}
|
2017-02-16 04:47:31 +00:00
|
|
|
}
|
|
|
|
}
|
2016-02-23 01:13:03 +00:00
|
|
|
}
|
|
|
|
else
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "no surface formats available for Vulkan swapchain");
|
2016-02-23 01:13:03 +00:00
|
|
|
|
2017-02-16 04:47:31 +00:00
|
|
|
if (m_format == VK_FORMAT_UNDEFINED)
|
|
|
|
Log.report(logvisor::Fatal, "no UNORM formats available for Vulkan swapchain");
|
|
|
|
|
2016-07-02 03:44:57 +00:00
|
|
|
m_ctx->initSwapChain(*m_windowCtx, m_surface, m_format, m_colorspace);
|
2016-02-23 01:13:03 +00:00
|
|
|
|
2016-02-22 02:47:45 +00:00
|
|
|
/* Spawn vsync thread */
|
|
|
|
m_vsyncRunning = true;
|
|
|
|
std::mutex initmt;
|
|
|
|
std::condition_variable initcv;
|
|
|
|
std::unique_lock<std::mutex> outerLk(initmt);
|
|
|
|
m_vsyncThread = std::thread([&]()
|
|
|
|
{
|
|
|
|
Display* vsyncDisp;
|
|
|
|
GLXContext vsyncCtx;
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> innerLk(initmt);
|
|
|
|
|
|
|
|
vsyncDisp = XOpenDisplay(0);
|
|
|
|
if (!vsyncDisp)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to open new vsync display");
|
2016-02-22 02:47:45 +00:00
|
|
|
XLockDisplay(vsyncDisp);
|
|
|
|
|
|
|
|
static int attributeList[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, 0 };
|
|
|
|
XVisualInfo *vi = glXChooseVisual(vsyncDisp, DefaultScreen(vsyncDisp), attributeList);
|
|
|
|
|
|
|
|
vsyncCtx = glXCreateContext(vsyncDisp, vi, nullptr, True);
|
|
|
|
if (!vsyncCtx)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make new vsync GLX context");
|
2016-02-22 02:47:45 +00:00
|
|
|
|
|
|
|
if (!glXMakeCurrent(vsyncDisp, DefaultRootWindow(vsyncDisp), vsyncCtx))
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "unable to make vsync context current");
|
2016-02-22 02:47:45 +00:00
|
|
|
}
|
|
|
|
initcv.notify_one();
|
|
|
|
|
|
|
|
while (m_vsyncRunning)
|
|
|
|
{
|
|
|
|
unsigned int sync;
|
|
|
|
int err = glXWaitVideoSyncSGI(1, 0, &sync);
|
|
|
|
if (err)
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "wait err");
|
2016-02-22 02:47:45 +00:00
|
|
|
m_vsynccv.notify_one();
|
|
|
|
}
|
|
|
|
|
|
|
|
glXMakeCurrent(vsyncDisp, 0, nullptr);
|
|
|
|
glXDestroyContext(vsyncDisp, vsyncCtx);
|
|
|
|
XUnlockDisplay(vsyncDisp);
|
|
|
|
XCloseDisplay(vsyncDisp);
|
|
|
|
});
|
|
|
|
initcv.wait(outerLk);
|
|
|
|
|
2018-01-11 16:25:05 +00:00
|
|
|
m_dataFactory = _NewVulkanDataFactory(this, m_ctx);
|
2016-02-22 02:47:45 +00:00
|
|
|
m_commandQueue = _NewVulkanCommandQueue(m_ctx, m_ctx->m_windows[m_parentWindow].get(), this);
|
2018-01-22 07:33:47 +00:00
|
|
|
m_commandQueue->startRenderer();
|
2016-07-20 17:14:18 +00:00
|
|
|
|
|
|
|
return true;
|
2016-02-22 02:47:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void makeCurrent() {}
|
|
|
|
|
|
|
|
void postInit() {}
|
|
|
|
|
|
|
|
IGraphicsCommandQueue* getCommandQueue()
|
|
|
|
{
|
2018-05-20 06:11:49 +00:00
|
|
|
return m_commandQueue.get();
|
2016-02-22 02:47:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
IGraphicsDataFactory* getDataFactory()
|
|
|
|
{
|
2018-05-20 06:11:49 +00:00
|
|
|
return m_dataFactory.get();
|
2016-02-22 02:47:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
IGraphicsDataFactory* getMainContextDataFactory()
|
|
|
|
{
|
|
|
|
return getDataFactory();
|
|
|
|
}
|
|
|
|
|
|
|
|
IGraphicsDataFactory* getLoadContextDataFactory()
|
|
|
|
{
|
|
|
|
return getDataFactory();
|
|
|
|
}
|
|
|
|
|
|
|
|
void present() {}
|
|
|
|
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2015-11-05 04:52:33 +00:00
|
|
|
class WindowXlib : public IWindow
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
Display* m_xDisp;
|
2015-05-09 05:33:48 +00:00
|
|
|
IWindowCallback* m_callback;
|
2015-10-31 04:28:21 +00:00
|
|
|
Colormap m_colormapId;
|
|
|
|
Window m_windowId;
|
2015-12-24 03:30:52 +00:00
|
|
|
XIMStyle m_bestStyle;
|
2016-01-06 21:02:23 +00:00
|
|
|
XIC m_xIC = nullptr;
|
2016-02-22 02:47:45 +00:00
|
|
|
std::unique_ptr<GraphicsContextXlib> m_gfxCtx;
|
2015-08-28 00:10:46 +00:00
|
|
|
uint32_t m_visualId;
|
2015-05-09 05:33:48 +00:00
|
|
|
|
2016-05-15 21:54:55 +00:00
|
|
|
/* Key state trackers (for auto-repeat detection) */
|
|
|
|
std::unordered_set<unsigned long> m_charKeys;
|
|
|
|
std::unordered_set<unsigned long> m_specialKeys;
|
|
|
|
std::unordered_set<unsigned long> m_modKeys;
|
|
|
|
|
|
|
|
|
2015-05-13 08:51:18 +00:00
|
|
|
/* Last known input device id (0xffff if not yet set) */
|
2015-10-31 04:28:21 +00:00
|
|
|
int m_lastInputID = 0xffff;
|
2015-11-21 01:12:22 +00:00
|
|
|
ETouchType m_touchType = ETouchType::None;
|
2015-05-13 08:51:18 +00:00
|
|
|
|
|
|
|
/* Scroll valuators */
|
|
|
|
int m_hScrollValuator = -1;
|
|
|
|
int m_vScrollValuator = -1;
|
|
|
|
double m_hScrollLast = 0.0;
|
|
|
|
double m_vScrollLast = 0.0;
|
|
|
|
|
2015-05-09 05:33:48 +00:00
|
|
|
/* Cached window rectangle (to avoid repeated X queries) */
|
2016-01-02 23:10:35 +00:00
|
|
|
boo::SWindowRect m_wrect;
|
2015-05-09 05:33:48 +00:00
|
|
|
float m_pixelFactor;
|
2015-11-05 04:52:33 +00:00
|
|
|
bool m_inFs = false;
|
|
|
|
|
2015-11-05 07:30:40 +00:00
|
|
|
/* Cached window style */
|
|
|
|
EWindowStyle m_styleFlags;
|
|
|
|
|
2015-11-30 00:20:20 +00:00
|
|
|
/* Current cursor enum */
|
|
|
|
EMouseCursor m_cursor = EMouseCursor::None;
|
|
|
|
bool m_cursorWait = false;
|
|
|
|
static Cursor GetXCursor(EMouseCursor cur)
|
|
|
|
{
|
|
|
|
switch (cur)
|
|
|
|
{
|
|
|
|
case EMouseCursor::Pointer:
|
|
|
|
return X_CURSORS.m_pointer;
|
|
|
|
case EMouseCursor::HorizontalArrow:
|
|
|
|
return X_CURSORS.m_hArrow;
|
|
|
|
case EMouseCursor::VerticalArrow:
|
|
|
|
return X_CURSORS.m_vArrow;
|
2015-12-20 04:39:48 +00:00
|
|
|
case EMouseCursor::IBeam:
|
|
|
|
return X_CURSORS.m_ibeam;
|
2016-01-10 06:42:00 +00:00
|
|
|
case EMouseCursor::Crosshairs:
|
|
|
|
return X_CURSORS.m_crosshairs;
|
2015-11-30 00:20:20 +00:00
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
return X_CURSORS.m_pointer;
|
|
|
|
}
|
|
|
|
|
2017-02-15 23:13:05 +00:00
|
|
|
bool m_openGL = false;
|
|
|
|
|
2015-05-06 00:50:57 +00:00
|
|
|
public:
|
2017-11-13 06:13:32 +00:00
|
|
|
WindowXlib(std::string_view title,
|
2016-02-23 01:13:03 +00:00
|
|
|
Display* display, void* xcbConn,
|
|
|
|
int defaultScreen, XIM xIM, XIMStyle bestInputStyle, XFontSet fontset,
|
2018-01-07 09:25:30 +00:00
|
|
|
GLXContext lastCtx, void* vulkanHandle, GLContext* glCtx)
|
2015-10-31 04:28:21 +00:00
|
|
|
: m_xDisp(display), m_callback(nullptr),
|
2015-12-24 03:30:52 +00:00
|
|
|
m_bestStyle(bestInputStyle)
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
2015-05-09 05:33:48 +00:00
|
|
|
if (!S_ATOMS)
|
2015-12-24 03:30:52 +00:00
|
|
|
S_ATOMS = new XlibAtoms(display);
|
2015-05-09 05:33:48 +00:00
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
for (int i = 1; i >= 0 ; --i)
|
|
|
|
{
|
2016-02-22 02:47:45 +00:00
|
|
|
#if BOO_HAS_VULKAN
|
2016-07-20 17:14:18 +00:00
|
|
|
if (vulkanHandle && i == 1)
|
|
|
|
{
|
|
|
|
m_gfxCtx.reset(new GraphicsContextXlibVulkan(this, display, (xcb_connection_t*)xcbConn, defaultScreen,
|
2018-01-07 09:25:30 +00:00
|
|
|
&g_VulkanContext, m_visualId, glCtx));
|
2016-07-20 17:14:18 +00:00
|
|
|
}
|
|
|
|
else
|
2016-02-22 02:47:45 +00:00
|
|
|
#endif
|
2016-07-20 17:14:18 +00:00
|
|
|
{
|
|
|
|
i = 0;
|
|
|
|
m_gfxCtx.reset(new GraphicsContextXlibGLX(IGraphicsContext::EGraphicsAPI::OpenGL3_3,
|
2018-01-07 09:25:30 +00:00
|
|
|
this, display, defaultScreen, lastCtx, m_visualId, glCtx));
|
2017-02-15 23:13:05 +00:00
|
|
|
m_openGL = true;
|
2016-07-20 17:14:18 +00:00
|
|
|
}
|
2016-02-22 02:47:45 +00:00
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
/* Default screen */
|
|
|
|
Screen* screen = ScreenOfDisplay(display, defaultScreen);
|
|
|
|
m_pixelFactor = screen->width / (float)screen->mwidth / REF_DPMM;
|
|
|
|
|
|
|
|
XVisualInfo visTemplate;
|
|
|
|
visTemplate.screen = defaultScreen;
|
|
|
|
int numVisuals;
|
|
|
|
XVisualInfo* visualList = XGetVisualInfo(display, VisualScreenMask, &visTemplate, &numVisuals);
|
|
|
|
Visual* selectedVisual = nullptr;
|
|
|
|
for (int i=0 ; i<numVisuals ; ++i)
|
2015-10-31 04:28:21 +00:00
|
|
|
{
|
2016-07-20 17:14:18 +00:00
|
|
|
if (visualList[i].visualid == m_visualId)
|
|
|
|
{
|
|
|
|
selectedVisual = visualList[i].visual;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
XFree(visualList);
|
|
|
|
|
|
|
|
/* Create colormap */
|
|
|
|
m_colormapId = XCreateColormap(m_xDisp, screen->root, selectedVisual, AllocNone);
|
|
|
|
|
|
|
|
/* Create window */
|
|
|
|
int x, y, w, h;
|
2017-11-02 09:24:50 +00:00
|
|
|
int nmonitors = 0;
|
|
|
|
XRRMonitorInfo* mInfo = XRRGetMonitors(m_xDisp, screen->root, true, &nmonitors);
|
|
|
|
if (nmonitors)
|
|
|
|
genFrameDefault(mInfo, x, y, w, h);
|
|
|
|
else
|
|
|
|
genFrameDefault(screen, x, y, w, h);
|
|
|
|
XRRFreeMonitors(mInfo);
|
2016-07-20 17:14:18 +00:00
|
|
|
XSetWindowAttributes swa;
|
|
|
|
swa.colormap = m_colormapId;
|
|
|
|
swa.border_pixmap = 0;
|
|
|
|
swa.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | StructureNotifyMask | LeaveWindowMask | EnterWindowMask;
|
|
|
|
|
|
|
|
m_windowId = XCreateWindow(display, screen->root, x, y, w, h, 10,
|
|
|
|
CopyFromParent, CopyFromParent, selectedVisual,
|
|
|
|
CWBorderPixel | CWEventMask | CWColormap, &swa);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now go create an IC using the style we chose.
|
|
|
|
* Also set the window and fontset attributes now.
|
|
|
|
*/
|
|
|
|
if (xIM)
|
|
|
|
{
|
|
|
|
XPoint pt = {0,0};
|
|
|
|
XVaNestedList nlist;
|
|
|
|
m_xIC = XCreateIC(xIM, XNInputStyle, bestInputStyle,
|
|
|
|
XNClientWindow, m_windowId,
|
|
|
|
XNFocusWindow, m_windowId,
|
|
|
|
XNPreeditAttributes, nlist = XVaCreateNestedList(0,
|
|
|
|
XNSpotLocation, &pt,
|
|
|
|
XNFontSet, fontset,
|
|
|
|
nullptr),
|
|
|
|
nullptr);
|
|
|
|
XFree(nlist);
|
|
|
|
long im_event_mask;
|
|
|
|
XGetICValues(m_xIC, XNFilterEvents, &im_event_mask, nullptr);
|
|
|
|
XSelectInput(display, m_windowId, swa.event_mask | im_event_mask);
|
|
|
|
XSetICFocus(m_xIC);
|
2015-10-31 04:28:21 +00:00
|
|
|
}
|
2015-05-09 05:33:48 +00:00
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
/* The XInput 2.1 extension enables per-pixel smooth scrolling trackpads */
|
|
|
|
XIEventMask mask = {XIAllMasterDevices, XIMaskLen(XI_LASTEVENT)};
|
|
|
|
mask.mask = (unsigned char*)malloc(mask.mask_len);
|
|
|
|
memset(mask.mask, 0, mask.mask_len);
|
|
|
|
/* XISetMask(mask.mask, XI_Motion); Can't do this without losing mouse move events :( */
|
|
|
|
XISetMask(mask.mask, XI_TouchBegin);
|
|
|
|
XISetMask(mask.mask, XI_TouchUpdate);
|
|
|
|
XISetMask(mask.mask, XI_TouchEnd);
|
|
|
|
XISelectEvents(m_xDisp, m_windowId, &mask, 1);
|
|
|
|
free(mask.mask);
|
2015-10-31 04:28:21 +00:00
|
|
|
|
2015-05-12 09:38:37 +00:00
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
/* Register netwm extension atom for window closing */
|
|
|
|
XSetWMProtocols(m_xDisp, m_windowId, &S_ATOMS->m_wmDeleteWindow, 1);
|
2015-05-12 09:38:37 +00:00
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
/* Set the title of the window */
|
2017-11-13 07:19:49 +00:00
|
|
|
const unsigned char* c_title = (unsigned char*)title.data();
|
2016-07-20 17:14:18 +00:00
|
|
|
XChangeProperty(m_xDisp, m_windowId, XA_WM_NAME, XA_STRING, 8, PropModeReplace, c_title, title.length());
|
2015-05-09 05:33:48 +00:00
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
/* Set the title of the window icon */
|
|
|
|
XChangeProperty(m_xDisp, m_windowId, XA_WM_ICON_NAME, XA_STRING, 8, PropModeReplace, c_title, title.length());
|
2015-05-09 05:33:48 +00:00
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
/* Add window icon */
|
|
|
|
if (MAINICON_NETWM_SZ && S_ATOMS->m_netwmIcon)
|
|
|
|
{
|
2018-01-02 05:09:15 +00:00
|
|
|
XChangeProperty(m_xDisp, m_windowId, S_ATOMS->m_netwmIcon, XA_CARDINAL,
|
2016-07-20 17:14:18 +00:00
|
|
|
32, PropModeReplace, MAINICON_NETWM, MAINICON_NETWM_SZ / sizeof(unsigned long));
|
|
|
|
}
|
2016-01-27 23:25:30 +00:00
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
/* Initialize context */
|
|
|
|
XMapWindow(m_xDisp, m_windowId);
|
|
|
|
setStyle(EWindowStyle::Default);
|
|
|
|
setCursor(EMouseCursor::Pointer);
|
2017-11-02 09:24:50 +00:00
|
|
|
setWindowFrameDefault();
|
2016-07-20 17:14:18 +00:00
|
|
|
XFlush(m_xDisp);
|
2015-11-05 07:30:40 +00:00
|
|
|
|
2016-07-20 17:14:18 +00:00
|
|
|
if (!m_gfxCtx->initializeContext(vulkanHandle))
|
|
|
|
{
|
|
|
|
XUnmapWindow(m_xDisp, m_windowId);
|
|
|
|
XDestroyWindow(m_xDisp, m_windowId);
|
|
|
|
XFreeColormap(m_xDisp, m_colormapId);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
2015-11-02 10:07:15 +00:00
|
|
|
~WindowXlib()
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2016-02-22 02:47:45 +00:00
|
|
|
m_gfxCtx->destroy();
|
2015-10-31 04:28:21 +00:00
|
|
|
XUnmapWindow(m_xDisp, m_windowId);
|
|
|
|
XDestroyWindow(m_xDisp, m_windowId);
|
|
|
|
XFreeColormap(m_xDisp, m_colormapId);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-08-18 19:40:26 +00:00
|
|
|
APP->_deletedWindow(this);
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void setCallback(IWindowCallback* cb)
|
|
|
|
{
|
2015-05-09 05:33:48 +00:00
|
|
|
m_callback = cb;
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
2017-07-17 03:58:18 +00:00
|
|
|
|
|
|
|
void closeWindow()
|
|
|
|
{
|
|
|
|
// TODO: Free window resources and prevent further access
|
|
|
|
XLockDisplay(m_xDisp);
|
|
|
|
XUnmapWindow(m_xDisp, m_windowId);
|
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
}
|
2015-05-06 00:50:57 +00:00
|
|
|
|
|
|
|
void showWindow()
|
|
|
|
{
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-31 04:28:21 +00:00
|
|
|
XMapWindow(m_xDisp, m_windowId);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void hideWindow()
|
|
|
|
{
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-31 04:28:21 +00:00
|
|
|
XUnmapWindow(m_xDisp, m_windowId);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
2016-07-20 22:19:53 +00:00
|
|
|
|
2015-05-06 00:50:57 +00:00
|
|
|
std::string getTitle()
|
|
|
|
{
|
2015-10-31 19:21:23 +00:00
|
|
|
unsigned long nitems;
|
|
|
|
Atom actualType;
|
|
|
|
int actualFormat;
|
|
|
|
unsigned long bytes;
|
2015-10-31 04:28:21 +00:00
|
|
|
unsigned char* string = nullptr;
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
|
|
|
int ret = XGetWindowProperty(m_xDisp, m_windowId, XA_WM_NAME, 0, ~0l, False,
|
|
|
|
XA_STRING, &actualType, &actualFormat, &nitems, &bytes, &string);
|
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
if (ret == Success)
|
2015-10-31 04:28:21 +00:00
|
|
|
{
|
|
|
|
std::string retval((const char*)string);
|
|
|
|
XFree(string);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
return std::string();
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
2017-11-13 06:13:32 +00:00
|
|
|
void setTitle(std::string_view title)
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
2017-11-13 07:19:49 +00:00
|
|
|
const unsigned char* c_title = (unsigned char*)title.data();
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-31 04:28:21 +00:00
|
|
|
XChangeProperty(m_xDisp, m_windowId, XA_WM_NAME, XA_STRING, 8,
|
|
|
|
PropModeReplace, c_title, title.length());
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
2015-11-30 00:20:20 +00:00
|
|
|
|
|
|
|
void setCursor(EMouseCursor cursor)
|
|
|
|
{
|
|
|
|
if (cursor == m_cursor && !m_cursorWait)
|
|
|
|
return;
|
|
|
|
m_cursor = cursor;
|
|
|
|
XLockDisplay(m_xDisp);
|
|
|
|
XDefineCursor(m_xDisp, m_windowId, GetXCursor(cursor));
|
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void setWaitCursor(bool wait)
|
|
|
|
{
|
|
|
|
if (wait && !m_cursorWait)
|
|
|
|
{
|
|
|
|
XLockDisplay(m_xDisp);
|
|
|
|
XDefineCursor(m_xDisp, m_windowId, X_CURSORS.m_wait);
|
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
m_cursorWait = true;
|
|
|
|
}
|
|
|
|
else if (!wait && m_cursorWait)
|
|
|
|
{
|
|
|
|
setCursor(m_cursor);
|
|
|
|
m_cursorWait = false;
|
|
|
|
}
|
|
|
|
}
|
2015-05-06 00:50:57 +00:00
|
|
|
|
|
|
|
void setWindowFrameDefault()
|
|
|
|
{
|
2017-11-02 09:24:50 +00:00
|
|
|
int x, y, w, h, nmonitors;
|
2015-10-31 04:28:21 +00:00
|
|
|
Screen* screen = DefaultScreenOfDisplay(m_xDisp);
|
2017-11-02 09:24:50 +00:00
|
|
|
XRRMonitorInfo* mInfo = XRRGetMonitors(m_xDisp, screen->root, true, &nmonitors);
|
|
|
|
if (nmonitors)
|
|
|
|
genFrameDefault(mInfo, x, y, w, h);
|
|
|
|
else
|
|
|
|
genFrameDefault(screen, x, y, w, h);
|
|
|
|
XRRFreeMonitors(mInfo);
|
2015-10-31 04:28:21 +00:00
|
|
|
XWindowChanges values = {(int)x, (int)y, (int)w, (int)h};
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-31 04:28:21 +00:00
|
|
|
XConfigureWindow(m_xDisp, m_windowId, CWX|CWY|CWWidth|CWHeight, &values);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void getWindowFrame(float& xOut, float& yOut, float& wOut, float& hOut) const
|
|
|
|
{
|
2015-10-31 19:21:23 +00:00
|
|
|
XWindowAttributes attrs;
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-31 19:21:23 +00:00
|
|
|
XGetWindowAttributes(m_xDisp, m_windowId, &attrs);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-10-31 19:21:23 +00:00
|
|
|
xOut = attrs.x;
|
|
|
|
yOut = attrs.y;
|
|
|
|
wOut = attrs.width;
|
|
|
|
hOut = attrs.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
void getWindowFrame(int& xOut, int& yOut, int& wOut, int& hOut) const
|
|
|
|
{
|
|
|
|
XWindowAttributes attrs;
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-31 19:21:23 +00:00
|
|
|
XGetWindowAttributes(m_xDisp, m_windowId, &attrs);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-10-31 19:21:23 +00:00
|
|
|
xOut = attrs.x;
|
|
|
|
yOut = attrs.y;
|
|
|
|
wOut = attrs.width;
|
|
|
|
hOut = attrs.height;
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void setWindowFrame(float x, float y, float w, float h)
|
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XWindowChanges values = {(int)x, (int)y, (int)w, (int)h};
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-31 04:28:21 +00:00
|
|
|
XConfigureWindow(m_xDisp, m_windowId, CWX|CWY|CWWidth|CWHeight, &values);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
2015-10-31 19:21:23 +00:00
|
|
|
|
|
|
|
void setWindowFrame(int x, int y, int w, int h)
|
|
|
|
{
|
|
|
|
XWindowChanges values = {x, y, w, h};
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-10-31 19:21:23 +00:00
|
|
|
XConfigureWindow(m_xDisp, m_windowId, CWX|CWY|CWWidth|CWHeight, &values);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-10-31 19:21:23 +00:00
|
|
|
}
|
2015-05-06 00:50:57 +00:00
|
|
|
|
|
|
|
float getVirtualPixelFactor() const
|
|
|
|
{
|
2015-05-09 05:33:48 +00:00
|
|
|
return m_pixelFactor;
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
2015-11-05 04:52:33 +00:00
|
|
|
|
2015-05-06 00:50:57 +00:00
|
|
|
bool isFullscreen() const
|
|
|
|
{
|
2015-11-05 07:30:40 +00:00
|
|
|
return m_inFs;
|
2015-10-31 04:28:21 +00:00
|
|
|
unsigned long nitems;
|
2015-10-31 06:39:11 +00:00
|
|
|
Atom actualType;
|
|
|
|
int actualFormat;
|
|
|
|
unsigned long bytes;
|
2015-10-31 04:28:21 +00:00
|
|
|
Atom* vals = nullptr;
|
|
|
|
bool fullscreen = false;
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
|
|
|
int ret = XGetWindowProperty(m_xDisp, m_windowId, S_ATOMS->m_netwmState, 0, ~0l, False,
|
|
|
|
XA_ATOM, &actualType, &actualFormat, &nitems, &bytes, (unsigned char**)&vals);
|
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
if (ret == Success)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
for (int i=0 ; i<nitems ; ++i)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
if (vals[i] == S_ATOMS->m_netwmStateFullscreen)
|
|
|
|
{
|
|
|
|
fullscreen = true;
|
|
|
|
break;
|
|
|
|
}
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
XFree(vals);
|
|
|
|
return fullscreen;
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2015-11-05 04:52:33 +00:00
|
|
|
|
2015-10-31 04:28:21 +00:00
|
|
|
return false;
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
2015-11-05 07:30:40 +00:00
|
|
|
|
|
|
|
void setStyle(EWindowStyle style)
|
|
|
|
{
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned long functions;
|
|
|
|
unsigned long decorations;
|
|
|
|
long inputMode;
|
|
|
|
unsigned long status;
|
|
|
|
} wmHints = {0};
|
|
|
|
|
|
|
|
if (S_ATOMS->m_motifWmHints)
|
|
|
|
{
|
|
|
|
wmHints.flags = MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS;
|
2015-11-21 01:12:22 +00:00
|
|
|
if ((style & EWindowStyle::Titlebar) != EWindowStyle::None)
|
2015-11-05 07:30:40 +00:00
|
|
|
{
|
|
|
|
wmHints.decorations |= MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MINIMIZE | MWM_DECOR_MENU;
|
|
|
|
wmHints.functions |= MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE;
|
|
|
|
}
|
2015-11-21 01:12:22 +00:00
|
|
|
if ((style & EWindowStyle::Resize) != EWindowStyle::None)
|
2015-11-05 07:30:40 +00:00
|
|
|
{
|
|
|
|
wmHints.decorations |= MWM_DECOR_MAXIMIZE | MWM_DECOR_RESIZEH;
|
|
|
|
wmHints.functions |= MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE;
|
|
|
|
}
|
|
|
|
|
2015-11-21 01:12:22 +00:00
|
|
|
if ((style & EWindowStyle::Close) != EWindowStyle::None)
|
2015-11-05 07:30:40 +00:00
|
|
|
wmHints.functions |= MWM_FUNC_CLOSE;
|
|
|
|
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-11-05 07:30:40 +00:00
|
|
|
XChangeProperty(m_xDisp, m_windowId, S_ATOMS->m_motifWmHints, S_ATOMS->m_motifWmHints, 32, PropModeReplace, (unsigned char*)&wmHints, 5);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-11-05 07:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_styleFlags = style;
|
|
|
|
}
|
|
|
|
|
|
|
|
EWindowStyle getStyle() const
|
|
|
|
{
|
|
|
|
return m_styleFlags;
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:50:57 +00:00
|
|
|
void setFullscreen(bool fs)
|
|
|
|
{
|
2015-11-05 04:52:33 +00:00
|
|
|
if (fs == m_inFs)
|
|
|
|
return;
|
|
|
|
|
|
|
|
XEvent fsEvent = {0};
|
2015-11-05 04:31:30 +00:00
|
|
|
fsEvent.xclient.type = ClientMessage;
|
2015-11-05 07:30:40 +00:00
|
|
|
fsEvent.xclient.serial = 0;
|
|
|
|
fsEvent.xclient.send_event = True;
|
2015-10-31 06:39:11 +00:00
|
|
|
fsEvent.xclient.window = m_windowId;
|
2015-11-05 07:30:40 +00:00
|
|
|
fsEvent.xclient.message_type = S_ATOMS->m_netwmState;
|
2015-10-31 06:39:11 +00:00
|
|
|
fsEvent.xclient.format = 32;
|
|
|
|
fsEvent.xclient.data.l[0] = fs;
|
2015-11-05 07:30:40 +00:00
|
|
|
fsEvent.xclient.data.l[1] = S_ATOMS->m_netwmStateFullscreen;
|
2015-10-31 06:39:11 +00:00
|
|
|
fsEvent.xclient.data.l[2] = 0;
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-11-05 04:31:30 +00:00
|
|
|
XSendEvent(m_xDisp, DefaultRootWindow(m_xDisp), False,
|
2015-10-31 04:28:21 +00:00
|
|
|
StructureNotifyMask | SubstructureRedirectMask, (XEvent*)&fsEvent);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-11-05 07:30:40 +00:00
|
|
|
|
2015-11-05 04:52:33 +00:00
|
|
|
m_inFs = fs;
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
|
|
|
|
2015-12-24 03:30:52 +00:00
|
|
|
struct ClipData
|
|
|
|
{
|
|
|
|
EClipboardType m_type = EClipboardType::None;
|
|
|
|
std::unique_ptr<uint8_t[]> m_data;
|
|
|
|
size_t m_sz = 0;
|
|
|
|
void clear()
|
|
|
|
{
|
|
|
|
m_type = EClipboardType::None;
|
|
|
|
m_data.reset();
|
|
|
|
m_sz = 0;
|
|
|
|
}
|
|
|
|
} m_clipData;
|
|
|
|
|
2015-12-24 20:55:23 +00:00
|
|
|
void claimKeyboardFocus(const int coord[2])
|
2015-12-24 03:30:52 +00:00
|
|
|
{
|
2016-01-06 21:02:23 +00:00
|
|
|
if (m_xIC)
|
2015-12-24 20:55:23 +00:00
|
|
|
{
|
2016-01-06 21:02:23 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
|
|
|
if (!coord)
|
|
|
|
{
|
|
|
|
XUnsetICFocus(m_xIC);
|
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
getWindowFrame(m_wrect.location[0], m_wrect.location[1], m_wrect.size[0], m_wrect.size[1]);
|
|
|
|
XPoint pt = {short(coord[0]), short(m_wrect.size[1] - coord[1])};
|
|
|
|
XVaNestedList list = XVaCreateNestedList(0, XNSpotLocation, &pt, nullptr);
|
|
|
|
XSetICValues(m_xIC, XNPreeditAttributes, list, nullptr);
|
|
|
|
XFree(list);
|
|
|
|
XSetICFocus(m_xIC);
|
2015-12-24 20:55:23 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
}
|
2015-12-24 03:30:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz)
|
|
|
|
{
|
|
|
|
Atom xType = GetClipboardTypeAtom(type);
|
|
|
|
if (!xType)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
XLockDisplay(m_xDisp);
|
|
|
|
m_clipData.m_type = type;
|
|
|
|
m_clipData.m_data.reset(new uint8_t[sz]);
|
|
|
|
m_clipData.m_sz = sz;
|
|
|
|
memcpy(m_clipData.m_data.get(), data, sz);
|
|
|
|
XSetSelectionOwner(m_xDisp, S_ATOMS->m_clipboard, m_windowId, CurrentTime);
|
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz)
|
|
|
|
{
|
|
|
|
Atom xType = GetClipboardTypeAtom(type);
|
|
|
|
if (!xType)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
XLockDisplay(m_xDisp);
|
|
|
|
XConvertSelection(m_xDisp, S_ATOMS->m_clipboard, xType, S_ATOMS->m_clipdata, m_windowId, CurrentTime);
|
|
|
|
XFlush(m_xDisp);
|
|
|
|
XEvent event;
|
|
|
|
for (int i=0 ; i<20; ++i)
|
|
|
|
{
|
|
|
|
if (XCheckTypedWindowEvent(m_xDisp, m_windowId, SelectionNotify, &event))
|
|
|
|
{
|
|
|
|
if (event.xselection.property != 0)
|
|
|
|
{
|
|
|
|
XSync(m_xDisp, false);
|
|
|
|
|
|
|
|
unsigned long nitems, rem;
|
|
|
|
int format;
|
|
|
|
unsigned char* data;
|
|
|
|
Atom type;
|
|
|
|
|
|
|
|
Atom t1 = S_ATOMS->m_clipboard;
|
|
|
|
Atom t2 = S_ATOMS->m_clipdata;
|
|
|
|
|
|
|
|
if (XGetWindowProperty(m_xDisp, m_windowId, S_ATOMS->m_clipdata, 0, 32, False, AnyPropertyType,
|
|
|
|
&type, &format, &nitems, &rem, &data))
|
|
|
|
{
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "Clipboard allocation failed");
|
2015-12-24 03:30:52 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rem != 0)
|
|
|
|
{
|
2016-03-04 23:02:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "partial clipboard read");
|
2015-12-24 03:30:52 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
sz = nitems * format / 8;
|
|
|
|
std::unique_ptr<uint8_t[]> ret(new uint8_t[sz]);
|
|
|
|
memcpy(ret.get(), data, sz);
|
|
|
|
XFree(data);
|
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
if (XCheckTypedWindowEvent(m_xDisp, m_windowId, SelectionRequest, &event) &&
|
|
|
|
event.xselectionrequest.owner == m_windowId)
|
|
|
|
handleSelectionRequest(&event.xselectionrequest);
|
|
|
|
if (XCheckTypedWindowEvent(m_xDisp, m_windowId, SelectionClear, &event) &&
|
|
|
|
event.xselectionclear.window == m_windowId)
|
|
|
|
m_clipData.clear();
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
}
|
|
|
|
XUnlockDisplay(m_xDisp);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleSelectionRequest(XSelectionRequestEvent* se)
|
|
|
|
{
|
|
|
|
XEvent reply;
|
|
|
|
reply.xselection.type = SelectionNotify;
|
|
|
|
reply.xselection.display = m_xDisp;
|
|
|
|
reply.xselection.requestor = se->requestor;
|
|
|
|
reply.xselection.selection = se->selection;
|
|
|
|
reply.xselection.target = se->target;
|
|
|
|
reply.xselection.time = se->time;
|
|
|
|
reply.xselection.property = se->property;
|
|
|
|
if (se->target == S_ATOMS->m_targets)
|
|
|
|
{
|
|
|
|
Atom ValidTargets[] = {GetClipboardTypeAtom(m_clipData.m_type)};
|
|
|
|
XChangeProperty(m_xDisp, se->requestor, se->property, XA_ATOM,
|
|
|
|
32, 0, (unsigned char*)ValidTargets, m_clipData.m_type != EClipboardType::None);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (se->target == GetClipboardTypeAtom(m_clipData.m_type))
|
|
|
|
{
|
|
|
|
XChangeProperty(m_xDisp, se->requestor, se->property, se->target, 8, PropModeReplace,
|
|
|
|
m_clipData.m_data.get(), m_clipData.m_sz);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
reply.xselection.property = 0;
|
|
|
|
}
|
|
|
|
XSendEvent(m_xDisp, se->requestor, False, 0, &reply);
|
|
|
|
}
|
|
|
|
|
2017-02-15 06:00:10 +00:00
|
|
|
void waitForRetrace(IAudioVoiceEngine* engine)
|
2015-08-28 00:10:46 +00:00
|
|
|
{
|
2017-02-15 06:00:10 +00:00
|
|
|
if (engine)
|
|
|
|
engine->pumpAndMixVoices();
|
2016-02-22 02:47:45 +00:00
|
|
|
std::unique_lock<std::mutex> lk(m_gfxCtx->m_vsyncmt);
|
|
|
|
m_gfxCtx->m_vsynccv.wait(lk);
|
2015-08-28 00:10:46 +00:00
|
|
|
}
|
|
|
|
|
2015-05-09 05:33:48 +00:00
|
|
|
uintptr_t getPlatformHandle() const
|
|
|
|
{
|
|
|
|
return (uintptr_t)m_windowId;
|
|
|
|
}
|
|
|
|
|
2015-10-31 04:28:21 +00:00
|
|
|
void _pointingDeviceChanged(int deviceId)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
int nDevices;
|
|
|
|
XIDeviceInfo* devices = XIQueryDevice(m_xDisp, deviceId, &nDevices);
|
2015-05-13 08:51:18 +00:00
|
|
|
|
2015-10-31 04:28:21 +00:00
|
|
|
for (int i=0 ; i<nDevices ; ++i)
|
2015-05-12 09:38:37 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XIDeviceInfo* device = &devices[i];
|
|
|
|
|
2015-05-13 08:51:18 +00:00
|
|
|
/* First iterate classes for scrollables */
|
|
|
|
int hScroll = -1;
|
|
|
|
int vScroll = -1;
|
|
|
|
m_hScrollLast = 0.0;
|
|
|
|
m_vScrollLast = 0.0;
|
|
|
|
m_hScrollValuator = -1;
|
|
|
|
m_vScrollValuator = -1;
|
2015-10-31 04:28:21 +00:00
|
|
|
for (int j=0 ; j<device->num_classes ; ++j)
|
2015-05-12 09:38:37 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XIAnyClassInfo* dclass = device->classes[j];
|
|
|
|
if (dclass->type == XIScrollClass)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XIScrollClassInfo* scrollClass = (XIScrollClassInfo*)dclass;
|
|
|
|
if (scrollClass->scroll_type == XIScrollTypeVertical)
|
2015-05-13 08:51:18 +00:00
|
|
|
vScroll = scrollClass->number;
|
2015-10-31 04:28:21 +00:00
|
|
|
else if (scrollClass->scroll_type == XIScrollTypeHorizontal)
|
2015-05-13 08:51:18 +00:00
|
|
|
hScroll = scrollClass->number;
|
|
|
|
}
|
2015-05-12 09:38:37 +00:00
|
|
|
}
|
2015-05-13 08:51:18 +00:00
|
|
|
|
|
|
|
/* Next iterate for touch and scroll valuators */
|
2015-10-31 04:28:21 +00:00
|
|
|
for (int j=0 ; j<device->num_classes ; ++j)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XIAnyClassInfo* dclass = device->classes[j];
|
|
|
|
if (dclass->type == XIValuatorClass)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XIValuatorClassInfo* valClass = (XIValuatorClassInfo*)dclass;
|
2015-05-13 08:51:18 +00:00
|
|
|
if (valClass->number == vScroll)
|
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
m_vScrollLast = valClass->value;
|
2015-05-13 08:51:18 +00:00
|
|
|
m_vScrollValuator = vScroll;
|
|
|
|
}
|
|
|
|
else if (valClass->number == hScroll)
|
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
m_hScrollLast = valClass->value;
|
2015-05-13 08:51:18 +00:00
|
|
|
m_hScrollValuator = hScroll;
|
|
|
|
}
|
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
else if (dclass->type == XITouchClass)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XITouchClassInfo* touchClass = (XITouchClassInfo*)dclass;
|
|
|
|
if (touchClass->mode == XIDirectTouch)
|
2015-11-21 01:12:22 +00:00
|
|
|
m_touchType = ETouchType::Display;
|
2015-10-31 04:28:21 +00:00
|
|
|
else if (touchClass->mode == XIDependentTouch)
|
2015-11-21 01:12:22 +00:00
|
|
|
m_touchType = ETouchType::Trackpad;
|
2015-05-13 08:51:18 +00:00
|
|
|
else
|
2015-11-21 01:12:22 +00:00
|
|
|
m_touchType = ETouchType::None;
|
2015-05-13 08:51:18 +00:00
|
|
|
}
|
|
|
|
}
|
2015-05-12 09:38:37 +00:00
|
|
|
}
|
2015-05-13 08:51:18 +00:00
|
|
|
|
2015-10-31 04:28:21 +00:00
|
|
|
XIFreeDeviceInfo(devices);
|
2015-05-13 08:51:18 +00:00
|
|
|
m_lastInputID = deviceId;
|
|
|
|
}
|
|
|
|
|
2015-12-04 01:33:43 +00:00
|
|
|
SWindowCoord MakeButtonEventCoord(XEvent* event) const
|
|
|
|
{
|
|
|
|
int x = event->xbutton.x;
|
2016-01-02 23:10:35 +00:00
|
|
|
int y = m_wrect.size[1]-event->xbutton.y;
|
2015-12-04 01:33:43 +00:00
|
|
|
return
|
|
|
|
{
|
|
|
|
{x, y},
|
|
|
|
{int(x / m_pixelFactor), int(y / m_pixelFactor)},
|
2016-01-02 23:10:35 +00:00
|
|
|
{x / float(m_wrect.size[0]), y / float(m_wrect.size[1])}
|
2015-12-04 01:33:43 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
SWindowCoord MakeMotionEventCoord(XEvent* event) const
|
|
|
|
{
|
|
|
|
int x = event->xmotion.x;
|
2016-01-02 23:10:35 +00:00
|
|
|
int y = m_wrect.size[1]-event->xmotion.y;
|
2015-12-04 01:33:43 +00:00
|
|
|
return
|
|
|
|
{
|
|
|
|
{x, y},
|
|
|
|
{int(x / m_pixelFactor), int(y / m_pixelFactor)},
|
2016-01-02 23:10:35 +00:00
|
|
|
{x / float(m_wrect.size[0]), y / float(m_wrect.size[1])}
|
2015-12-04 01:33:43 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
SWindowCoord MakeCrossingEventCoord(XEvent* event) const
|
|
|
|
{
|
|
|
|
int x = event->xcrossing.x;
|
2016-01-02 23:10:35 +00:00
|
|
|
int y = m_wrect.size[1]-event->xcrossing.y;
|
2015-12-04 01:33:43 +00:00
|
|
|
return
|
|
|
|
{
|
|
|
|
{x, y},
|
|
|
|
{int(x / m_pixelFactor), int(y / m_pixelFactor)},
|
2016-01-02 23:10:35 +00:00
|
|
|
{x / float(m_wrect.size[0]), y / float(m_wrect.size[1])}
|
2015-12-04 01:33:43 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-01-06 21:02:23 +00:00
|
|
|
#if 0
|
2015-12-24 03:30:52 +00:00
|
|
|
/* This procedure sets the application's size constraints and returns
|
|
|
|
* the IM's preferred size for either the Preedit or Status areas,
|
|
|
|
* depending on the value of the name argument. The area argument is
|
|
|
|
* used to pass the constraints and to return the preferred size.
|
|
|
|
*/
|
|
|
|
void GetPreferredGeometry(const char* name, XRectangle* area)
|
|
|
|
{
|
|
|
|
XVaNestedList list;
|
|
|
|
list = XVaCreateNestedList(0, XNAreaNeeded, area, nullptr);
|
|
|
|
/* set the constraints */
|
|
|
|
XSetICValues(m_xIC, name, list, nullptr);
|
|
|
|
/* query the preferred size */
|
|
|
|
XGetICValues(m_xIC, name, list, nullptr);
|
|
|
|
XFree(list);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This procedure sets the geometry of either the Preedit or Status
|
|
|
|
* Areas, depending on the value of the name argument.
|
|
|
|
*/
|
|
|
|
void SetGeometry(const char* name, XRectangle* area)
|
|
|
|
{
|
|
|
|
XVaNestedList list;
|
|
|
|
list = XVaCreateNestedList(0, XNArea, area, nullptr);
|
|
|
|
XSetICValues(m_xIC, name, list, nullptr);
|
|
|
|
XFree(list);
|
|
|
|
}
|
2016-01-06 21:02:23 +00:00
|
|
|
#endif
|
2015-12-24 03:30:52 +00:00
|
|
|
|
2018-05-06 22:44:31 +00:00
|
|
|
bool _incomingEvent(void* e)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XEvent* event = (XEvent*)e;
|
|
|
|
switch (event->type)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-12-24 03:30:52 +00:00
|
|
|
case SelectionRequest:
|
|
|
|
{
|
|
|
|
handleSelectionRequest(&event->xselectionrequest);
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-12-24 03:30:52 +00:00
|
|
|
}
|
2015-11-05 09:28:51 +00:00
|
|
|
case ClientMessage:
|
|
|
|
{
|
|
|
|
if (event->xclient.data.l[0] == S_ATOMS->m_wmDeleteWindow && m_callback)
|
2015-11-26 23:03:01 +00:00
|
|
|
{
|
2015-11-05 09:28:51 +00:00
|
|
|
m_callback->destroyed();
|
2015-11-26 23:03:01 +00:00
|
|
|
m_callback = nullptr;
|
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
return true;
|
2015-11-05 09:28:51 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case Expose:
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-11-05 09:28:51 +00:00
|
|
|
Window nw;
|
|
|
|
XWindowAttributes wxa;
|
|
|
|
int x, y;
|
|
|
|
XTranslateCoordinates(m_xDisp, m_windowId, DefaultRootWindow(m_xDisp), event->xexpose.x, event->xexpose.y, &x, &y, &nw);
|
|
|
|
XGetWindowAttributes(m_xDisp, m_windowId, &wxa);
|
2016-01-02 23:10:35 +00:00
|
|
|
m_wrect.location[0] = x - wxa.x;
|
|
|
|
m_wrect.location[1] = y - wxa.y;
|
2016-03-17 21:03:38 +00:00
|
|
|
#if 0
|
|
|
|
/* This breaks with GNOME, why? */
|
2016-01-02 23:10:35 +00:00
|
|
|
m_wrect.size[0] = event->xexpose.width;
|
|
|
|
m_wrect.size[1] = event->xexpose.height;
|
2016-03-17 21:03:38 +00:00
|
|
|
#else
|
|
|
|
m_wrect.size[0] = wxa.width;
|
|
|
|
m_wrect.size[1] = wxa.height;
|
|
|
|
#endif
|
2015-11-02 09:31:06 +00:00
|
|
|
if (m_callback)
|
|
|
|
{
|
2015-12-21 00:55:18 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2016-06-27 04:33:01 +00:00
|
|
|
m_gfxCtx->resized(m_wrect);
|
2017-02-15 23:13:05 +00:00
|
|
|
m_callback->resized(m_wrect, m_openGL);
|
2015-12-21 00:55:18 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-11-02 09:31:06 +00:00
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case ConfigureNotify:
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-11-05 09:28:51 +00:00
|
|
|
Window nw;
|
|
|
|
XWindowAttributes wxa;
|
|
|
|
int x, y;
|
|
|
|
XTranslateCoordinates(m_xDisp, m_windowId, DefaultRootWindow(m_xDisp), event->xconfigure.x, event->xconfigure.y, &x, &y, &nw);
|
|
|
|
XGetWindowAttributes(m_xDisp, m_windowId, &wxa);
|
2016-01-02 23:10:35 +00:00
|
|
|
m_wrect.location[0] = x - wxa.x;
|
|
|
|
m_wrect.location[1] = y - wxa.y;
|
|
|
|
m_wrect.size[0] = event->xconfigure.width;
|
|
|
|
m_wrect.size[1] = event->xconfigure.height;
|
2015-11-02 09:31:06 +00:00
|
|
|
|
2015-11-05 09:28:51 +00:00
|
|
|
if (m_callback)
|
2016-01-02 23:10:35 +00:00
|
|
|
m_callback->windowMoved(m_wrect);
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case KeyPress:
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
|
|
|
if (m_callback)
|
|
|
|
{
|
2015-11-21 01:12:22 +00:00
|
|
|
ESpecialKey specialKey;
|
|
|
|
EModifierKey modifierKey;
|
2015-12-24 03:30:52 +00:00
|
|
|
unsigned int state = event->xkey.state;
|
|
|
|
event->xkey.state &= ~ControlMask;
|
2015-12-27 23:23:17 +00:00
|
|
|
ITextInputCallback* inputCb = m_callback->getTextInputCallback();
|
2016-01-06 21:02:23 +00:00
|
|
|
if (m_xIC)
|
2015-12-24 20:55:23 +00:00
|
|
|
{
|
2016-01-06 21:02:23 +00:00
|
|
|
std::string utf8Frag = translateUTF8(&event->xkey, m_xIC);
|
|
|
|
if (utf8Frag.size())
|
|
|
|
{
|
|
|
|
if (inputCb)
|
|
|
|
inputCb->insertText(utf8Frag);
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2016-01-06 21:02:23 +00:00
|
|
|
}
|
2015-12-24 20:55:23 +00:00
|
|
|
}
|
|
|
|
char charCode = translateKeysym(&event->xkey, specialKey, modifierKey);
|
2015-12-24 03:30:52 +00:00
|
|
|
EModifierKey modifierMask = translateModifiers(state);
|
2015-05-09 05:33:48 +00:00
|
|
|
if (charCode)
|
2015-12-27 23:23:17 +00:00
|
|
|
{
|
|
|
|
if (inputCb &&
|
|
|
|
(modifierMask & (EModifierKey::Ctrl|EModifierKey::Command)) == EModifierKey::None)
|
|
|
|
inputCb->insertText(std::string(1, charCode));
|
2016-05-15 21:54:55 +00:00
|
|
|
|
|
|
|
bool isRepeat = m_charKeys.find(charCode) != m_charKeys.cend();
|
|
|
|
m_callback->charKeyDown(charCode, modifierMask, isRepeat);
|
|
|
|
if (!isRepeat)
|
|
|
|
m_charKeys.insert(charCode);
|
2015-12-27 23:23:17 +00:00
|
|
|
}
|
2015-11-21 01:12:22 +00:00
|
|
|
else if (specialKey != ESpecialKey::None)
|
2016-05-15 21:54:55 +00:00
|
|
|
{
|
|
|
|
bool isRepeat = m_specialKeys.find((unsigned long)specialKey) != m_specialKeys.cend();
|
|
|
|
m_callback->specialKeyDown(specialKey, modifierMask, isRepeat);
|
|
|
|
if (!isRepeat)
|
|
|
|
m_specialKeys.insert((unsigned long)specialKey);
|
|
|
|
}
|
2015-11-21 01:12:22 +00:00
|
|
|
else if (modifierKey != EModifierKey::None)
|
2016-05-15 21:54:55 +00:00
|
|
|
{
|
|
|
|
bool isRepeat = m_modKeys.find((unsigned long)modifierKey) != m_modKeys.cend();
|
|
|
|
m_callback->modKeyDown(modifierKey, isRepeat);
|
|
|
|
if (!isRepeat)
|
|
|
|
m_modKeys.insert((unsigned long)modifierKey);
|
|
|
|
}
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case KeyRelease:
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
|
|
|
if (m_callback)
|
|
|
|
{
|
2015-11-21 01:12:22 +00:00
|
|
|
ESpecialKey specialKey;
|
|
|
|
EModifierKey modifierKey;
|
2015-12-24 03:30:52 +00:00
|
|
|
unsigned int state = event->xkey.state;
|
|
|
|
event->xkey.state &= ~ControlMask;
|
2015-12-24 20:55:23 +00:00
|
|
|
char charCode = translateKeysym(&event->xkey, specialKey, modifierKey);
|
2015-12-24 03:30:52 +00:00
|
|
|
EModifierKey modifierMask = translateModifiers(state);
|
2015-05-09 05:33:48 +00:00
|
|
|
if (charCode)
|
2016-05-15 21:54:55 +00:00
|
|
|
{
|
|
|
|
m_charKeys.erase(charCode);
|
2015-11-21 01:12:22 +00:00
|
|
|
m_callback->charKeyUp(charCode, modifierMask);
|
2016-05-15 21:54:55 +00:00
|
|
|
}
|
2015-11-21 01:12:22 +00:00
|
|
|
else if (specialKey != ESpecialKey::None)
|
2016-05-15 21:54:55 +00:00
|
|
|
{
|
|
|
|
m_specialKeys.erase((unsigned long)specialKey);
|
2015-11-21 01:12:22 +00:00
|
|
|
m_callback->specialKeyUp(specialKey, modifierMask);
|
2016-05-15 21:54:55 +00:00
|
|
|
}
|
2015-11-21 01:12:22 +00:00
|
|
|
else if (modifierKey != EModifierKey::None)
|
2016-05-15 21:54:55 +00:00
|
|
|
{
|
|
|
|
m_modKeys.erase((unsigned long)modifierKey);
|
2015-11-21 01:12:22 +00:00
|
|
|
m_callback->modKeyUp(modifierKey);
|
2016-05-15 21:54:55 +00:00
|
|
|
}
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case ButtonPress:
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-05-13 08:51:18 +00:00
|
|
|
if (m_callback)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2016-01-02 23:10:35 +00:00
|
|
|
getWindowFrame(m_wrect.location[0], m_wrect.location[1], m_wrect.size[0], m_wrect.size[1]);
|
2015-11-21 01:12:22 +00:00
|
|
|
EMouseButton button = translateButton(event->xbutton.button);
|
|
|
|
if (button != EMouseButton::None)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-11-21 01:12:22 +00:00
|
|
|
EModifierKey modifierMask = translateModifiers(event->xbutton.state);
|
2015-12-04 01:33:43 +00:00
|
|
|
m_callback->mouseDown(MakeButtonEventCoord(event), (EMouseButton)button,
|
2015-11-03 04:27:56 +00:00
|
|
|
(EModifierKey)modifierMask);
|
2015-05-13 08:51:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Also handle legacy scroll events here */
|
2015-10-31 04:28:21 +00:00
|
|
|
if (event->xbutton.button >= 4 && event->xbutton.button <= 7 &&
|
2015-05-13 08:51:18 +00:00
|
|
|
m_hScrollValuator == -1 && m_vScrollValuator == -1)
|
|
|
|
{
|
2015-11-03 04:27:56 +00:00
|
|
|
SScrollDelta scrollDelta =
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
|
|
|
{0.0, 0.0},
|
|
|
|
false
|
|
|
|
};
|
2015-10-31 04:28:21 +00:00
|
|
|
if (event->xbutton.button == 4)
|
2015-05-13 08:51:18 +00:00
|
|
|
scrollDelta.delta[1] = 1.0;
|
2015-10-31 04:28:21 +00:00
|
|
|
else if (event->xbutton.button == 5)
|
2015-05-13 08:51:18 +00:00
|
|
|
scrollDelta.delta[1] = -1.0;
|
2015-10-31 04:28:21 +00:00
|
|
|
else if (event->xbutton.button == 6)
|
2015-05-13 08:51:18 +00:00
|
|
|
scrollDelta.delta[0] = 1.0;
|
2015-10-31 04:28:21 +00:00
|
|
|
else if (event->xbutton.button == 7)
|
2015-05-13 08:51:18 +00:00
|
|
|
scrollDelta.delta[0] = -1.0;
|
2015-12-04 01:33:43 +00:00
|
|
|
m_callback->scroll(MakeButtonEventCoord(event), scrollDelta);
|
2015-05-13 08:51:18 +00:00
|
|
|
}
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case ButtonRelease:
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-05-13 08:51:18 +00:00
|
|
|
if (m_callback)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2016-01-02 23:10:35 +00:00
|
|
|
getWindowFrame(m_wrect.location[0], m_wrect.location[1], m_wrect.size[0], m_wrect.size[1]);
|
2015-11-21 01:12:22 +00:00
|
|
|
EMouseButton button = translateButton(event->xbutton.button);
|
|
|
|
if (button != EMouseButton::None)
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
2015-11-21 01:12:22 +00:00
|
|
|
EModifierKey modifierMask = translateModifiers(event->xbutton.state);
|
2015-12-04 01:33:43 +00:00
|
|
|
m_callback->mouseUp(MakeButtonEventCoord(event), (EMouseButton)button,
|
2015-11-03 04:27:56 +00:00
|
|
|
(EModifierKey)modifierMask);
|
2015-05-13 08:51:18 +00:00
|
|
|
}
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2015-11-05 09:28:51 +00:00
|
|
|
case FocusIn:
|
|
|
|
{
|
|
|
|
if (m_callback)
|
|
|
|
m_callback->focusGained();
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-11-05 09:28:51 +00:00
|
|
|
}
|
|
|
|
case FocusOut:
|
|
|
|
{
|
|
|
|
if (m_callback)
|
|
|
|
m_callback->focusLost();
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-11-05 09:28:51 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case MotionNotify:
|
2015-05-09 05:33:48 +00:00
|
|
|
{
|
|
|
|
if (m_callback)
|
|
|
|
{
|
2016-01-02 23:10:35 +00:00
|
|
|
getWindowFrame(m_wrect.location[0], m_wrect.location[1], m_wrect.size[0], m_wrect.size[1]);
|
2015-12-04 01:33:43 +00:00
|
|
|
m_callback->mouseMove(MakeMotionEventCoord(event));
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-13 08:51:18 +00:00
|
|
|
}
|
2015-11-05 09:28:51 +00:00
|
|
|
case EnterNotify:
|
|
|
|
{
|
|
|
|
if (m_callback)
|
|
|
|
{
|
2016-01-02 23:10:35 +00:00
|
|
|
getWindowFrame(m_wrect.location[0], m_wrect.location[1], m_wrect.size[0], m_wrect.size[1]);
|
2015-12-04 01:33:43 +00:00
|
|
|
m_callback->mouseEnter(MakeCrossingEventCoord(event));
|
2015-11-05 09:28:51 +00:00
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-11-05 09:28:51 +00:00
|
|
|
}
|
|
|
|
case LeaveNotify:
|
|
|
|
{
|
|
|
|
if (m_callback)
|
|
|
|
{
|
2016-01-02 23:10:35 +00:00
|
|
|
getWindowFrame(m_wrect.location[0], m_wrect.location[1], m_wrect.size[0], m_wrect.size[1]);
|
2015-12-04 01:33:43 +00:00
|
|
|
m_callback->mouseLeave(MakeCrossingEventCoord(event));
|
2015-11-05 09:28:51 +00:00
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-11-05 09:28:51 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case GenericEvent:
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
if (event->xgeneric.extension == XINPUT_OPCODE)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2016-01-02 23:10:35 +00:00
|
|
|
getWindowFrame(m_wrect.location[0], m_wrect.location[1], m_wrect.size[0], m_wrect.size[1]);
|
2015-10-31 04:28:21 +00:00
|
|
|
switch (event->xgeneric.evtype)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
case XI_Motion:
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-11-02 09:31:06 +00:00
|
|
|
fprintf(stderr, "motion\n");
|
|
|
|
|
2015-10-31 04:28:21 +00:00
|
|
|
XIDeviceEvent* ev = (XIDeviceEvent*)event;
|
2015-05-13 08:51:18 +00:00
|
|
|
if (m_lastInputID != ev->deviceid)
|
|
|
|
_pointingDeviceChanged(ev->deviceid);
|
|
|
|
|
|
|
|
int cv = 0;
|
|
|
|
double newScroll[2] = {m_hScrollLast, m_vScrollLast};
|
|
|
|
bool didScroll = false;
|
2015-10-31 04:28:21 +00:00
|
|
|
for (int i=0 ; i<ev->valuators.mask_len*8 ; ++i)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
if (XIMaskIsSet(ev->valuators.mask, i))
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
|
|
|
if (i == m_hScrollValuator)
|
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
newScroll[0] = ev->valuators.values[cv];
|
2015-05-13 08:51:18 +00:00
|
|
|
didScroll = true;
|
|
|
|
}
|
|
|
|
else if (i == m_vScrollValuator)
|
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
newScroll[1] = ev->valuators.values[cv];
|
2015-05-13 08:51:18 +00:00
|
|
|
didScroll = true;
|
|
|
|
}
|
|
|
|
++cv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-03 04:27:56 +00:00
|
|
|
SScrollDelta scrollDelta =
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
|
|
|
{newScroll[0] - m_hScrollLast, newScroll[1] - m_vScrollLast},
|
|
|
|
true
|
|
|
|
};
|
|
|
|
|
|
|
|
m_hScrollLast = newScroll[0];
|
|
|
|
m_vScrollLast = newScroll[1];
|
|
|
|
|
|
|
|
if (m_callback && didScroll)
|
|
|
|
{
|
2015-12-04 01:33:43 +00:00
|
|
|
int event_x = int(ev->event_x) >> 16;
|
2016-01-02 23:10:35 +00:00
|
|
|
int event_y = m_wrect.size[1] - (int(ev->event_y) >> 16);
|
2015-11-03 04:27:56 +00:00
|
|
|
SWindowCoord coord =
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
|
|
|
{event_x, event_y},
|
2015-12-04 01:33:43 +00:00
|
|
|
{int(event_x / m_pixelFactor), int(event_y / m_pixelFactor)},
|
2016-01-02 23:10:35 +00:00
|
|
|
{event_x / float(m_wrect.size[0]), event_y / float(m_wrect.size[1])}
|
2015-05-13 08:51:18 +00:00
|
|
|
};
|
|
|
|
m_callback->scroll(coord, scrollDelta);
|
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-13 08:51:18 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case XI_TouchBegin:
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XIDeviceEvent* ev = (XIDeviceEvent*)event;
|
2015-05-13 08:51:18 +00:00
|
|
|
if (m_lastInputID != ev->deviceid)
|
|
|
|
_pointingDeviceChanged(ev->deviceid);
|
|
|
|
|
|
|
|
int cv = 0;
|
|
|
|
double vals[32] = {};
|
2015-10-31 04:28:21 +00:00
|
|
|
for (int i=0 ; i<ev->valuators.mask_len*8 && i<32 ; ++i)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
if (XIMaskIsSet(ev->valuators.mask, i))
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
vals[i] = ev->valuators.values[cv];
|
2015-05-13 08:51:18 +00:00
|
|
|
++cv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-03 04:27:56 +00:00
|
|
|
STouchCoord coord =
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
|
|
|
{vals[0], vals[1]}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (m_callback)
|
|
|
|
m_callback->touchDown(coord, ev->detail);
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-13 08:51:18 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case XI_TouchUpdate:
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XIDeviceEvent* ev = (XIDeviceEvent*)event;
|
2015-05-13 08:51:18 +00:00
|
|
|
if (m_lastInputID != ev->deviceid)
|
|
|
|
_pointingDeviceChanged(ev->deviceid);
|
|
|
|
|
|
|
|
int cv = 0;
|
|
|
|
double vals[32] = {};
|
2015-10-31 04:28:21 +00:00
|
|
|
for (int i=0 ; i<ev->valuators.mask_len*8 && i<32 ; ++i)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
if (XIMaskIsSet(ev->valuators.mask, i))
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
vals[i] = ev->valuators.values[cv];
|
2015-05-13 08:51:18 +00:00
|
|
|
++cv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-03 04:27:56 +00:00
|
|
|
STouchCoord coord =
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
|
|
|
{vals[0], vals[1]}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (m_callback)
|
|
|
|
m_callback->touchMove(coord, ev->detail);
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-13 08:51:18 +00:00
|
|
|
}
|
2015-10-31 04:28:21 +00:00
|
|
|
case XI_TouchEnd:
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
XIDeviceEvent* ev = (XIDeviceEvent*)event;
|
2015-05-13 08:51:18 +00:00
|
|
|
if (m_lastInputID != ev->deviceid)
|
|
|
|
_pointingDeviceChanged(ev->deviceid);
|
|
|
|
|
|
|
|
int cv = 0;
|
|
|
|
double vals[32] = {};
|
2015-10-31 04:28:21 +00:00
|
|
|
for (int i=0 ; i<ev->valuators.mask_len*8 && i<32 ; ++i)
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
if (XIMaskIsSet(ev->valuators.mask, i))
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
2015-10-31 04:28:21 +00:00
|
|
|
vals[i] = ev->valuators.values[cv];
|
2015-05-13 08:51:18 +00:00
|
|
|
++cv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-03 04:27:56 +00:00
|
|
|
STouchCoord coord =
|
2015-05-13 08:51:18 +00:00
|
|
|
{
|
|
|
|
{vals[0], vals[1]}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (m_callback)
|
|
|
|
m_callback->touchUp(coord, ev->detail);
|
2018-05-06 22:44:31 +00:00
|
|
|
return false;
|
2015-05-13 08:51:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-05-09 05:33:48 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-06 22:44:31 +00:00
|
|
|
|
|
|
|
return false;
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ETouchType getTouchType() const
|
|
|
|
{
|
2015-05-13 08:51:18 +00:00
|
|
|
return m_touchType;
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
2015-10-30 06:26:02 +00:00
|
|
|
IGraphicsCommandQueue* getCommandQueue()
|
|
|
|
{
|
2016-02-22 02:47:45 +00:00
|
|
|
return m_gfxCtx->getCommandQueue();
|
2015-10-30 06:26:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
IGraphicsDataFactory* getDataFactory()
|
|
|
|
{
|
2016-02-22 02:47:45 +00:00
|
|
|
return m_gfxCtx->getDataFactory();
|
2015-10-30 06:26:02 +00:00
|
|
|
}
|
|
|
|
|
2015-11-17 06:41:32 +00:00
|
|
|
IGraphicsDataFactory* getMainContextDataFactory()
|
|
|
|
{
|
2016-02-22 02:47:45 +00:00
|
|
|
return m_gfxCtx->getMainContextDataFactory();
|
2015-11-17 06:41:32 +00:00
|
|
|
}
|
|
|
|
|
2015-10-30 06:26:02 +00:00
|
|
|
IGraphicsDataFactory* getLoadContextDataFactory()
|
|
|
|
{
|
2016-02-22 02:47:45 +00:00
|
|
|
return m_gfxCtx->getLoadContextDataFactory();
|
2015-10-30 06:26:02 +00:00
|
|
|
}
|
|
|
|
|
2015-11-05 04:31:30 +00:00
|
|
|
bool _isWindowMapped()
|
|
|
|
{
|
|
|
|
XWindowAttributes attr;
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(m_xDisp);
|
2015-11-05 04:31:30 +00:00
|
|
|
XGetWindowAttributes(m_xDisp, m_windowId, &attr);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(m_xDisp);
|
2015-11-05 04:31:30 +00:00
|
|
|
return attr.map_state != IsUnmapped;
|
|
|
|
}
|
2015-05-06 00:50:57 +00:00
|
|
|
};
|
|
|
|
|
2017-11-13 07:19:49 +00:00
|
|
|
std::shared_ptr<IWindow> _WindowXlibNew(std::string_view title,
|
2017-07-17 03:58:18 +00:00
|
|
|
Display* display, void* xcbConn,
|
|
|
|
int defaultScreen, XIM xIM, XIMStyle bestInputStyle, XFontSet fontset,
|
2018-01-07 09:25:30 +00:00
|
|
|
GLXContext lastCtx, void* vulkanHandle, GLContext* glCtx)
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
2015-11-12 04:31:59 +00:00
|
|
|
XLockDisplay(display);
|
2017-07-17 03:58:18 +00:00
|
|
|
std::shared_ptr<IWindow> ret = std::make_shared<WindowXlib>(title, display, xcbConn,
|
|
|
|
defaultScreen, xIM, bestInputStyle, fontset, lastCtx,
|
2018-01-07 09:25:30 +00:00
|
|
|
vulkanHandle, glCtx);
|
2015-11-12 04:31:59 +00:00
|
|
|
XUnlockDisplay(display);
|
|
|
|
return ret;
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|