blob: 953eb36d74413ea41568de340883f28f6e7e43e5 [file] [log] [blame]
/*
* 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>
#include "SkUtils.h"
#include "Timer.h"
#include "../VulkanWindowContext.h"
#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;
Window_unix* window = new Window_unix();
if (!window->init(display)) {
delete window;
return nullptr;
}
return window;
}
bool Window_unix::init(Display* display) {
fDisplay = display;
fWidth = 1280;
fHeight = 960;
fHWnd = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, fWidth, fHeight,
0, BlackPixel(display, DefaultScreen(display)),
BlackPixel(display, DefaultScreen(display)));
if (!fHWnd) {
return false;
}
// choose the events we care about
XSelectInput(display, fHWnd,
ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
// set up to catch window delete message
fWmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, fHWnd, &fWmDeleteMessage, 1);
// add to hashtable of windows
gWindowMap.add(this);
// init event variables
fPendingPaint = false;
fPendingResize = false;
return true;
}
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) {
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);
XSetWMName(fDisplay, fHWnd, &textproperty);
}
void Window_unix::show() {
XMapWindow(fDisplay, fHWnd);
}
bool Window_unix::attach(BackendType attachType, const DisplayParams& params) {
ContextPlatformData_unix platformData;
platformData.fDisplay = fDisplay;
platformData.fHWnd = fHWnd;
XWindowAttributes attribs;
XGetWindowAttributes(fDisplay, fHWnd, &attribs);
platformData.fVisualID = XVisualIDFromVisual(attribs.visual);
switch (attachType) {
case kVulkan_BackendType:
default:
fWindowContext = VulkanWindowContext::Create((void*)&platformData, params);
break;
}
return (SkToBool(fWindowContext));
}
void Window_unix::onInval() {
XEvent event;
event.type = Expose;
event.xexpose.send_event = True;
event.xexpose.display = fDisplay;
event.xexpose.window = fHWnd;
event.xexpose.x = 0;
event.xexpose.y = 0;
event.xexpose.width = fWidth;
event.xexpose.height = fHeight;
event.xexpose.count = 0;
XSendEvent(fDisplay, fHWnd, False, 0, &event);
}
} // namespace sk_app