diff --git a/src/she/CMakeLists.txt b/src/she/CMakeLists.txt index 5c369a80e..77d1b7a06 100644 --- a/src/she/CMakeLists.txt +++ b/src/she/CMakeLists.txt @@ -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() diff --git a/src/she/skia/skia_display.cpp b/src/she/skia/skia_display.cpp index 5032d4de9..9bb1f130c 100644 --- a/src/she/skia/skia_display.cpp +++ b/src/she/skia/skia_display.cpp @@ -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) diff --git a/src/she/skia/skia_system.h b/src/she/skia/skia_system.h index 5e0007474..947659127 100644 --- a/src/she/skia/skia_system.h +++ b/src/she/skia/skia_system.h @@ -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) diff --git a/src/she/skia/skia_window_x11.cpp b/src/she/skia/skia_window_x11.cpp index 576241e2e..3f5396099 100644 --- a/src/she/skia/skia_window_x11.cpp +++ b/src/she/skia/skia_window_x11.cpp @@ -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(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 diff --git a/src/she/skia/skia_window_x11.h b/src/she/skia/skia_window_x11.h index c962632ba..f5fc6710d 100644 --- a/src/she/skia/skia_window_x11.h +++ b/src/she/skia/skia_window_x11.h @@ -20,7 +20,7 @@ namespace she { class EventQueue; class SkiaDisplay; -class SkiaWindow : public X11Window { +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); }; diff --git a/src/she/x11/event_queue.cpp b/src/she/x11/event_queue.cpp index d67e7319b..af67a3e0c 100644 --- a/src/she/x11/event_queue.cpp +++ b/src/she/x11/event_queue.cpp @@ -10,10 +10,57 @@ #include "she/x11/event_queue.h" +#include "she/x11/window.h" + #include 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 diff --git a/src/she/x11/window.cpp b/src/she/x11/window.cpp new file mode 100644 index 000000000..26f9115c2 --- /dev/null +++ b/src/she/x11/window.cpp @@ -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 + +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 diff --git a/src/she/x11/window.h b/src/she/x11/window.h index b5c530bff..cb954f98e 100644 --- a/src/she/x11/window.h +++ b/src/she/x11/window.h @@ -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 #include #include #include +#include namespace she { class Event; +class Surface; -template 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(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