2015-05-06 00:37:39 +03:00
|
|
|
//
|
|
|
|
// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
//
|
|
|
|
|
|
|
|
// X11Window.cpp: Implementation of OSWindow for X11
|
|
|
|
|
|
|
|
#include "x11/X11Window.h"
|
|
|
|
|
2015-05-14 18:44:53 +03:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
Bool WaitForMapNotify(Display *dpy, XEvent *event, XPointer window)
|
|
|
|
{
|
|
|
|
return event->type == MapNotify && event->xmap.window == reinterpret_cast<Window>(window);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:37:39 +03:00
|
|
|
X11Window::X11Window()
|
2015-05-12 17:37:01 +03:00
|
|
|
: WM_DELETE_WINDOW(None),
|
|
|
|
mDisplay(nullptr),
|
2015-05-06 00:37:39 +03:00
|
|
|
mWindow(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
X11Window::~X11Window()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool X11Window::initialize(const std::string &name, size_t width, size_t height)
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
|
|
|
|
mDisplay = XOpenDisplay(NULL);
|
|
|
|
if (!mDisplay)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-05-12 17:37:01 +03:00
|
|
|
{
|
|
|
|
int screen = DefaultScreen(mDisplay);
|
|
|
|
Window root = RootWindow(mDisplay, screen);
|
|
|
|
|
|
|
|
Colormap colormap = XCreateColormap(mDisplay, root, DefaultVisual(mDisplay, screen), AllocNone);
|
|
|
|
int depth = DefaultDepth(mDisplay, screen);
|
|
|
|
Visual *visual = DefaultVisual(mDisplay, screen);
|
2015-05-06 00:37:39 +03:00
|
|
|
|
2015-05-12 17:37:01 +03:00
|
|
|
XSetWindowAttributes attributes;
|
|
|
|
unsigned long attributeMask = CWBorderPixel | CWColormap | CWEventMask;
|
2015-05-06 00:37:39 +03:00
|
|
|
|
2015-05-12 17:37:01 +03:00
|
|
|
attributes.event_mask = StructureNotifyMask | PointerMotionMask | ButtonPressMask |
|
|
|
|
ButtonReleaseMask | FocusChangeMask | EnterWindowMask |
|
|
|
|
LeaveWindowMask;
|
|
|
|
attributes.border_pixel = 0;
|
|
|
|
attributes.colormap = colormap;
|
2015-05-06 00:37:39 +03:00
|
|
|
|
2015-05-12 17:37:01 +03:00
|
|
|
mWindow = XCreateWindow(mDisplay, root, 0, 0, width, height, 0, depth, InputOutput,
|
|
|
|
visual, attributeMask, &attributes);
|
|
|
|
XFreeColormap(mDisplay, colormap);
|
|
|
|
}
|
2015-05-06 00:37:39 +03:00
|
|
|
|
|
|
|
if (!mWindow)
|
|
|
|
{
|
2015-05-12 17:37:01 +03:00
|
|
|
destroy();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tell the window manager to notify us when the user wants to close the
|
|
|
|
// window so we can do it ourselves.
|
|
|
|
WM_DELETE_WINDOW = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False);
|
|
|
|
WM_PROTOCOLS = XInternAtom(mDisplay, "WM_PROTOCOLS", False);
|
|
|
|
if (WM_DELETE_WINDOW == None || WM_PROTOCOLS == None)
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(XSetWMProtocols(mDisplay, mWindow, &WM_DELETE_WINDOW, 1) == 0)
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create an atom to identify our test event
|
|
|
|
TEST_EVENT = XInternAtom(mDisplay, "ANGLE_TEST_EVENT", False);
|
|
|
|
if (TEST_EVENT == None)
|
|
|
|
{
|
|
|
|
destroy();
|
2015-05-06 00:37:39 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
XFlush(mDisplay);
|
|
|
|
|
|
|
|
mX = 0;
|
|
|
|
mY = 0;
|
|
|
|
mWidth = width;
|
|
|
|
mHeight = height;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void X11Window::destroy()
|
|
|
|
{
|
|
|
|
if (mWindow)
|
|
|
|
{
|
|
|
|
XDestroyWindow(mDisplay, mWindow);
|
|
|
|
mWindow = 0;
|
|
|
|
}
|
|
|
|
if (mDisplay)
|
|
|
|
{
|
|
|
|
XCloseDisplay(mDisplay);
|
|
|
|
mDisplay = nullptr;
|
|
|
|
}
|
2015-05-12 17:37:01 +03:00
|
|
|
WM_DELETE_WINDOW = None;
|
|
|
|
WM_PROTOCOLS = None;
|
2015-05-06 00:37:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
EGLNativeWindowType X11Window::getNativeWindow() const
|
|
|
|
{
|
|
|
|
return mWindow;
|
|
|
|
}
|
|
|
|
|
|
|
|
EGLNativeDisplayType X11Window::getNativeDisplay() const
|
|
|
|
{
|
|
|
|
return mDisplay;
|
|
|
|
}
|
|
|
|
|
|
|
|
void X11Window::messageLoop()
|
|
|
|
{
|
2015-05-12 17:37:01 +03:00
|
|
|
int eventCount = XPending(mDisplay);
|
|
|
|
while (eventCount--)
|
|
|
|
{
|
|
|
|
XEvent event;
|
|
|
|
XNextEvent(mDisplay, &event);
|
|
|
|
processEvent(event);
|
|
|
|
}
|
2015-05-06 00:37:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void X11Window::setMousePosition(int x, int y)
|
|
|
|
{
|
2015-05-12 17:37:01 +03:00
|
|
|
XWarpPointer(mDisplay, None, mWindow, 0, 0, 0, 0, x, y);
|
2015-05-06 00:37:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
OSWindow *CreateOSWindow()
|
|
|
|
{
|
|
|
|
return new X11Window();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool X11Window::setPosition(int x, int y)
|
|
|
|
{
|
2015-05-12 17:37:01 +03:00
|
|
|
XMoveWindow(mDisplay, mWindow, x, y);
|
|
|
|
XFlush(mDisplay);
|
2015-05-06 00:37:39 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool X11Window::resize(int width, int height)
|
|
|
|
{
|
2015-05-12 17:37:01 +03:00
|
|
|
XResizeWindow(mDisplay, mWindow, width, height);
|
|
|
|
XFlush(mDisplay);
|
2015-05-06 00:37:39 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void X11Window::setVisible(bool isVisible)
|
|
|
|
{
|
|
|
|
if (isVisible)
|
|
|
|
{
|
|
|
|
XMapWindow(mDisplay, mWindow);
|
2015-05-14 18:44:53 +03:00
|
|
|
|
|
|
|
// Wait until we get an event saying this window is mapped so that the
|
|
|
|
// code calling setVisible can assume the window is visible.
|
|
|
|
// This is important when creating a framebuffer as the framebuffer content
|
|
|
|
// is undefined when the window is not visible.
|
|
|
|
XEvent dummyEvent;
|
|
|
|
XIfEvent(mDisplay, &dummyEvent, WaitForMapNotify, reinterpret_cast<XPointer>(mWindow));
|
2015-05-06 00:37:39 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
XUnmapWindow(mDisplay, mWindow);
|
2015-05-14 18:44:53 +03:00
|
|
|
XFlush(mDisplay);
|
2015-05-06 00:37:39 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-12 17:37:01 +03:00
|
|
|
void X11Window::signalTestEvent()
|
2015-05-06 00:37:39 +03:00
|
|
|
{
|
2015-05-12 17:37:01 +03:00
|
|
|
XEvent event;
|
|
|
|
event.type = ClientMessage;
|
|
|
|
event.xclient.message_type = TEST_EVENT;
|
|
|
|
// Format needs to be valid or a BadValue is generated
|
|
|
|
event.xclient.format = 32;
|
|
|
|
|
|
|
|
// Hijack StructureNotifyMask as we know we will be listening for it.
|
|
|
|
XSendEvent(mDisplay, mWindow, False, StructureNotifyMask, &event);
|
2015-05-06 00:37:39 +03:00
|
|
|
}
|
|
|
|
|
2015-05-12 17:37:01 +03:00
|
|
|
void X11Window::processEvent(const XEvent &xEvent)
|
2015-05-06 00:37:39 +03:00
|
|
|
{
|
2015-05-12 17:37:01 +03:00
|
|
|
// TODO(cwallez) handle key presses and text events
|
|
|
|
switch (xEvent.type)
|
|
|
|
{
|
|
|
|
case ButtonPress:
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
MouseButton button = MOUSEBUTTON_UNKNOWN;
|
|
|
|
int wheelX = 0;
|
|
|
|
int wheelY = 0;
|
|
|
|
|
|
|
|
// The mouse wheel updates are sent via button events.
|
|
|
|
switch (xEvent.xbutton.button)
|
|
|
|
{
|
|
|
|
case Button4:
|
|
|
|
wheelY = 1;
|
|
|
|
break;
|
|
|
|
case Button5:
|
|
|
|
wheelY = -1;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
wheelX = 1;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
wheelX = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Button1:
|
|
|
|
button = MOUSEBUTTON_LEFT;
|
|
|
|
break;
|
|
|
|
case Button2:
|
|
|
|
button = MOUSEBUTTON_MIDDLE;
|
|
|
|
break;
|
|
|
|
case Button3:
|
|
|
|
button = MOUSEBUTTON_RIGHT;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
button = MOUSEBUTTON_BUTTON4;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
button = MOUSEBUTTON_BUTTON5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wheelY != 0)
|
|
|
|
{
|
|
|
|
event.Type = Event::EVENT_MOUSE_WHEEL_MOVED;
|
|
|
|
event.MouseWheel.Delta = wheelY;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (button != MOUSEBUTTON_UNKNOWN)
|
|
|
|
{
|
|
|
|
event.Type = Event::EVENT_MOUSE_BUTTON_RELEASED;
|
|
|
|
event.MouseButton.Button = button;
|
|
|
|
event.MouseButton.X = xEvent.xbutton.x;
|
|
|
|
event.MouseButton.Y = xEvent.xbutton.y;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ButtonRelease:
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
MouseButton button = MOUSEBUTTON_UNKNOWN;
|
|
|
|
|
|
|
|
switch (xEvent.xbutton.button)
|
|
|
|
{
|
|
|
|
case Button1:
|
|
|
|
button = MOUSEBUTTON_LEFT;
|
|
|
|
break;
|
|
|
|
case Button2:
|
|
|
|
button = MOUSEBUTTON_MIDDLE;
|
|
|
|
break;
|
|
|
|
case Button3:
|
|
|
|
button = MOUSEBUTTON_RIGHT;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
button = MOUSEBUTTON_BUTTON4;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
button = MOUSEBUTTON_BUTTON5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (button != MOUSEBUTTON_UNKNOWN)
|
|
|
|
{
|
|
|
|
event.Type = Event::EVENT_MOUSE_BUTTON_RELEASED;
|
|
|
|
event.MouseButton.Button = button;
|
|
|
|
event.MouseButton.X = xEvent.xbutton.x;
|
|
|
|
event.MouseButton.Y = xEvent.xbutton.y;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EnterNotify:
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
event.Type = Event::EVENT_MOUSE_ENTERED;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LeaveNotify:
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
event.Type = Event::EVENT_MOUSE_LEFT;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MotionNotify:
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
event.Type = Event::EVENT_MOUSE_MOVED;
|
|
|
|
event.MouseMove.X = xEvent.xmotion.x;
|
|
|
|
event.MouseMove.Y = xEvent.xmotion.y;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ConfigureNotify:
|
|
|
|
{
|
|
|
|
if (xEvent.xconfigure.width != mWidth || xEvent.xconfigure.height != mHeight)
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
event.Type = Event::EVENT_RESIZED;
|
|
|
|
event.Size.Width = xEvent.xconfigure.width;
|
|
|
|
event.Size.Height = xEvent.xconfigure.height;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
if (xEvent.xconfigure.x != mX || xEvent.xconfigure.y != mY)
|
|
|
|
{
|
2015-05-22 01:02:02 +03:00
|
|
|
// Sometimes, the window manager reparents our window (for example
|
|
|
|
// when resizing) then the X and Y coordinates will be with respect to
|
|
|
|
// the new parent and not what the user wants to know. Use
|
|
|
|
// XTranslateCoordinates to get the coordinates on the screen.
|
|
|
|
int screen = DefaultScreen(mDisplay);
|
|
|
|
Window root = RootWindow(mDisplay, screen);
|
|
|
|
|
|
|
|
int x, y;
|
|
|
|
Window child;
|
|
|
|
XTranslateCoordinates(mDisplay, mWindow, root, 0, 0, &x, &y, &child);
|
|
|
|
|
|
|
|
if (x != mX || y != mY)
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
event.Type = Event::EVENT_MOVED;
|
|
|
|
event.Move.X = x;
|
|
|
|
event.Move.Y = y;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
2015-05-12 17:37:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FocusIn:
|
|
|
|
if (xEvent.xfocus.mode == NotifyNormal || xEvent.xfocus.mode == NotifyWhileGrabbed)
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
event.Type = Event::EVENT_GAINED_FOCUS;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FocusOut:
|
|
|
|
if (xEvent.xfocus.mode == NotifyNormal || xEvent.xfocus.mode == NotifyWhileGrabbed)
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
event.Type = Event::EVENT_LOST_FOCUS;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DestroyNotify:
|
|
|
|
// We already received WM_DELETE_WINDOW
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ClientMessage:
|
|
|
|
if (xEvent.xclient.message_type == WM_PROTOCOLS &&
|
|
|
|
static_cast<Atom>(xEvent.xclient.data.l[0]) == WM_DELETE_WINDOW)
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
event.Type = Event::EVENT_CLOSED;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
else if (xEvent.xclient.message_type == TEST_EVENT)
|
|
|
|
{
|
|
|
|
Event event;
|
|
|
|
event.Type = Event::EVENT_TEST;
|
|
|
|
pushEvent(event);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2015-05-06 00:37:39 +03:00
|
|
|
}
|