202 lines
4.6 KiB
C++
202 lines
4.6 KiB
C++
|
|
#include <Spectre/Display/DisplayDescription.h> // Prevents conflict with X.h defining "None"
|
|
#include <Spectre/System/Log.h>
|
|
#include <Spectre/Graphics/Image.h>
|
|
#include "X11WindowEventHandler.h"
|
|
#include "X11SharedDisplay.h"
|
|
#include "GLXContext.h"
|
|
#include "X11Display.h"
|
|
|
|
namespace sp {
|
|
|
|
X11Display::
|
|
X11Display() :
|
|
m_screen (0),
|
|
m_disp (NULL),
|
|
m_size (200,200),
|
|
m_cur_last (0),
|
|
m_cur_hidden (0)
|
|
{
|
|
}
|
|
|
|
bool X11Display::create(DisplayDescription description)
|
|
{
|
|
XSetWindowAttributes attr;
|
|
XVisualInfo* vi;
|
|
Atom protocols;
|
|
Visual* visual;
|
|
Window root_win;
|
|
|
|
m_disp = XGetDisplay();
|
|
if (m_disp == NULL) {
|
|
Log::warn("X11: Could not open display");
|
|
return false;
|
|
}
|
|
|
|
m_screen = DefaultScreen(m_disp);
|
|
root_win = XRootWindow(m_disp, m_screen);
|
|
visual = DefaultVisual(m_disp, m_screen);
|
|
|
|
attr.border_pixel = BlackPixel(m_disp, m_screen);
|
|
attr.background_pixel = WhitePixel(m_disp, m_screen);
|
|
//attr.override_redirect = True;
|
|
attr.colormap = ::XCreateColormap(m_disp, root_win, visual, AllocNone);
|
|
// We want Keyboard,Mouse,Resize,Exposure (repaint) events.
|
|
attr.event_mask = KeyPressMask | KeyReleaseMask
|
|
| ButtonPressMask | ButtonReleaseMask | PointerMotionMask
|
|
| StructureNotifyMask | ExposureMask;
|
|
|
|
m_win = XCreateWindow(m_disp, root_win,
|
|
0, 0,
|
|
m_size.x, m_size.y, 0, DefaultDepth(m_disp, m_screen),
|
|
InputOutput, visual,
|
|
CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, &attr);
|
|
|
|
// Register event handler
|
|
X11WindowEventHandler::registerHandler(m_disp, m_win, this);
|
|
|
|
// X11 does not handle pressing the X button on a window.
|
|
// that is the job of the window manager.
|
|
// Here we can request the WM to send us a delete event so we can handle it ourself.
|
|
protocols = getAtom("WM_DELETE_WINDOW");
|
|
XSetWMProtocols(m_disp, m_win, &protocols, 1);
|
|
|
|
XMapWindow(m_disp, m_win);
|
|
|
|
setSize(description.mode.width, description.mode.height);
|
|
|
|
// Clear and take focus
|
|
XClearWindow(m_disp, m_win);
|
|
XMapRaised(m_disp, m_win);
|
|
|
|
Log::info("X11: Created display");
|
|
|
|
createHiddenCursor();
|
|
|
|
return true;
|
|
}
|
|
|
|
void X11Display::destroy()
|
|
{
|
|
if (m_win) {
|
|
X11WindowEventHandler::unregisterHandler(m_disp, m_win);
|
|
::XDestroyWindow(m_disp, m_win);
|
|
}
|
|
|
|
if (m_disp) {
|
|
XReleaseDisplay(m_disp);
|
|
m_disp = NULL;
|
|
}
|
|
}
|
|
|
|
bool X11Display::isValid()
|
|
{
|
|
return m_disp && m_win;
|
|
}
|
|
|
|
void* X11Display::getHandle() const
|
|
{
|
|
return (void*) m_win;
|
|
}
|
|
|
|
void X11Display::setSize(unsigned int width, unsigned int height)
|
|
{
|
|
m_size = Vector2u(width, height);
|
|
|
|
::XResizeWindow(m_disp, m_win, m_size.x, m_size.y);
|
|
}
|
|
|
|
Vector2u X11Display::getSize() const
|
|
{
|
|
return m_size;
|
|
}
|
|
|
|
void X11Display::setPosition(unsigned int x, unsigned int y)
|
|
{
|
|
::XMoveWindow(m_disp, m_win, x, y);
|
|
}
|
|
|
|
void X11Display::setCaption(const std::string& caption)
|
|
{
|
|
::XStoreName(m_disp, m_win, caption.c_str());
|
|
}
|
|
|
|
// TODO: Move this up to the non-platform layer.
|
|
void X11Display::setIcon(const std::string& icon)
|
|
{
|
|
Image img;
|
|
|
|
if (img.loadFromFile(icon)) {
|
|
setIcon(img.getWidth(), img.getHeight(), img.getPixels());
|
|
}
|
|
}
|
|
|
|
void X11Display::setIcon(unsigned int width, unsigned int height, const uint8_t *pixels)
|
|
{
|
|
::Atom net_wm_icon = getAtom("_NET_WM_ICON", False);
|
|
::Atom cardinal = getAtom("CARDINAL", False);
|
|
std::vector<uint64_t> buffer(2 + width * height);
|
|
uint64_t *ptr = &buffer[0];
|
|
|
|
*ptr++ = width;
|
|
*ptr++ = height;
|
|
|
|
// TODO: Conversion between differnet formats should be defined as functions in Graphics/PixelFormat.h
|
|
for (std::size_t i = 0; i < width * height; i++) {
|
|
*ptr++ = (pixels[i * 4 + 2] << 0)
|
|
| (pixels[i * 4 + 1] << 8)
|
|
| (pixels[i * 4 + 0] << 16)
|
|
| (pixels[i * 4 + 3] << 24);
|
|
}
|
|
|
|
::XChangeProperty(m_disp, m_win,
|
|
net_wm_icon, cardinal, 32,
|
|
PropModeReplace,
|
|
reinterpret_cast<const unsigned char*>(&buffer[0]),
|
|
2 + width * height);
|
|
|
|
XFlush(m_disp);
|
|
}
|
|
|
|
void X11Display::createHiddenCursor()
|
|
{
|
|
XColor c;
|
|
Pixmap pix = ::XCreatePixmap(m_disp, m_win, 1, 1, 1);
|
|
GC gc = ::XCreateGC(m_disp, pix, 0, NULL);
|
|
|
|
// Draw transparent pixel.
|
|
::XDrawPoint(m_disp, pix, gc, 0, 0);
|
|
|
|
c.red = c.green = c.blue = 0;
|
|
c.flags = DoRed | DoGreen | DoBlue;
|
|
m_cur_hidden = XCreatePixmapCursor(m_disp, pix, pix, &c, &c, 0, 0);
|
|
|
|
// Free GC and pixmap.
|
|
::XFreePixmap(m_disp, pix);
|
|
::XFreeGC(m_disp, gc);
|
|
}
|
|
|
|
void X11Display::showCursor(bool value)
|
|
{
|
|
XDefineCursor(m_disp, m_win, value ? m_cur_last : m_cur_hidden);
|
|
}
|
|
|
|
void X11Display::processEvent(const ::XEvent& event)
|
|
{
|
|
Vector2u size;
|
|
|
|
switch(event.xany.type) {
|
|
case ConfigureNotify:
|
|
size.x = event.xconfigure.width;
|
|
size.y = event.xconfigure.height;
|
|
|
|
if (m_size != size) {
|
|
m_size = size;
|
|
Log::info("X11: Resize event");
|
|
onReshape(size.x, size.y);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace sp
|