2016-05-23 20:13:36 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2016 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
//#include <tchar.h>
|
|
|
|
|
2016-07-26 19:02:50 +00:00
|
|
|
#include "WindowContextFactory_unix.h"
|
|
|
|
|
2016-05-23 20:13:36 +00:00
|
|
|
#include "SkUtils.h"
|
|
|
|
#include "Timer.h"
|
2016-05-27 13:47:08 +00:00
|
|
|
#include "../GLWindowContext.h"
|
2016-05-23 20:13:36 +00:00
|
|
|
#include "Window_unix.h"
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
#include "keysym2ucs.h"
|
|
|
|
}
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
#include <X11/XKBlib.h>
|
|
|
|
|
|
|
|
namespace sk_app {
|
|
|
|
|
|
|
|
SkTDynamicHash<Window_unix, XWindow> Window_unix::gWindowMap;
|
|
|
|
|
|
|
|
Window* Window::CreateNativeWindow(void* platformData) {
|
|
|
|
Display* display = (Display*)platformData;
|
2017-01-05 18:50:49 +00:00
|
|
|
SkASSERT(display);
|
2016-05-23 20:13:36 +00:00
|
|
|
|
|
|
|
Window_unix* window = new Window_unix();
|
2016-05-27 13:47:08 +00:00
|
|
|
if (!window->initWindow(display, nullptr)) {
|
2016-05-23 20:13:36 +00:00
|
|
|
delete window;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return window;
|
|
|
|
}
|
|
|
|
|
2016-05-27 13:47:08 +00:00
|
|
|
const long kEventMask = ExposureMask | StructureNotifyMask |
|
|
|
|
KeyPressMask | KeyReleaseMask |
|
|
|
|
PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
|
|
|
|
|
|
|
|
bool Window_unix::initWindow(Display* display, const DisplayParams* params) {
|
|
|
|
if (params && params->fMSAASampleCount != fMSAASampleCount) {
|
|
|
|
this->closeWindow();
|
|
|
|
}
|
|
|
|
// we already have a window
|
|
|
|
if (fDisplay) {
|
|
|
|
return true;
|
2016-07-26 19:56:32 +00:00
|
|
|
}
|
2016-05-23 20:13:36 +00:00
|
|
|
fDisplay = display;
|
|
|
|
|
|
|
|
fWidth = 1280;
|
|
|
|
fHeight = 960;
|
|
|
|
|
2016-05-27 13:47:08 +00:00
|
|
|
// Attempt to create a window that supports GL
|
|
|
|
GLint att[] = {
|
|
|
|
GLX_RGBA,
|
|
|
|
GLX_DEPTH_SIZE, 24,
|
|
|
|
GLX_DOUBLEBUFFER,
|
|
|
|
GLX_STENCIL_SIZE, 8,
|
|
|
|
None
|
|
|
|
};
|
|
|
|
SkASSERT(nullptr == fVisualInfo);
|
|
|
|
if (params && params->fMSAASampleCount > 0) {
|
|
|
|
static const GLint kAttCount = SK_ARRAY_COUNT(att);
|
|
|
|
GLint msaaAtt[kAttCount + 4];
|
|
|
|
memcpy(msaaAtt, att, sizeof(att));
|
|
|
|
SkASSERT(None == msaaAtt[kAttCount - 1]);
|
|
|
|
msaaAtt[kAttCount - 1] = GLX_SAMPLE_BUFFERS_ARB;
|
|
|
|
msaaAtt[kAttCount + 0] = 1;
|
|
|
|
msaaAtt[kAttCount + 1] = GLX_SAMPLES_ARB;
|
|
|
|
msaaAtt[kAttCount + 2] = params->fMSAASampleCount;
|
|
|
|
msaaAtt[kAttCount + 3] = None;
|
|
|
|
fVisualInfo = glXChooseVisual(display, DefaultScreen(display), msaaAtt);
|
|
|
|
fMSAASampleCount = params->fMSAASampleCount;
|
|
|
|
}
|
|
|
|
if (nullptr == fVisualInfo) {
|
|
|
|
fVisualInfo = glXChooseVisual(display, DefaultScreen(display), att);
|
|
|
|
fMSAASampleCount = 0;
|
2016-05-27 12:47:00 +00:00
|
|
|
}
|
2016-05-23 20:13:36 +00:00
|
|
|
|
2016-05-27 13:47:08 +00:00
|
|
|
if (fVisualInfo) {
|
|
|
|
Colormap colorMap = XCreateColormap(display,
|
|
|
|
RootWindow(display, fVisualInfo->screen),
|
|
|
|
fVisualInfo->visual,
|
|
|
|
AllocNone);
|
|
|
|
XSetWindowAttributes swa;
|
|
|
|
swa.colormap = colorMap;
|
|
|
|
swa.event_mask = kEventMask;
|
|
|
|
fWindow = XCreateWindow(display,
|
|
|
|
RootWindow(display, fVisualInfo->screen),
|
|
|
|
0, 0, // x, y
|
|
|
|
fWidth, fHeight,
|
|
|
|
0, // border width
|
|
|
|
fVisualInfo->depth,
|
|
|
|
InputOutput,
|
|
|
|
fVisualInfo->visual,
|
|
|
|
CWEventMask | CWColormap,
|
|
|
|
&swa);
|
|
|
|
} else {
|
|
|
|
// Create a simple window instead. We will not be able to show GL
|
|
|
|
fWindow = XCreateSimpleWindow(display,
|
|
|
|
DefaultRootWindow(display),
|
|
|
|
0, 0, // x, y
|
|
|
|
fWidth, fHeight,
|
|
|
|
0, // border width
|
|
|
|
0, // border value
|
|
|
|
0); // background value
|
|
|
|
XSelectInput(display, fWindow, kEventMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fWindow) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-05-27 13:20:10 +00:00
|
|
|
|
2016-05-23 20:13:36 +00:00
|
|
|
// set up to catch window delete message
|
|
|
|
fWmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
|
2016-05-27 13:47:08 +00:00
|
|
|
XSetWMProtocols(display, fWindow, &fWmDeleteMessage, 1);
|
2016-05-23 20:13:36 +00:00
|
|
|
|
|
|
|
// add to hashtable of windows
|
|
|
|
gWindowMap.add(this);
|
|
|
|
|
|
|
|
// init event variables
|
|
|
|
fPendingPaint = false;
|
|
|
|
fPendingResize = false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-05-27 13:47:08 +00:00
|
|
|
void Window_unix::closeWindow() {
|
|
|
|
if (fDisplay) {
|
|
|
|
this->detach();
|
|
|
|
SkASSERT(fGC);
|
|
|
|
XFreeGC(fDisplay, fGC);
|
|
|
|
fGC = nullptr;
|
|
|
|
gWindowMap.remove(fWindow);
|
|
|
|
XDestroyWindow(fDisplay, fWindow);
|
|
|
|
fWindow = 0;
|
|
|
|
fVisualInfo = nullptr;
|
|
|
|
fDisplay = nullptr;
|
|
|
|
fMSAASampleCount = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-23 20:13:36 +00:00
|
|
|
static Window::Key get_key(KeySym keysym) {
|
|
|
|
static const struct {
|
|
|
|
KeySym fXK;
|
|
|
|
Window::Key fKey;
|
|
|
|
} gPair[] = {
|
|
|
|
{ XK_BackSpace, Window::Key::kBack },
|
|
|
|
{ XK_Clear, Window::Key::kBack },
|
|
|
|
{ XK_Return, Window::Key::kOK },
|
|
|
|
{ XK_Up, Window::Key::kUp },
|
|
|
|
{ XK_Down, Window::Key::kDown },
|
|
|
|
{ XK_Left, Window::Key::kLeft },
|
|
|
|
{ XK_Right, Window::Key::kRight }
|
|
|
|
};
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
|
|
|
|
if (gPair[i].fXK == keysym) {
|
|
|
|
return gPair[i].fKey;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Window::Key::kNONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t get_modifiers(const XEvent& event) {
|
|
|
|
static const struct {
|
|
|
|
unsigned fXMask;
|
|
|
|
unsigned fSkMask;
|
|
|
|
} gModifiers[] = {
|
|
|
|
{ ShiftMask, Window::kShift_ModifierKey },
|
|
|
|
{ ControlMask, Window::kControl_ModifierKey },
|
|
|
|
{ Mod1Mask, Window::kOption_ModifierKey },
|
|
|
|
};
|
|
|
|
|
|
|
|
auto modifiers = 0;
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) {
|
|
|
|
if (event.xkey.state & gModifiers[i].fXMask) {
|
|
|
|
modifiers |= gModifiers[i].fSkMask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return modifiers;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window_unix::handleEvent(const XEvent& event) {
|
|
|
|
switch (event.type) {
|
2016-05-27 13:47:08 +00:00
|
|
|
case MapNotify:
|
|
|
|
if (!fGC) {
|
|
|
|
fGC = XCreateGC(fDisplay, fWindow, 0, nullptr);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2016-05-23 20:13:36 +00:00
|
|
|
case ClientMessage:
|
|
|
|
if ((Atom)event.xclient.data.l[0] == fWmDeleteMessage &&
|
|
|
|
gWindowMap.count() == 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ButtonPress:
|
|
|
|
if (event.xbutton.button == Button1) {
|
|
|
|
this->onMouse(event.xbutton.x, event.xbutton.y,
|
|
|
|
Window::kDown_InputState, get_modifiers(event));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ButtonRelease:
|
|
|
|
if (event.xbutton.button == Button1) {
|
|
|
|
this->onMouse(event.xbutton.x, event.xbutton.y,
|
|
|
|
Window::kUp_InputState, get_modifiers(event));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MotionNotify:
|
|
|
|
// only track if left button is down
|
|
|
|
if (event.xmotion.state & Button1Mask) {
|
|
|
|
this->onMouse(event.xmotion.x, event.xmotion.y,
|
|
|
|
Window::kMove_InputState, get_modifiers(event));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KeyPress: {
|
|
|
|
int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
|
|
|
|
KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode,
|
|
|
|
0, shiftLevel);
|
|
|
|
if (keysym == XK_Escape) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Window::Key key = get_key(keysym);
|
|
|
|
if (key != Window::Key::kNONE) {
|
|
|
|
(void) this->onKey(key, Window::kDown_InputState,
|
|
|
|
get_modifiers(event));
|
|
|
|
} else {
|
|
|
|
long uni = keysym2ucs(keysym);
|
|
|
|
if (uni != -1) {
|
|
|
|
(void) this->onChar((SkUnichar) uni,
|
|
|
|
get_modifiers(event));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case KeyRelease: {
|
|
|
|
int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
|
|
|
|
KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode,
|
|
|
|
0, shiftLevel);
|
|
|
|
Window::Key key = get_key(keysym);
|
|
|
|
(void) this->onKey(key, Window::kUp_InputState,
|
|
|
|
get_modifiers(event));
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
// these events should be handled in the main event loop
|
|
|
|
SkASSERT(event.type != Expose && event.type != ConfigureNotify);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window_unix::setTitle(const char* title) {
|
|
|
|
XTextProperty textproperty;
|
|
|
|
XStringListToTextProperty(const_cast<char**>(&title), 1, &textproperty);
|
2016-05-27 13:47:08 +00:00
|
|
|
XSetWMName(fDisplay, fWindow, &textproperty);
|
2016-05-23 20:13:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Window_unix::show() {
|
2016-05-27 13:47:08 +00:00
|
|
|
XMapWindow(fDisplay, fWindow);
|
2016-05-23 20:13:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Window_unix::attach(BackendType attachType, const DisplayParams& params) {
|
2016-05-27 13:47:08 +00:00
|
|
|
this->initWindow(fDisplay, ¶ms);
|
|
|
|
|
2016-07-26 19:56:32 +00:00
|
|
|
window_context_factory::XlibWindowInfo winInfo;
|
|
|
|
winInfo.fDisplay = fDisplay;
|
|
|
|
winInfo.fWindow = fWindow;
|
|
|
|
winInfo.fVisualInfo = fVisualInfo;
|
2016-05-23 20:13:36 +00:00
|
|
|
switch (attachType) {
|
2016-06-17 16:29:14 +00:00
|
|
|
#ifdef SK_VULKAN
|
2016-05-23 20:13:36 +00:00
|
|
|
case kVulkan_BackendType:
|
2016-07-26 19:56:32 +00:00
|
|
|
fWindowContext = window_context_factory::NewVulkanForXlib(winInfo, params);
|
2016-05-27 12:47:00 +00:00
|
|
|
break;
|
2016-06-17 16:29:14 +00:00
|
|
|
#endif
|
2016-05-27 13:47:08 +00:00
|
|
|
case kNativeGL_BackendType:
|
2016-07-26 19:56:32 +00:00
|
|
|
fWindowContext = window_context_factory::NewGLForXlib(winInfo, params);
|
|
|
|
break;
|
|
|
|
case kRaster_BackendType:
|
|
|
|
fWindowContext = window_context_factory::NewRasterForXlib(winInfo, params);
|
2016-05-27 13:47:08 +00:00
|
|
|
break;
|
2016-05-23 20:13:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (SkToBool(fWindowContext));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window_unix::onInval() {
|
|
|
|
XEvent event;
|
|
|
|
event.type = Expose;
|
|
|
|
event.xexpose.send_event = True;
|
|
|
|
event.xexpose.display = fDisplay;
|
2016-05-27 13:47:08 +00:00
|
|
|
event.xexpose.window = fWindow;
|
2016-05-23 20:13:36 +00:00
|
|
|
event.xexpose.x = 0;
|
|
|
|
event.xexpose.y = 0;
|
|
|
|
event.xexpose.width = fWidth;
|
|
|
|
event.xexpose.height = fHeight;
|
|
|
|
event.xexpose.count = 0;
|
|
|
|
|
2016-05-27 13:47:08 +00:00
|
|
|
XSendEvent(fDisplay, fWindow, False, 0, &event);
|
2016-05-23 20:13:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace sk_app
|