| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "BsUnixWindow.h"
- #include "BsUnixPlatform.h"
- #include "Math/BsRect2I.h"
- #include <X11/Xlib.h>
- #include <X11/Xutil.h>
- #include <X11/Xatom.h>
- #include <X11/extensions/Xrandr.h>
- #define _NET_WM_STATE_REMOVE 0
- #define _NET_WM_STATE_ADD 1
- #define _NET_WM_STATE_TOGGLE 2
- #define WM_NormalState 1
- #define WM_IconicState 3
- namespace bs
- {
- enum class WindowState
- {
- Minimized,
- Maximized,
- Normal
- };
- struct LinuxWindow::Pimpl
- {
- ::Window xWindow = 0;
- INT32 x, y;
- UINT32 width, height;
- bool hasTitleBar = true;
- bool dragInProgress = false;
- bool resizeDisabled = false;
- WindowState state = WindowState::Normal;
- Rect2I dragZone;
- int32_t dragStartX, dragStartY;
- };
- LinuxWindow::LinuxWindow(const WINDOW_DESC &desc)
- {
- ::Display* display = LinuxPlatform::getXDisplay();
- int screen;
- if(desc.screen == (UINT32)-1)
- screen = XDefaultScreen(display);
- else
- screen = std::min((int)desc.screen, XScreenCount(display));
- XSetWindowAttributes attributes;
- attributes.background_pixel = XWhitePixel(display, screen);
- attributes.border_pixel = XBlackPixel(display, screen);
- attributes.colormap = XCreateColormap(display,
- XRootWindow(display, desc.visualInfo.screen),
- desc.visualInfo.visual,
- AllocNone);
- uint32_t borderWidth = 0;
- m->x = desc.x;
- m->y = desc.y;
- m->width = desc.width;
- m->height = desc.height;
- m->xWindow = XCreateWindow(display,
- XRootWindow(display, desc.visualInfo.screen),
- desc.x, desc.y,
- desc.width, desc.height,
- borderWidth, desc.visualInfo.depth,
- InputOutput, desc.visualInfo.visual,
- CWBackPixel | CWBorderPixel | CWColormap, &attributes);
- XStoreName(display, m->xWindow, desc.title.c_str());
- XSizeHints hints;
- hints.flags = PPosition | PSize;
- hints.x = desc.x;
- hints.y = desc.y;
- hints.width = desc.width;
- hints.height = desc.height;
- if(!desc.allowResize)
- {
- hints.flags |= PMinSize | PMaxSize;
- hints.min_height = desc.height;
- hints.max_height = desc.height;
- hints.min_width = desc.width;
- hints.max_width = desc.width;
- }
- XSetNormalHints(display, m->xWindow, &hints);
- setShowDecorations(desc.showDecorations);
- setIsModal(desc.modal);
- // Ensures the child window is always on top of the parent window
- if(desc.parent)
- XSetTransientForHint(display, m->xWindow, desc.parent);
- XSelectInput(display, m->xWindow,
- ExposureMask | FocusChangeMask |
- KeyPressMask | KeyReleaseMask |
- ButtonPressMask | ButtonReleaseMask |
- EnterWindowMask | LeaveWindowMask |
- PointerMotionMask | ButtonMotionMask |
- StructureNotifyMask
- );
- XMapWindow(display, m->xWindow);
- // Make sure we get the window delete message from WM, so we can clean up ourselves
- Atom atomDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", False);
- XSetWMProtocols(display, m->xWindow, &atomDeleteWindow, 1);
- // Set background image if assigned
- if(desc.background)
- {
- Pixmap pixmap = LinuxPlatform::createPixmap(desc.background);
- XSetWindowBackgroundPixmap(display, m->xWindow, pixmap);
- XFreePixmap(display, pixmap);
- }
- m->hasTitleBar = desc.showDecorations;
- m->resizeDisabled = !desc.allowResize;
- LinuxPlatform::_registerWindow(m->xWindow, this);
- }
- LinuxWindow::~LinuxWindow()
- {
- if(m->xWindow != 0)
- _cleanUp();
- }
- void LinuxWindow::close()
- {
- XDestroyWindow(LinuxPlatform::getXDisplay(), m->xWindow);
- XFlush(LinuxPlatform::getXDisplay());
- _cleanUp();
- }
- void LinuxWindow::move(INT32 x, INT32 y)
- {
- m->x = x;
- m->y = y;
- XMoveWindow(LinuxPlatform::getXDisplay(), m->xWindow, x, y);
- }
- void LinuxWindow::resize(UINT32 width, UINT32 height)
- {
- // If resize is disabled on WM level, we need to force it
- if(m->resizeDisabled)
- {
- XSizeHints hints;
- hints.flags = PMinSize | PMaxSize;
- hints.min_height = height;
- hints.max_height = height;
- hints.min_width = width;
- hints.max_width = width;
- XSetNormalHints(LinuxPlatform::getXDisplay(), m->xWindow, &hints);
- }
- m->width = width;
- m->height = height;
- XResizeWindow(LinuxPlatform::getXDisplay(), m->xWindow, width, height);
- }
- void LinuxWindow::hide()
- {
- // TODOPORT - Need to track all states so I can restore them on show()
- XUnmapWindow(LinuxPlatform::getXDisplay(), m->xWindow);
- }
- void LinuxWindow::show()
- {
- XMapWindow(LinuxPlatform::getXDisplay(), m->xWindow);
- XMoveResizeWindow(LinuxPlatform::getXDisplay(), m->xWindow, m->x, m->y, m->width, m->height);
- // TODOPORT - Restore all states (pos, size and style)
- }
- void LinuxWindow::maximize()
- {
- maximize(true);
- }
- void LinuxWindow::minimize()
- {
- minimize(true);
- }
- void LinuxWindow::restore()
- {
- if(isMaximized())
- maximize(false);
- else if(isMinimized())
- minimize(false);
- }
- INT32 LinuxWindow::getLeft() const
- {
- INT32 x, y;
- ::Window child;
- XTranslateCoordinates(LinuxPlatform::getXDisplay(), m->xWindow, DefaultRootWindow(LinuxPlatform::getXDisplay()),
- 0, 0, &x, &y, &child);
- return x;
- }
- INT32 LinuxWindow::getTop() const
- {
- INT32 x, y;
- ::Window child;
- XTranslateCoordinates(LinuxPlatform::getXDisplay(), m->xWindow, DefaultRootWindow(LinuxPlatform::getXDisplay()),
- 0, 0, &x, &y, &child);
- return y;
- }
- UINT32 LinuxWindow::getWidth() const
- {
- XWindowAttributes xwa;
- XGetWindowAttributes(LinuxPlatform::getXDisplay(), m->xWindow, &xwa);
- return xwa.width;
- }
- UINT32 LinuxWindow::getHeight() const
- {
- XWindowAttributes xwa;
- XGetWindowAttributes(LinuxPlatform::getXDisplay(), m->xWindow, &xwa);
- return xwa.height;
- }
- Vector2I LinuxWindow::windowToScreenPos(const Vector2I& windowPos) const
- {
- Vector2I screenPos;
- ::Window child;
- XTranslateCoordinates(LinuxPlatform::getXDisplay(), m->xWindow, DefaultRootWindow(LinuxPlatform::getXDisplay()),
- windowPos.x, windowPos.y, &screenPos.x, &screenPos.y, &child);
- return screenPos;
- }
- Vector2I LinuxWindow::screenToWindowPos(const Vector2I& screenPos) const
- {
- Vector2I windowPos;
- ::Window child;
- XTranslateCoordinates(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), m->xWindow,
- screenPos.x, screenPos.y, &windowPos.x, &windowPos.y, &child);
- return windowPos;
- }
- void LinuxWindow::setIcon(const SPtr<PixelData>& data)
- {
- Pixmap iconPixmap = LinuxPlatform::createPixmap(data);
- XWMHints* hints = XAllocWMHints();
- hints->flags = IconPixmapHint;
- hints->icon_pixmap = iconPixmap;
- XSetWMHints(LinuxPlatform::getXDisplay(), m->xWindow, hints);
- XFlush(LinuxPlatform::getXDisplay());
- XFree(hints);
- XFreePixmap(LinuxPlatform::getXDisplay(), iconPixmap);
- }
- void LinuxWindow::_cleanUp()
- {
- LinuxPlatform::_unregisterWindow(m->xWindow);
- m->xWindow = 0;
- }
- bool LinuxWindow::_dragStart(int32_t x, int32_t y)
- {
- if(m->hasTitleBar)
- return false;
- if(m->dragZone.width == 0 || m->dragZone.height == 0)
- return false;
- if(x >= m->dragZone.x && x < (m->dragZone.x + m->dragZone.width) &&
- y >= m->dragZone.y && y < (m->dragZone.y + m->dragZone.height))
- {
- m->dragStartX = x;
- m->dragStartY = y;
- m->dragInProgress = true;
- return true;
- }
- return false;
- }
- void LinuxWindow::_dragUpdate(int32_t x, int32_t y)
- {
- if(!m->dragInProgress)
- return;
- int32_t offsetX = x - m->dragStartX;
- int32_t offsetY = y - m->dragStartY;
- move(getLeft() + offsetX, getTop() + offsetY);
- }
- void LinuxWindow::_dragEnd()
- {
- m->dragInProgress = false;
- }
- ::Window LinuxWindow::_getXWindow() const
- {
- return m->xWindow;
- }
- bool LinuxWindow::isMaximized() const
- {
- Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE", False);
- Atom type;
- int32_t format;
- uint64_t length;
- uint64_t remaining;
- uint8_t* data = nullptr;
- int32_t result = XGetWindowProperty(LinuxPlatform::getXDisplay(), m->xWindow, wmState,
- 0, 1024, False, XA_ATOM, &type, &format,
- &length, &remaining, &data);
- if (result == Success)
- {
- Atom* atoms = (Atom*)data;
- Atom wmMaxHorz = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_MAXIMIZED_HORZ", False);
- Atom wmMaxVert = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_MAXIMIZED_VERT", False);
- bool foundHorz = false;
- bool foundVert = false;
- for (uint64_t i = 0; i < length; i++)
- {
- if (atoms[i] == wmMaxHorz)
- foundHorz = true;
- if (atoms[i] == wmMaxVert)
- foundVert = true;
- if (foundVert && foundHorz)
- return true;
- }
- XFree(atoms);
- }
- return false;
- }
- bool LinuxWindow::isMinimized()
- {
- Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "WM_STATE", True);
- Atom type;
- int32_t format;
- uint64_t length;
- uint64_t remaining;
- uint8_t* data = nullptr;
- int32_t result = XGetWindowProperty(LinuxPlatform::getXDisplay(), m->xWindow, wmState,
- 0, 1024, False, AnyPropertyType, &type, &format,
- &length, &remaining, &data);
- if(result == Success)
- {
- long* state = (long*) data;
- if(state[0] == WM_IconicState)
- return true;
- }
- return false;
- }
- void LinuxWindow::maximize(bool enable)
- {
- Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE", False);
- Atom wmMaxHorz = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_MAXIMIZED_HORZ", False);
- Atom wmMaxVert = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_MAXIMIZED_VERT", False);
- XEvent xev;
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = m->xWindow;
- xev.xclient.message_type = wmState;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = enable ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
- xev.xclient.data.l[1] = wmMaxHorz;
- xev.xclient.data.l[2] = wmMaxVert;
- XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
- SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- }
- void LinuxWindow::minimize(bool enable)
- {
- XEvent xev;
- Atom wmChange = XInternAtom(LinuxPlatform::getXDisplay(), "WM_CHANGE_STATE", False);
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = m->xWindow;
- xev.xclient.message_type = wmChange;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = enable ? WM_IconicState : WM_NormalState;
- XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
- SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- }
- void LinuxWindow::_setFullscreen(bool fullscreen)
- {
- // Attempt to bypass compositor if switching to fullscreen
- if(fullscreen)
- {
- Atom wmBypassCompositor = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_BYPASS_COMPOSITOR", False);
- if (wmBypassCompositor)
- {
- static constexpr uint32_t enabled = 1;
- XChangeProperty(LinuxPlatform::getXDisplay(), m->xWindow, wmBypassCompositor,
- XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &enabled, 1);
- }
- }
- // Make the switch to fullscreen
- XEvent xev;
- Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE", False);
- Atom wmFullscreen = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_FULLSCREEN", False);
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = m->xWindow;
- xev.xclient.message_type = wmState;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
- xev.xclient.data.l[1] = wmFullscreen;
- xev.xclient.data.l[2] = 0;
- XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
- SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- }
- void LinuxWindow::setShowDecorations(bool show)
- {
- static constexpr uint32_t MWM_HINTS_FUNCTIONS = (1 << 0);
- static constexpr uint32_t MWM_HINTS_DECORATIONS = (1 << 1);
- static constexpr uint32_t MWM_DECOR_BORDER = (1 << 1);
- static constexpr uint32_t MWM_DECOR_RESIZEH = (1 << 2);
- static constexpr uint32_t MWM_DECOR_TITLE = (1 << 3);
- static constexpr uint32_t MWM_DECOR_MENU = (1 << 4);
- static constexpr uint32_t MWM_DECOR_MINIMIZE = (1 << 5);
- static constexpr uint32_t MWM_DECOR_MAXIMIZE = (1 << 6);
- static constexpr uint32_t MWM_FUNC_RESIZE = (1 << 1);
- static constexpr uint32_t MWM_FUNC_MOVE = (1 << 2);
- static constexpr uint32_t MWM_FUNC_MINIMIZE = (1 << 3);
- static constexpr uint32_t MWM_FUNC_MAXIMIZE = (1 << 4);
- static constexpr uint32_t MWM_FUNC_CLOSE = (1 << 5);
- struct MotifHints
- {
- uint32_t flags;
- uint32_t functions;
- uint32_t decorations;
- int32_t inputMode;
- uint32_t status;
- };
- if(show)
- return;
- MotifHints motifHints;
- motifHints.flags = MWM_HINTS_DECORATIONS;
- motifHints.decorations = 0;
- motifHints.functions = 0;
- motifHints.inputMode = 0;
- motifHints.status = 0;
- Atom wmHintsAtom = XInternAtom(LinuxPlatform::getXDisplay(), "_MOTIF_WM_HINTS", False);
- XChangeProperty(LinuxPlatform::getXDisplay(), m->xWindow,
- wmHintsAtom, wmHintsAtom,
- 32,
- PropModeReplace,
- (unsigned char *)&motifHints,
- 5);
- }
- void LinuxWindow::setIsModal(bool modal)
- {
- if(modal)
- {
- Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE", False);
- Atom wmValue = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_MODAL", False);
- XEvent xev;
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = m->xWindow;
- xev.xclient.message_type = wmState;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
- xev.xclient.data.l[1] = wmValue;
- xev.xclient.data.l[2] = 0;
- xev.xclient.data.l[3] = 1;
- XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
- SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- }
- }
- }
|