mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-02 22:21:21 +00:00
286 lines
7.4 KiB
C++
286 lines
7.4 KiB
C++
// 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 {
|
|
|
|
// Event generated by the window manager when the close button on the
|
|
// window is pressed by the userh.
|
|
Atom wmDeleteMessage = 0;
|
|
|
|
// 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_x(int state)
|
|
{
|
|
int modifiers = kKeyNoneModifier;
|
|
if (state & ShiftMask) modifiers |= kKeyShiftModifier;
|
|
if (state & LockMask) modifiers |= kKeyAltModifier;
|
|
if (state & ControlMask) modifiers |= kKeyCtrlModifier;
|
|
return (KeyModifiers)modifiers;
|
|
}
|
|
|
|
bool is_mouse_wheel_button(int button)
|
|
{
|
|
return (button == Button4 || button == Button5 ||
|
|
button == 6 || button == 7);
|
|
}
|
|
|
|
gfx::Point get_mouse_wheel_delta(int button)
|
|
{
|
|
gfx::Point delta(0, 0);
|
|
switch (button) {
|
|
// Vertical wheel
|
|
case Button4: delta.y = -1; break;
|
|
case Button5: delta.y = +1; break;
|
|
// Horizontal wheel
|
|
case 6: delta.x = -1; break;
|
|
case 7: delta.x = +1; break;
|
|
}
|
|
return delta;
|
|
}
|
|
|
|
Event::MouseButton get_mouse_button_from_x(int button)
|
|
{
|
|
switch (button) {
|
|
case Button1: TRACE("LeftButton\n"); return Event::LeftButton;
|
|
case Button2: TRACE("MiddleButton\n"); return Event::MiddleButton;
|
|
case Button3: TRACE("RightButton\n"); return Event::RightButton;
|
|
case 8: TRACE("X1Button\n"); return Event::X1Button;
|
|
case 9: TRACE("X2Button\n"); return Event::X2Button;
|
|
}
|
|
TRACE("Unknown Button %d\n", button);
|
|
return Event::NoneButton;
|
|
}
|
|
|
|
} // 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)
|
|
{
|
|
// Initialize special messages (just the first time a X11Window is
|
|
// created)
|
|
if (!wmDeleteMessage)
|
|
wmDeleteMessage = XInternAtom(m_display, "WM_DELETE_WINDOW", False);
|
|
|
|
::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);
|
|
XSetWMProtocols(m_display, m_window, &wmDeleteMessage, 1);
|
|
|
|
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:
|
|
case ButtonRelease: {
|
|
Event ev;
|
|
|
|
if (is_mouse_wheel_button(event.xbutton.button)) {
|
|
ev.setType(Event::MouseWheel);
|
|
ev.setWheelDelta(get_mouse_wheel_delta(event.xbutton.button));
|
|
}
|
|
else {
|
|
ev.setType(event.type == ButtonPress? Event::MouseDown:
|
|
Event::MouseUp);
|
|
ev.setButton(get_mouse_button_from_x(event.xbutton.button));
|
|
}
|
|
ev.setModifiers(get_modifiers_from_x(event.xbutton.state));
|
|
ev.setPosition(gfx::Point(event.xbutton.x / m_scale,
|
|
event.xbutton.y / m_scale));
|
|
|
|
queueEvent(ev);
|
|
break;
|
|
}
|
|
|
|
case MotionNotify: {
|
|
Event ev;
|
|
ev.setType(Event::MouseMove);
|
|
ev.setModifiers(get_modifiers_from_x(event.xmotion.state));
|
|
ev.setPosition(gfx::Point(event.xmotion.x / m_scale,
|
|
event.xmotion.y / m_scale));
|
|
queueEvent(ev);
|
|
break;
|
|
}
|
|
|
|
case EnterNotify:
|
|
case LeaveNotify:
|
|
// "mode" can be NotifyGrab or NotifyUngrab when middle mouse
|
|
// button is pressed/released. We must not generated
|
|
// MouseEnter/Leave events on those cases, only on NotifyNormal
|
|
// (when mouse leaves/enter the X11 window).
|
|
if (event.xcrossing.mode == NotifyNormal) {
|
|
Event ev;
|
|
ev.setType(event.type == EnterNotify ? Event::MouseEnter:
|
|
Event::MouseLeave);
|
|
ev.setModifiers(get_modifiers_from_x(event.xcrossing.state));
|
|
ev.setPosition(gfx::Point(event.xcrossing.x / m_scale,
|
|
event.xcrossing.y / m_scale));
|
|
queueEvent(ev);
|
|
}
|
|
break;
|
|
|
|
case ClientMessage:
|
|
// When the close button is pressed
|
|
if (Atom(event.xclient.data.l[0]) == wmDeleteMessage) {
|
|
Event ev;
|
|
ev.setType(Event::CloseDisplay);
|
|
queueEvent(ev);
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
} // namespace she
|