Implement mouse events and display on Linux/Skia port

This commit is contained in:
David Capello 2017-04-20 16:33:05 -03:00
parent 706b9a84c6
commit 5e11bebaf0
8 changed files with 403 additions and 113 deletions

View File

@ -212,6 +212,7 @@ if(USE_SKIA_BACKEND)
skia/skia_window_x11.cpp
x11/event_queue.cpp
x11/keys.cpp
x11/window.cpp
x11/x11.cpp)
endif()
endif()

View File

@ -151,12 +151,7 @@ bool SkiaDisplay::setNativeMouseCursor(const she::Surface* surface,
const gfx::Point& focus,
const int scale)
{
#if defined(_WIN32) || defined(__APPLE__)
return m_window.setNativeMouseCursor(surface, focus, scale);
#else
// TODO impl this for Linux
return false;
#endif
}
void SkiaDisplay::setMousePosition(const gfx::Point& position)

View File

@ -70,10 +70,8 @@ public:
return Capabilities(
int(Capabilities::MultipleDisplays) |
int(Capabilities::CanResizeDisplay) |
int(Capabilities::DisplayScale)
#if defined(_WIN32) || defined(__APPLE__)
| int(Capabilities::CustomNativeMouseCursor)
#endif
int(Capabilities::DisplayScale) |
int(Capabilities::CustomNativeMouseCursor)
// TODO enable this when the GPU support is ready
#if 0 // SK_SUPPORT_GPU
| int(Capabilities::GpuAccelerationSwitch)

View File

@ -14,17 +14,46 @@
#include "she/event.h"
#include "she/event_queue.h"
#include "she/skia/skia_display.h"
#include "she/skia/skia_surface.h"
#include "she/x11/x11.h"
#include "SkBitmap.h"
namespace she {
namespace {
bool convert_skia_bitmap_to_ximage(const SkBitmap& bitmap, XImage& image)
{
bitmap.lockPixels();
memset(&image, 0, sizeof(image));
int bpp = 8*bitmap.bytesPerPixel();
image.width = bitmap.width();
image.height = bitmap.height();
image.format = ZPixmap;
image.data = (char*)bitmap.getPixels();
image.byte_order = LSBFirst;
image.bitmap_unit = bpp;
image.bitmap_bit_order = LSBFirst;
image.bitmap_pad = bpp;
image.depth = 24;
image.bytes_per_line = bitmap.rowBytes() - 4*bitmap.width();
image.bits_per_pixel = bpp;
bool result = XInitImage(&image);
bitmap.unlockPixels();
return result;
}
} // anonymous namespace
SkiaWindow::SkiaWindow(EventQueue* queue, SkiaDisplay* display,
int width, int height, int scale)
: X11Window(X11::instance()->display(), width, height)
: X11Window(X11::instance()->display(), width, height, scale)
, m_queue(queue)
, m_display(display)
, m_clientSize(width, height)
, m_scale(scale)
{
}
@ -32,22 +61,6 @@ SkiaWindow::~SkiaWindow()
{
}
void SkiaWindow::queueEventImpl(Event& ev)
{
ev.setDisplay(m_display);
m_queue->queueEvent(ev);
}
int SkiaWindow::scale() const
{
return m_scale;
}
void SkiaWindow::setScale(int scale)
{
m_scale = scale;
}
void SkiaWindow::setVisible(bool visible)
{
}
@ -66,39 +79,59 @@ bool SkiaWindow::isMinimized() const
return false;
}
gfx::Size SkiaWindow::clientSize() const
void SkiaWindow::queueEvent(Event& ev)
{
return m_clientSize;
ev.setDisplay(m_display);
m_queue->queueEvent(ev);
}
gfx::Size SkiaWindow::restoredSize() const
void SkiaWindow::paintGC(const gfx::Rect& rc)
{
return m_clientSize;
SkiaSurface* surface = static_cast<SkiaSurface*>(m_display->getSurface());
const SkBitmap& bitmap = surface->bitmap();
int scale = this->scale();
if (scale == 1) {
XImage image;
if (convert_skia_bitmap_to_ximage(bitmap, image)) {
XPutImage(
x11display(), handle(), gc(), &image,
rc.x, rc.y,
rc.x, rc.y,
rc.w, rc.h);
}
}
else {
SkBitmap scaled;
if (scaled.tryAllocPixels(
SkImageInfo::Make(rc.w, rc.h,
bitmap.info().colorType(),
bitmap.info().alphaType()))) {
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
SkCanvas canvas(scaled);
SkRect srcRect = SkRect::Make(SkIRect::MakeXYWH(rc.x/scale, rc.y/scale, rc.w/scale, rc.h/scale));
SkRect dstRect = SkRect::Make(SkIRect::MakeXYWH(0, 0, rc.w, rc.h));
canvas.drawBitmapRect(bitmap, srcRect, dstRect, &paint,
SkCanvas::kStrict_SrcRectConstraint);
XImage image;
if (convert_skia_bitmap_to_ximage(scaled, image)) {
XPutImage(
x11display(), handle(), gc(), &image,
0, 0,
rc.x, rc.y,
rc.w, rc.h);
}
}
}
}
void SkiaWindow::captureMouse()
{
}
void SkiaWindow::releaseMouse()
{
}
void SkiaWindow::setMousePosition(const gfx::Point& position)
{
}
bool SkiaWindow::setNativeMouseCursor(NativeCursor cursor)
{
return false;
}
void SkiaWindow::updateWindow(const gfx::Rect& bounds)
{
}
void SkiaWindow::onExpose()
void SkiaWindow::resizeDisplay(const gfx::Size& sz)
{
m_display->resize(sz);
updateWindow(gfx::Rect(sz / scale()));
}
} // namespace she

