#include // Prevents conflict with X.h defining "None" #include #include #include "X11WindowEventHandler.h" #include "X11SharedDisplay.h" #include "GLXContext.h" #include "X11Display.h" // Sometimes not defined in headers. #ifndef _NET_WM_STATE_TOGGLE #define _NET_WM_STATE_TOGGLE 2 #endif namespace sp { namespace _priv { // Pointer to the display that has focus (or NULL if none have). X11Display* focused_display = NULL; } X11Display* X11Display::getFocused() { return _priv::focused_display; } 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 InputFocus,Keyboard,Mouse,Resize,Exposure (repaint) events. attr.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | ExposureMask; m_win = ::XCreateWindow(m_disp, root_win, 0, 0, /* Position */ m_size.x, m_size.y, 0 /* Border width */, 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); setVisible(true); 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_disp) { if (m_win) { X11WindowEventHandler::unregisterHandler(m_disp, m_win); ::XDestroyWindow(m_disp, m_win); } 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) { // X11 does not like if width or height is zero. if (width == 0) { width = 1; } if (height == 0) { height = 1; } 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); } Vector2u X11Display::getPosition() const { Vector2u pos(0, 0); XWindowAttributes attr; if (XGetWindowAttributes(m_disp, m_win, &attr)) { pos.x = attr.x; pos.y = attr.y; } return pos; } void X11Display::setVisible(bool visible) { if (visible) { ::XMapWindow(m_disp, m_win); } else { ::XUnmapWindow(m_disp, m_win); } } void X11Display::minimize() { ::XIconifyWindow(m_disp, m_win, m_screen); } void X11Display::maximize() { ::XClientMessageEvent ev = {}; ev.type = ClientMessage; ev.window = m_win; ev.message_type = getAtom("_NET_WM_STATE"); ev.format = 32; ev.data.l[0] = _NET_WM_STATE_TOGGLE; ev.data.l[1] = getAtom("_NET_WM_STATE_MAXIMIZED_HORZ"); ev.data.l[2] = getAtom("_NET_WM_STATE_MAXIMIZED_VERT"); ev.data.l[3] = 1; ::XSendEvent(m_disp, DefaultRootWindow(m_disp), False, SubstructureNotifyMask, (XEvent*) &ev); } 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 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(&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::grabCursor(bool value) { // TODO (this is abit harder on X11 than windows.) } 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; case MotionNotify : // Generates to much events to log :) break; case FocusIn: Log::debug("X11: FocusIn"); _priv::focused_display = this; break; case FocusOut: Log::debug("X11: FocusOut"); _priv::focused_display = NULL; break; } } } // namespace sp