View File

@ -20,7 +20,7 @@ namespace she {
class EventQueue;
class SkiaDisplay;
class SkiaWindow : public X11Window<SkiaWindow> {
class SkiaWindow : public X11Window {
public:
enum class Backend { NONE, GL };
@ -28,21 +28,11 @@ public:
int width, int height, int scale);
~SkiaWindow();
void queueEventImpl(Event& ev);
int scale() const;
void setScale(int scale);
void setVisible(bool visible);
void maximize();
bool isMaximized() const;
bool isMinimized() const;
gfx::Size clientSize() const;
gfx::Size restoredSize() const;
void captureMouse();
void releaseMouse();
void setMousePosition(const gfx::Point& position);
bool setNativeMouseCursor(NativeCursor cursor);
void updateWindow(const gfx::Rect& bounds);
std::string getLayout() { return ""; }
void setLayout(const std::string& layout) { }
@ -51,12 +41,12 @@ public:
}
private:
void onExpose() override;
void queueEvent(Event& ev) override;
void paintGC(const gfx::Rect& rc) override;
void resizeDisplay(const gfx::Size& sz) override;
EventQueue* m_queue;
SkiaDisplay* m_display;
gfx::Size m_clientSize;
int m_scale;
DISABLE_COPYING(SkiaWindow);
};

View File

@ -10,10 +10,57 @@
#include "she/x11/event_queue.h"
#include "she/x11/window.h"
#include <X11/Xlib.h>
namespace she {
namespace {
const char* get_event_name(XEvent& event)
{
switch (event.type) {
case KeyPress: return "KeyPress";
case KeyRelease: return "KeyRelease";
case ButtonPress: return "ButtonPress";
case ButtonRelease: return "ButtonRelease";
case MotionNotify: return "MotionNotify";
case EnterNotify: return "EnterNotify";
case LeaveNotify: return "LeaveNotify";
case FocusIn: return "FocusIn";
case FocusOut: return "FocusOut";
case KeymapNotify: return "KeymapNotify";
case Expose: return "Expose";
case GraphicsExpose: return "GraphicsExpose";
case NoExpose: return "NoExpose";
case VisibilityNotify: return "VisibilityNotify";
case CreateNotify: return "CreateNotify";
case DestroyNotify: return "DestroyNotify";
case UnmapNotify: return "UnmapNotify";
case MapNotify: return "MapNotify";
case MapRequest: return "MapRequest";
case ReparentNotify: return "ReparentNotify";
case ConfigureNotify: return "ConfigureNotify";
case ConfigureRequest: return "ConfigureRequest";
case GravityNotify: return "GravityNotify";
case ResizeRequest: return "ResizeRequest";
case CirculateNotify: return "CirculateNotify";
case CirculateRequest: return "CirculateRequest";
case PropertyNotify: return "PropertyNotify";
case SelectionClear: return "SelectionClear";
case SelectionRequest: return "SelectionRequest";
case SelectionNotify: return "SelectionNotify";
case ColormapNotify: return "ColormapNotify";
case ClientMessage: return "ClientMessage";
case MappingNotify: return "MappingNotify";
case GenericEvent: return "GenericEvent";
}
return "Unknown";
}
} // anonymous namespace
void X11EventQueue::getEvent(Event& ev, bool canWait)
{
::Display* display = X11::instance()->display();
@ -40,7 +87,12 @@ void X11EventQueue::getEvent(Event& ev, bool canWait)
void X11EventQueue::processX11Event(XEvent& event)
{
// TODO
TRACE("XEvent: %s (%d)\n", get_event_name(event), event.type);
X11Window* window = X11Window::getPointerFromHandle(event.xany.window);
ASSERT(window);
if (window)
window->processX11Event(event);
}
} // namespace she

229
src/she/x11/window.cpp Normal file
View File

@ -0,0 +1,229 @@
// SHE library
// Copyright (C) 2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "she/x11/window.h"
#include "gfx/rect.h"
#include "she/event.h"
#include <map>
namespace she {
namespace {
// See https://bugs.freedesktop.org/show_bug.cgi?id=12871 for more
// information, it looks like the official way to convert a X Window
// into our own user data pointer (X11Window instance) is using a map.
std::map<::Window, X11Window*> g_activeWindows;
KeyModifiers get_modifiers_from_xevent(int state)
{
int modifiers = kKeyNoneModifier;
if (state & ShiftMask) modifiers |= kKeyShiftModifier;
if (state & LockMask) modifiers |= kKeyAltModifier;
if (state & ControlMask) modifiers |= kKeyCtrlModifier;
return (KeyModifiers)modifiers;
}
} // anonymous namespace
// static
X11Window* X11Window::getPointerFromHandle(Window handle)
{
auto it = g_activeWindows.find(handle);
if (it != g_activeWindows.end())
return it->second;
else
return nullptr;
}
// static
void X11Window::addWindow(X11Window* window)
{
ASSERT(g_activeWindows.find(window->handle()) == g_activeWindows.end());
g_activeWindows[window->handle()] = window;
}
// static
void X11Window::removeWindow(X11Window* window)
{
auto it = g_activeWindows.find(window->handle());
ASSERT(it != g_activeWindows.end());
if (it != g_activeWindows.end()) {
ASSERT(it->second == window);
g_activeWindows.erase(it);
}
}
X11Window::X11Window(::Display* display, int width, int height, int scale)
: m_display(display)
, m_scale(scale)
, m_clientSize(1, 1)
{
::Window root = XDefaultRootWindow(m_display);
XSetWindowAttributes swa;
swa.event_mask = (StructureNotifyMask | ExposureMask | PropertyChangeMask |
EnterWindowMask | LeaveWindowMask | FocusChangeMask |
ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
KeyPressMask | KeyReleaseMask);
m_window = XCreateWindow(
m_display, root,
0, 0, width, height, 0,
CopyFromParent,
InputOutput,
CopyFromParent,
CWEventMask,
&swa);
XMapWindow(m_display, m_window);
m_gc = XCreateGC(m_display, m_window, 0, nullptr);
X11Window::addWindow(this);
}
X11Window::~X11Window()
{
XFreeGC(m_display, m_gc);
XDestroyWindow(m_display, m_window);
X11Window::removeWindow(this);
}
void X11Window::setScale(const int scale)
{
m_scale = scale;
resizeDisplay(m_clientSize);
}
void X11Window::setTitle(const std::string& title)
{
XTextProperty prop;
prop.value = (unsigned char*)title.c_str();
prop.encoding = XA_STRING;
prop.format = 8;
prop.nitems = std::strlen((char*)title.c_str());
XSetWMName(m_display, m_window, &prop);
}
void X11Window::captureMouse()
{
}
void X11Window::releaseMouse()
{
}
void X11Window::setMousePosition(const gfx::Point& position)
{
}
void X11Window::updateWindow(const gfx::Rect& unscaledBounds)
{
XEvent ev;
memset(&ev, 0, sizeof(ev));
ev.xexpose.type = Expose;
ev.xexpose.display = x11display();
ev.xexpose.window = handle();
ev.xexpose.x = unscaledBounds.x*m_scale;
ev.xexpose.y = unscaledBounds.y*m_scale;
ev.xexpose.width = unscaledBounds.w*m_scale;
ev.xexpose.height = unscaledBounds.h*m_scale;
XSendEvent(m_display, m_window, False,
ExposureMask, (XEvent*)&ev);
}
bool X11Window::setNativeMouseCursor(NativeCursor cursor)
{
return false;
}
bool X11Window::setNativeMouseCursor(const she::Surface* surface,
const gfx::Point& focus,
const int scale)
{
return false;
}
void X11Window::processX11Event(XEvent& event)
{
switch (event.type) {
case ConfigureNotify: {
gfx::Size newSize(event.xconfigure.width,
event.xconfigure.height);
if (newSize.w > 0 &&
newSize.h > 0 &&
m_clientSize != newSize) {
m_clientSize = newSize;
resizeDisplay(m_clientSize);
}
break;
}
case Expose: {
gfx::Rect rc(event.xexpose.x, event.xexpose.y,
event.xexpose.width, event.xexpose.height);
paintGC(rc);
break;
}
case ButtonPress: {
Event ev;
ev.setType(Event::MouseDown);
ev.setModifiers(get_modifiers_from_xevent(event.xbutton.state));
ev.setPosition(gfx::Point(event.xbutton.x / m_scale,
event.xbutton.y / m_scale));
ev.setButton(
event.xbutton.button == 1 ? Event::LeftButton:
event.xbutton.button == 2 ? Event::MiddleButton:
event.xbutton.button == 3 ? Event::RightButton:
event.xbutton.button == 4 ? Event::X1Button:
event.xbutton.button == 5 ? Event::X2Button: Event::NoneButton);
queueEvent(ev);
break;
}
case ButtonRelease: {
Event ev;
ev.setType(Event::MouseUp);
ev.setModifiers(get_modifiers_from_xevent(event.xbutton.state));
ev.setPosition(gfx::Point(event.xbutton.x / m_scale,
event.xbutton.y / m_scale));
ev.setButton(
event.xbutton.button == 1 ? Event::LeftButton:
event.xbutton.button == 2 ? Event::MiddleButton:
event.xbutton.button == 3 ? Event::RightButton:
event.xbutton.button == 4 ? Event::X1Button:
event.xbutton.button == 5 ? Event::X2Button: Event::NoneButton);
queueEvent(ev);
break;
}
case MotionNotify: {
Event ev;
ev.setType(Event::MouseMove);
ev.setModifiers(get_modifiers_from_xevent(event.xmotion.state));
ev.setPosition(gfx::Point(event.xmotion.x / m_scale,
event.xmotion.y / m_scale));
queueEvent(ev);
break;
}
}
}
} // namespace she

View File

@ -8,72 +8,64 @@
#define SHE_X11_WINDOW_INCLUDED
#pragma once
#include "gfx/fwd.h"
#include "gfx/size.h"
#include "she/native_cursor.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <cstring>
#include <string>
namespace she {
class Event;
class Surface;
template<typename T>
class X11Window {
public:
X11Window(::Display* display, int width, int height)
: m_display(display) {
::Window root = XDefaultRootWindow(m_display);
X11Window(::Display* display, int width, int height, int scale);
~X11Window();
XSetWindowAttributes swa;
swa.event_mask = (StructureNotifyMask | ExposureMask | PropertyChangeMask |
EnterWindowMask | LeaveWindowMask | FocusChangeMask |
ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
KeyPressMask | KeyReleaseMask);
int scale() const { return m_scale; }
void setScale(const int scale);
m_window = XCreateWindow(
m_display, root,
0, 0, width, height, 0,
CopyFromParent,
InputOutput,
CopyFromParent,
CWEventMask,
&swa);
void setTitle(const std::string& title);
XMapWindow(m_display, m_window);
XSelectInput(m_display, m_window, StructureNotifyMask);
gfx::Size clientSize() const { return m_clientSize; }
gfx::Size restoredSize() const { return m_clientSize; }
void captureMouse();
void releaseMouse();
void setMousePosition(const gfx::Point& position);
void updateWindow(const gfx::Rect& bounds);
bool setNativeMouseCursor(NativeCursor cursor);
bool setNativeMouseCursor(const she::Surface* surface,
const gfx::Point& focus,
const int scale);
m_gc = XCreateGC(m_display, m_window, 0, nullptr);
}
~X11Window() {
XFreeGC(m_display, m_gc);
XDestroyWindow(m_display, m_window);
}
void queueEvent(Event& ev) {
static_cast<T*>(this)->queueEventImpl(ev);
}
void setTitle(const std::string& title) {
XTextProperty prop;
prop.value = (unsigned char*)title.c_str();
prop.encoding = XA_STRING;
prop.format = 8;
prop.nitems = std::strlen((char*)title.c_str());
XSetWMName(m_display, m_window, &prop);
}
virtual void onExpose() = 0;
::Display* display() const { return m_display; }
::Display* x11display() const { return m_display; }
::Window handle() const { return m_window; }
::GC gc() const { return m_gc; }
void processX11Event(XEvent& event);
static X11Window* getPointerFromHandle(Window handle);
protected:
virtual void queueEvent(Event& event) = 0;
virtual void paintGC(const gfx::Rect& rc) = 0;
virtual void resizeDisplay(const gfx::Size& sz) = 0;
private:
static void addWindow(X11Window* window);
static void removeWindow(X11Window* window);
::Display* m_display;
::Window m_window;
::GC m_gc;
int m_scale;
gfx::Size m_clientSize;
};
} // namespace she