diff options
Diffstat (limited to 'src/SFML/Window/Unix')
-rw-r--r-- | src/SFML/Window/Unix/ClipboardImpl.cpp | 459 | ||||
-rw-r--r-- | src/SFML/Window/Unix/ClipboardImpl.hpp | 78 | ||||
-rw-r--r-- | src/SFML/Window/Unix/GlxExtensions.hpp | 3 | ||||
-rw-r--r-- | src/SFML/Window/Unix/WindowImplX11.cpp | 307 | ||||
-rw-r--r-- | src/SFML/Window/Unix/WindowImplX11.hpp | 34 |
5 files changed, 662 insertions, 219 deletions
diff --git a/src/SFML/Window/Unix/ClipboardImpl.cpp b/src/SFML/Window/Unix/ClipboardImpl.cpp index 32ef21b..df2fd95 100644 --- a/src/SFML/Window/Unix/ClipboardImpl.cpp +++ b/src/SFML/Window/Unix/ClipboardImpl.cpp @@ -27,233 +27,356 @@ //////////////////////////////////////////////////////////// #include <SFML/Window/Unix/ClipboardImpl.hpp> #include <SFML/Window/Unix/Display.hpp> -#include <SFML/System/String.hpp> -#include <SFML/System/Sleep.hpp> -#include <iostream> -#include <string> -#include <X11/Xlib.h> +#include <SFML/System/Clock.hpp> +#include <SFML/System/Err.hpp> +#include <X11/Xatom.h> +#include <vector> namespace { + // Filter the events received by windows (only allow those matching a specific window) + Bool checkEvent(::Display*, XEvent* event, XPointer userData) + { + // Just check if the event matches the window + return event->xany.window == reinterpret_cast< ::Window >(userData); + } +} + +namespace sf +{ +namespace priv +{ + //////////////////////////////////////////////////////////// -void initClipboard(); -void* hostSelection(void*); +String ClipboardImpl::getString() +{ + return getInstance().getStringImpl(); +} -sf::String string; -pthread_mutex_t mutex; -pthread_t host_thread; +//////////////////////////////////////////////////////////// +void ClipboardImpl::setString(const String& text) +{ + getInstance().setStringImpl(text); +} -bool is_fail = false; -bool is_init = false; -bool is_host = false; -Display* display = NULL; -Window window = 0; +//////////////////////////////////////////////////////////// +void ClipboardImpl::processEvents() +{ + getInstance().processEventsImpl(); +} -Atom selection = 0; -Atom atom_targ = 0; -Atom atom_text = 0; -Atom utf8_text = 0; -int xa_string = 31; -int xa_atom = 4; //////////////////////////////////////////////////////////// -void initClipboard() +ClipboardImpl::ClipboardImpl() : +m_window (0), +m_requestResponded(false) { - is_init = true; + // Open a connection with the X server + m_display = OpenDisplay(); - display = XOpenDisplay(NULL); - int screen = DefaultScreen(display); - window = XCreateSimpleWindow(display, RootWindow(display, screen), - 0, 0, 1, 1, 0, BlackPixel(display, screen), WhitePixel(display, screen)); + // Get the atoms we need to make use of the clipboard + m_clipboard = getAtom("CLIPBOARD", false); + m_targets = getAtom("TARGETS", false); + m_text = getAtom("TEXT", false); + m_utf8String = getAtom("UTF8_STRING", true ); + m_targetProperty = getAtom("SFML_CLIPBOARD_TARGET_PROPERTY", false); - selection = XInternAtom(display, "CLIPBOARD", false); - atom_targ = XInternAtom(display, "TARGETS", false); - atom_text = XInternAtom(display, "TEXT", false); - utf8_text = XInternAtom(display, "UTF8_STRING", true); + // Create a hidden window that will broker our clipboard interactions with X + m_window = XCreateSimpleWindow(m_display, DefaultRootWindow(m_display), 0, 0, 1, 1, 0, 0, 0); - if(utf8_text == None) - { - std::cerr << "UTF-8 format unavailable on clipboard." << std::endl; - utf8_text = xa_string; - } + // Register the events we are interested in + XSelectInput(m_display, m_window, SelectionNotify | SelectionClear | SelectionRequest); +} - if(pthread_mutex_init(&mutex, NULL)) - { - is_fail = true; - std::cerr << "Unable to initialize mutex. Failed to initialize clipboard." << std::endl; - return; - } - if(pthread_create(&host_thread, NULL, hostSelection, NULL)) +//////////////////////////////////////////////////////////// +ClipboardImpl::~ClipboardImpl() +{ + // Destroy the window + if (m_window) { - is_fail = true; - std::cerr << "Unable to create host thread. Failed to initialize clipboard." << std::endl; - return; + XDestroyWindow(m_display, m_window); + XFlush(m_display); } + + // Close the connection with the X server + CloseDisplay(m_display); } + //////////////////////////////////////////////////////////// -void* hostSelection(void*) +ClipboardImpl& ClipboardImpl::getInstance() { - while(true) - { - if(XPending(display) && is_host) - { - XEvent event; + static ClipboardImpl instance; - pthread_mutex_lock(&mutex); - XNextEvent(display, &event); - pthread_mutex_unlock(&mutex); + return instance; +} - switch(event.type) - { - case SelectionClear: - { - pthread_mutex_lock(&mutex); - is_host = false; - pthread_mutex_unlock(&mutex); - break; - } - case SelectionRequest: - { - if(event.xselectionrequest.selection == selection) - { - XSelectionRequestEvent* sel_req_event = &event.xselectionrequest; - XSelectionEvent sel_event = {0}; - - int result = 0; - sel_event.type = SelectionNotify, - sel_event.display = sel_req_event->display, - sel_event.requestor = sel_req_event->requestor, - sel_event.selection = sel_req_event->selection, - sel_event.time = sel_req_event->time, - sel_event.target = sel_req_event->target, - sel_event.property = sel_req_event->property; - - std::basic_string<unsigned char> str = string.toUtf8(); - - if(sel_event.target == atom_targ) - result = XChangeProperty(sel_event.display, sel_event.requestor, - sel_event.property, xa_atom, 32, PropModeReplace, - reinterpret_cast<unsigned char*>(&utf8_text), 1); - else if(sel_event.target == xa_string || sel_event.target == atom_text) - result = XChangeProperty(sel_event.display, sel_event.requestor, - sel_event.property, xa_string, 8, PropModeReplace, - reinterpret_cast<unsigned char*>(&str[0]), str.size()); - else if(sel_event.target == utf8_text) - result = XChangeProperty(sel_event.display, sel_event.requestor, - sel_event.property, utf8_text, 8, PropModeReplace, - reinterpret_cast<unsigned char*>(&str[0]), str.size()); - else - sel_event.property = None; - - if((result & 2) == 0) - XSendEvent(display, sel_event.requestor, 0, 0, - reinterpret_cast<XEvent*>(&sel_event)); - } - break; - } - default: break; - } - } - else - sf::sleep(sf::milliseconds(20)); +//////////////////////////////////////////////////////////// +String ClipboardImpl::getStringImpl() +{ + // Check if anybody owns the current selection + if (XGetSelectionOwner(m_display, m_clipboard) == None) + { + m_clipboardContents.clear(); + + return m_clipboardContents; } -} + + // Process any already pending events + processEvents(); + + m_requestResponded = false; + + // Request the current selection to be converted to UTF-8 (or STRING + // if UTF-8 is not available) and written to our window property + XConvertSelection( + m_display, + m_clipboard, + (m_utf8String != None) ? m_utf8String : XA_STRING, + m_targetProperty, + m_window, + CurrentTime + ); + + Clock clock; + + // Wait for a response for up to 1000ms + while (!m_requestResponded && (clock.getElapsedTime().asMilliseconds() < 1000)) + processEvents(); + + // If no response was received within the time period, clear our clipboard contents + if (!m_requestResponded) + m_clipboardContents.clear(); + + return m_clipboardContents; } -namespace sf -{ -namespace priv -{ //////////////////////////////////////////////////////////// -String ClipboardImpl::getString() +void ClipboardImpl::setStringImpl(const String& text) { - if(!is_init) - initClipboard(); + m_clipboardContents = text; + + // Set our window as the current owner of the selection + XSetSelectionOwner(m_display, m_clipboard, m_window, CurrentTime); - if(is_fail || is_host) - return string; + // Check if setting the selection owner was successful + if (XGetSelectionOwner(m_display, m_clipboard) != m_window) + err() << "Cannot set clipboard string: Unable to get ownership of X selection" << std::endl; +} - // Dangerous! Wipes all previous events! - XSync(display, true); - XConvertSelection(display, selection, utf8_text, atom_text, window, CurrentTime); +//////////////////////////////////////////////////////////// +void ClipboardImpl::processEventsImpl() +{ XEvent event; - pthread_mutex_lock(&mutex); - XNextEvent(display, &event); - pthread_mutex_unlock(&mutex); + // Pick out the events that are interesting for this window + while (XCheckIfEvent(m_display, &event, &checkEvent, reinterpret_cast<XPointer>(m_window))) + m_events.push_back(event); - if(event.type == SelectionNotify) + // Handle the events for this window that we just picked out + while (!m_events.empty()) { - if(event.xselection.selection != selection || event.xselection.target != utf8_text) + event = m_events.front(); + m_events.pop_front(); + processEvent(event); + } +} + + +//////////////////////////////////////////////////////////// +void ClipboardImpl::processEvent(XEvent& windowEvent) +{ + switch (windowEvent.type) + { + case SelectionClear: { - std::cerr << "Failed to convert selection." << std::endl; - return string; + // We don't have any resources we need to clean up + // when losing selection ownership so we don't do + // anything when we receive SelectionClear + // We will still respond to any future SelectionRequest + // events since doing so doesn't really do any harm + break; } - - if(event.xselection.property) + case SelectionNotify: { - Atom target; - int format; - unsigned long size; - unsigned long byte_left; - unsigned char* data; + // Notification that the current selection owner + // has responded to our request + + XSelectionEvent& selectionEvent = *reinterpret_cast<XSelectionEvent*>(&windowEvent.xselection); - XGetWindowProperty(event.xselection.display, - event.xselection.requestor, event.xselection.property, - 0L, (~0L), false, AnyPropertyType, - &target, &format, &size, &byte_left, &data); + m_clipboardContents.clear(); - if(target == utf8_text) + // If retrieving the selection fails or conversion is unsuccessful + // we leave the contents of the clipboard empty since we don't + // own it and we don't know what it could currently be + if ((selectionEvent.property == None) || (selectionEvent.selection != m_clipboard)) + break; + + Atom type; + int format; + unsigned long items; + unsigned long remainingBytes; + unsigned char* data = 0; + + // The selection owner should have wrote the selection + // data to the specified window property + int result = XGetWindowProperty( + m_display, + m_window, + m_targetProperty, + 0, + 0x7fffffff, + False, + AnyPropertyType, + &type, + &format, + &items, + &remainingBytes, + &data + ); + + if (result == Success) { - std::basic_string<unsigned char> str(data, size); - string = sf::String::fromUtf8(str.begin(), str.end()); + // We don't support INCR for now + // It is very unlikely that this will be returned + // for purely text data transfer anyway + if (type != getAtom("INCR", false)) + { + // Only copy the data if the format is what we expect + if ((type == m_utf8String) && (format == 8)) + { + m_clipboardContents = String::fromUtf8(data, data + items); + } + else if ((type == XA_STRING) && (format == 8)) + { + // Convert from ANSI std::string to sf::String + m_clipboardContents = std::string(data, data + items); + } + } XFree(data); + + // The selection requestor must always delete the property themselves + XDeleteProperty(m_display, m_window, m_targetProperty); } - XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); + m_requestResponded = true; + + break; } - } + case SelectionRequest: + { + // Respond to a request for our clipboard contents + XSelectionRequestEvent& selectionRequestEvent = *reinterpret_cast<XSelectionRequestEvent*>(&windowEvent.xselectionrequest); - return string; -} + // Our reply + XSelectionEvent selectionEvent; + selectionEvent.type = SelectionNotify; + selectionEvent.requestor = selectionRequestEvent.requestor; + selectionEvent.selection = selectionRequestEvent.selection; + selectionEvent.property = selectionRequestEvent.property; + selectionEvent.time = selectionRequestEvent.time; -//////////////////////////////////////////////////////////// -void ClipboardImpl::setString(const String& text) -{ - if(!is_init) - initClipboard(); + if (selectionRequestEvent.selection == m_clipboard) + { + if (selectionRequestEvent.target == m_targets) + { + // Respond to a request for our valid conversion targets + std::vector<Atom> targets; - if(is_fail) - return; + targets.push_back(m_targets); + targets.push_back(m_text); + targets.push_back(XA_STRING); - if(!is_host) - { - XSetSelectionOwner(display, selection, window, CurrentTime); + if (m_utf8String != None) + targets.push_back(m_utf8String); - if(XGetSelectionOwner(display, selection) != window) - { - std::cerr << "Unable to get ownership of selection." << std::endl; - return; - } + XChangeProperty( + m_display, + selectionRequestEvent.requestor, + selectionRequestEvent.property, + XA_ATOM, + 32, + PropModeReplace, + reinterpret_cast<unsigned char*>(&targets[0]), + targets.size() + ); - pthread_mutex_lock(&mutex); - is_host = true; - pthread_mutex_unlock(&mutex); - } + // Notify the requestor that they can read the targets from their window property + selectionEvent.target = m_targets; + + XSendEvent(m_display, selectionRequestEvent.requestor, True, NoEventMask, reinterpret_cast<XEvent*>(&selectionEvent)); + + break; + } + else if ((selectionRequestEvent.target == XA_STRING) || ((m_utf8String == None) && (selectionRequestEvent.target == m_text))) + { + // Respond to a request for conversion to a Latin-1 string + std::string data = m_clipboardContents.toAnsiString(); + + XChangeProperty( + m_display, + selectionRequestEvent.requestor, + selectionRequestEvent.property, + XA_STRING, + 8, + PropModeReplace, + reinterpret_cast<const unsigned char*>(data.c_str()), + data.size() + ); - pthread_mutex_lock(&mutex); - string = text; - pthread_mutex_unlock(&mutex); + // Notify the requestor that they can read the data from their window property + selectionEvent.target = XA_STRING; + + XSendEvent(m_display, selectionRequestEvent.requestor, True, NoEventMask, reinterpret_cast<XEvent*>(&selectionEvent)); + + break; + } + else if ((m_utf8String != None) && ((selectionRequestEvent.target == m_utf8String) || (selectionRequestEvent.target == m_text))) + { + // Respond to a request for conversion to a UTF-8 string + // or an encoding of our choosing (we always choose UTF-8) + std::basic_string<Uint8> data = m_clipboardContents.toUtf8(); + + XChangeProperty( + m_display, + selectionRequestEvent.requestor, + selectionRequestEvent.property, + m_utf8String, + 8, + PropModeReplace, + reinterpret_cast<const unsigned char*>(data.c_str()), + data.size() + ); + + // Notify the requestor that they can read the data from their window property + selectionEvent.target = m_utf8String; + + XSendEvent(m_display, selectionRequestEvent.requestor, True, NoEventMask, reinterpret_cast<XEvent*>(&selectionEvent)); + + break; + } + } + + // Notify the requestor that we could not respond to their request + selectionEvent.target = selectionRequestEvent.target; + selectionEvent.property = None; + + XSendEvent(m_display, selectionRequestEvent.requestor, True, NoEventMask, reinterpret_cast<XEvent*>(&selectionEvent)); + + break; + } + default: + break; + } } } // namespace priv diff --git a/src/SFML/Window/Unix/ClipboardImpl.hpp b/src/SFML/Window/Unix/ClipboardImpl.hpp index f4aca98..48ec787 100644 --- a/src/SFML/Window/Unix/ClipboardImpl.hpp +++ b/src/SFML/Window/Unix/ClipboardImpl.hpp @@ -29,6 +29,8 @@ // Headers //////////////////////////////////////////////////////////// #include <SFML/System/String.hpp> +#include <X11/Xlib.h> +#include <deque> namespace sf @@ -67,6 +69,82 @@ public: /// //////////////////////////////////////////////////////////// static void setString(const String& text); + + //////////////////////////////////////////////////////////// + /// \brief Process pending events for the hidden clipboard window + /// + /// This function has to be called as part of normal window + /// event processing in order for our application to respond + /// to selection requests from other applications. + /// + //////////////////////////////////////////////////////////// + static void processEvents(); + +private: + + //////////////////////////////////////////////////////////// + /// \brief Constructor + /// + //////////////////////////////////////////////////////////// + ClipboardImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~ClipboardImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Get singleton instance + /// + /// \return Singleton instance + /// + //////////////////////////////////////////////////////////// + static ClipboardImpl& getInstance(); + + //////////////////////////////////////////////////////////// + /// \brief getString implementation + /// + /// \return Current content of the clipboard + /// + //////////////////////////////////////////////////////////// + String getStringImpl(); + + //////////////////////////////////////////////////////////// + /// \brief setString implementation + /// + /// \param text sf::String object containing the data to be sent to the clipboard + /// + //////////////////////////////////////////////////////////// + void setStringImpl(const String& text); + + //////////////////////////////////////////////////////////// + /// \brief processEvents implementation + /// + //////////////////////////////////////////////////////////// + void processEventsImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Process an incoming event from the window + /// + /// \param windowEvent Event which has been received + /// + //////////////////////////////////////////////////////////// + void processEvent(XEvent& windowEvent); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + ::Window m_window; ///< X identifier defining our window + ::Display* m_display; ///< Pointer to the display + Atom m_clipboard; ///< X Atom identifying the CLIPBOARD selection + Atom m_targets; ///< X Atom identifying TARGETS + Atom m_text; ///< X Atom identifying TEXT + Atom m_utf8String; ///< X Atom identifying UTF8_STRING + Atom m_targetProperty; ///< X Atom identifying our destination window property + String m_clipboardContents; ///< Our clipboard contents + std::deque<XEvent> m_events; ///< Queue we use to store pending events for this window + bool m_requestResponded; ///< Holds whether our selection request has been responded to or not }; } // namespace priv diff --git a/src/SFML/Window/Unix/GlxExtensions.hpp b/src/SFML/Window/Unix/GlxExtensions.hpp index abbb132..fb2c7da 100644 --- a/src/SFML/Window/Unix/GlxExtensions.hpp +++ b/src/SFML/Window/Unix/GlxExtensions.hpp @@ -25,11 +25,12 @@ #ifndef SF_POINTER_C_GENERATED_HEADER_GLXWIN_HPP #define SF_POINTER_C_GENERATED_HEADER_GLXWIN_HPP -#ifdef __glxext_h_ +#if defined(__glxext_h_) || defined(__glx_glxext_h_) #error Attempt to include glx_exts after including glxext.h #endif #define __glxext_h_ +#define __glx_glxext_h_ #include <X11/Xlib.h> #include <X11/Xutil.h> diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index e2c8d29..da69750 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -26,6 +26,7 @@ // Headers //////////////////////////////////////////////////////////// #include <SFML/Window/Unix/WindowImplX11.hpp> +#include <SFML/Window/Unix/ClipboardImpl.hpp> #include <SFML/Window/Unix/Display.hpp> #include <SFML/Window/Unix/InputImpl.hpp> #include <SFML/System/Utf.hpp> @@ -173,7 +174,7 @@ namespace if (result != Success || actualType != XA_WINDOW || numItems != 1) { - if(result == Success) + if (result == Success) XFree(data); sf::priv::CloseDisplay(display); @@ -205,7 +206,7 @@ namespace if (result != Success || actualType != XA_WINDOW || numItems != 1) { - if(result == Success) + if (result == Success) XFree(data); sf::priv::CloseDisplay(display); @@ -269,7 +270,7 @@ namespace windowManagerName = sf::String::fromUtf8(begin, end); } - if(result == Success) + if (result == Success) XFree(data); sf::priv::CloseDisplay(display); @@ -486,6 +487,7 @@ m_inputMethod (NULL), m_inputContext (NULL), m_isExternal (true), m_oldVideoMode (0), +m_oldRRCrtc (0), m_hiddenCursor (0), m_lastCursor (None), m_keyRepeat (true), @@ -534,6 +536,7 @@ m_inputMethod (NULL), m_inputContext (NULL), m_isExternal (false), m_oldVideoMode (0), +m_oldRRCrtc (0), m_hiddenCursor (0), m_lastCursor (None), m_keyRepeat (true), @@ -555,8 +558,17 @@ m_lastInputTime (0) m_screen = DefaultScreen(m_display); // Compute position and size - int left = m_fullscreen ? 0 : (DisplayWidth(m_display, m_screen) - mode.width) / 2; - int top = m_fullscreen ? 0 : (DisplayHeight(m_display, m_screen) - mode.height) / 2; + Vector2i windowPosition; + if(m_fullscreen) + { + windowPosition = getPrimaryMonitorPosition(); + } + else + { + windowPosition.x = (DisplayWidth(m_display, m_screen) - mode.width) / 2; + windowPosition.y = (DisplayWidth(m_display, m_screen) - mode.height) / 2; + } + int width = mode.width; int height = mode.height; @@ -571,7 +583,7 @@ m_lastInputTime (0) m_window = XCreateWindow(m_display, DefaultRootWindow(m_display), - left, top, + windowPosition.x, windowPosition.y, width, height, 0, visualInfo.depth, @@ -668,9 +680,11 @@ m_lastInputTime (0) { m_useSizeHints = true; XSizeHints* sizeHints = XAllocSizeHints(); - sizeHints->flags = PMinSize | PMaxSize; + sizeHints->flags = PMinSize | PMaxSize | USPosition; sizeHints->min_width = sizeHints->max_width = width; sizeHints->min_height = sizeHints->max_height = height; + sizeHints->x = windowPosition.x; + sizeHints->y = windowPosition.y; XSetWMNormalHints(m_display, m_window, sizeHints); XFree(sizeHints); } @@ -707,7 +721,15 @@ m_lastInputTime (0) // Set fullscreen video mode and switch to fullscreen if necessary if (m_fullscreen) { - setPosition(Vector2i(0, 0)); + // Disable hint for min and max size, + // otherwise some windows managers will not remove window decorations + XSizeHints *sizeHints = XAllocSizeHints(); + long flags = 0; + XGetWMNormalHints(m_display, m_window, sizeHints, &flags); + sizeHints->flags &= ~(PMinSize | PMaxSize); + XSetWMNormalHints(m_display, m_window, sizeHints); + XFree(sizeHints); + setVideoMode(mode); switchToFullscreen(); } @@ -721,11 +743,11 @@ WindowImplX11::~WindowImplX11() cleanup(); // Destroy icon pixmap - if(m_iconPixmap) + if (m_iconPixmap) XFreePixmap(m_display, m_iconPixmap); // Destroy icon mask pixmap - if(m_iconMaskPixmap) + if (m_iconMaskPixmap) XFreePixmap(m_display, m_iconMaskPixmap); // Destroy the cursor @@ -779,6 +801,9 @@ void WindowImplX11::processEvents() m_events.pop_front(); processEvent(event); } + + // Process clipboard window events + priv::ClipboardImpl::processEvents(); } @@ -956,10 +981,10 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 return; } - if(m_iconPixmap) + if (m_iconPixmap) XFreePixmap(m_display, m_iconPixmap); - if(m_iconMaskPixmap) + if (m_iconMaskPixmap) XFreePixmap(m_display, m_iconMaskPixmap); m_iconPixmap = XCreatePixmap(m_display, RootWindow(m_display, m_screen), width, height, defDepth); @@ -1236,53 +1261,99 @@ void WindowImplX11::setVideoMode(const VideoMode& mode) return; // Check if the XRandR extension is present - int version; - if (!XQueryExtension(m_display, "RANDR", &version, &version, &version)) + int xRandRMajor, xRandRMinor; + if (!checkXRandR(xRandRMajor, xRandRMinor)) { // XRandR extension is not supported: we cannot use fullscreen mode err() << "Fullscreen is not supported, switching to window mode" << std::endl; return; } - // Get the current configuration - XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen)); + // Get root window + ::Window rootWindow = RootWindow(m_display, m_screen); - if (!config) + // Get the screen resources + XRRScreenResources* res = XRRGetScreenResources(m_display, rootWindow); + if (!res) { - // Failed to get the screen configuration - err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; + err() << "Failed to get the current screen resources for fullscreen mode, switching to window mode" << std::endl; return; } - // Save the current video mode before we switch to fullscreen - Rotation currentRotation; - m_oldVideoMode = XRRConfigCurrentConfiguration(config, ¤tRotation); + RROutput output = getOutputPrimary(rootWindow, res, xRandRMajor, xRandRMinor); + + // Get output info from output + XRROutputInfo* outputInfo = XRRGetOutputInfo(m_display, res, output); + if (!outputInfo || outputInfo->connection == RR_Disconnected) + { + XRRFreeScreenResources(res); - // Get the available screen sizes - int nbSizes; - XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes); + // If outputInfo->connection == RR_Disconnected, free output info + if (outputInfo) + XRRFreeOutputInfo(outputInfo); - // Search for a matching size - for (int i = 0; (sizes && i < nbSizes); ++i) + err() << "Failed to get output info for fullscreen mode, switching to window mode" << std::endl; + return; + } + + // Retreive current RRMode, screen position and rotation + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, outputInfo->crtc); + if (!crtcInfo) { - XRRConfigRotations(config, ¤tRotation); + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + err() << "Failed to get crtc info for fullscreen mode, switching to window mode" << std::endl; + return; + } - if (currentRotation == RR_Rotate_90 || currentRotation == RR_Rotate_270) - std::swap(sizes[i].height, sizes[i].width); + // Find RRMode to set + bool modeFound = false; + RRMode xRandMode; - if ((sizes[i].width == static_cast<int>(mode.width)) && (sizes[i].height == static_cast<int>(mode.height))) - { - // Switch to fullscreen mode - XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), i, currentRotation, CurrentTime); + for (int i = 0; (i < res->nmode) && !modeFound; i++) + { + if (crtcInfo->rotation == RR_Rotate_90 || crtcInfo->rotation == RR_Rotate_270) + std::swap(res->modes[i].height, res->modes[i].width); - // Set "this" as the current fullscreen window - fullscreenWindow = this; - break; + // Check if screen size match + if (res->modes[i].width == static_cast<int>(mode.width) && + res->modes[i].height == static_cast<int>(mode.height)) + { + xRandMode = res->modes[i].id; + modeFound = true; } } - // Free the configuration instance - XRRFreeScreenConfigInfo(config); + if (!modeFound) + { + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + err() << "Failed to find a matching RRMode for fullscreen mode, switching to window mode" << std::endl; + return; + } + + // Save the current video mode before we switch to fullscreen + m_oldVideoMode = crtcInfo->mode; + m_oldRRCrtc = outputInfo->crtc; + + // Switch to fullscreen mode + XRRSetCrtcConfig(m_display, + res, + outputInfo->crtc, + CurrentTime, + crtcInfo->x, + crtcInfo->y, + xRandMode, + crtcInfo->rotation, + &output, + 1); + + // Set "this" as the current fullscreen window + fullscreenWindow = this; + + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + XRRFreeCrtcInfo(crtcInfo); } @@ -1291,19 +1362,55 @@ void WindowImplX11::resetVideoMode() { if (fullscreenWindow == this) { - // Get current screen info - XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen)); - if (config) + // Try to set old configuration + // Check if the XRandR extension + int xRandRMajor, xRandRMinor; + if (checkXRandR(xRandRMajor, xRandRMinor)) { - // Get the current rotation - Rotation currentRotation; - XRRConfigCurrentConfiguration(config, ¤tRotation); + XRRScreenResources* res = XRRGetScreenResources(m_display, DefaultRootWindow(m_display)); + if (!res) + { + err() << "Failed to get the current screen resources to reset the video mode" << std::endl; + return; + } + + // Retreive current screen position and rotation + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, m_oldRRCrtc); + if (!crtcInfo) + { + XRRFreeScreenResources(res); + err() << "Failed to get crtc info to reset the video mode" << std::endl; + return; + } + + RROutput output; + + // if version >= 1.3 get the primary screen else take the first screen + if ((xRandRMajor == 1 && xRandRMinor >= 3) || xRandRMajor > 1) + { + output = XRRGetOutputPrimary(m_display, DefaultRootWindow(m_display)); - // Reset the video mode - XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), m_oldVideoMode, currentRotation, CurrentTime); + // Check if returned output is valid, otherwise use the first screen + if (output == None) + output = res->outputs[0]; + } + else{ + output = res->outputs[0]; + } - // Free the configuration instance - XRRFreeScreenConfigInfo(config); + XRRSetCrtcConfig(m_display, + res, + m_oldRRCrtc, + CurrentTime, + crtcInfo->x, + crtcInfo->y, + m_oldVideoMode, + crtcInfo->rotation, + &output, + 1); + + XRRFreeCrtcInfo(crtcInfo); + XRRFreeScreenResources(res); } // Reset the fullscreen window @@ -1499,7 +1606,7 @@ void WindowImplX11::updateLastInputTime(::Time time) { Atom netWmUserTime = getAtom("_NET_WM_USER_TIME", true); - if(netWmUserTime) + if (netWmUserTime) { XChangeProperty(m_display, m_window, @@ -1969,6 +2076,106 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) return true; } + +//////////////////////////////////////////////////////////// +bool WindowImplX11::checkXRandR(int& xRandRMajor, int& xRandRMinor) +{ + // Check if the XRandR extension is present + int version; + if (!XQueryExtension(m_display, "RANDR", &version, &version, &version)) + { + err() << "XRandR extension is not supported" << std::endl; + return false; + } + + // Check XRandR version, 1.2 required + if (!XRRQueryVersion(m_display, &xRandRMajor, &xRandRMinor) || xRandRMajor < 1 || (xRandRMajor == 1 && xRandRMinor < 2 )) + { + err() << "XRandR is too old" << std::endl; + return false; + } + + return true; +} + + +//////////////////////////////////////////////////////////// +RROutput WindowImplX11::getOutputPrimary(::Window& rootWindow, XRRScreenResources* res, int xRandRMajor, int xRandRMinor) +{ + // if xRandR version >= 1.3 get the primary screen else take the first screen + if ((xRandRMajor == 1 && xRandRMinor >= 3) || xRandRMajor > 1) + { + RROutput output = XRRGetOutputPrimary(m_display, rootWindow); + + // Check if returned output is valid, otherwise use the first screen + if (output == None) + return res->outputs[0]; + else + return output; + } + + // xRandr version can't get the primary screen, use the first screen + return res->outputs[0]; +} + + +//////////////////////////////////////////////////////////// +Vector2i WindowImplX11::getPrimaryMonitorPosition() +{ + Vector2i monitorPosition; + + // Get root window + ::Window rootWindow = RootWindow(m_display, m_screen); + + // Get the screen resources + XRRScreenResources* res = XRRGetScreenResources(m_display, rootWindow); + if (!res) + { + err() << "Failed to get the current screen resources for.primary monitor position" << std::endl; + return monitorPosition; + } + + // Get xRandr version + int xRandRMajor, xRandRMinor; + if (!checkXRandR(xRandRMajor, xRandRMinor)) + xRandRMajor = xRandRMinor = 0; + + RROutput output = getOutputPrimary(rootWindow, res, xRandRMajor, xRandRMinor); + + // Get output info from output + XRROutputInfo* outputInfo = XRRGetOutputInfo(m_display, res, output); + if (!outputInfo || outputInfo->connection == RR_Disconnected) + { + XRRFreeScreenResources(res); + + // If outputInfo->connection == RR_Disconnected, free output info + if (outputInfo) + XRRFreeOutputInfo(outputInfo); + + err() << "Failed to get output info for.primary monitor position" << std::endl; + return monitorPosition; + } + + // Retreive current RRMode, screen position and rotation + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, outputInfo->crtc); + if (!crtcInfo) + { + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + err() << "Failed to get crtc info for.primary monitor position" << std::endl; + return monitorPosition; + } + + monitorPosition.x = crtcInfo->x; + monitorPosition.y = crtcInfo->y; + + XRRFreeCrtcInfo(crtcInfo); + XRRFreeOutputInfo(outputInfo); + XRRFreeScreenResources(res); + + return monitorPosition; +} + } // namespace priv } // namespace sf diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 0ff694b..a025a1a 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -34,6 +34,7 @@ #include <SFML/Window/WindowStyle.hpp> // Prevent conflict with macro None from Xlib #include <X11/Xlib.h> #include <deque> +#include <X11/extensions/Xrandr.h> namespace sf @@ -265,6 +266,38 @@ private: bool processEvent(XEvent& windowEvent); //////////////////////////////////////////////////////////// + /// \brief Check if a valid version of XRandR extension is present + /// + /// \param xRandRMajor XRandR major version + /// \param xRandRMinor XRandR minor version + /// + /// \return True if a valid XRandR version found, false otherwise + /// + //////////////////////////////////////////////////////////// + bool checkXRandR(int& xRandRMajor, int& xRandRMinor); + + //////////////////////////////////////////////////////////// + /// \brief Get the RROutput of the primary monitor + /// + /// \param rootWindow the root window + /// \param res screen resources + /// \param xRandRMajor XRandR major version + /// \param xRandRMinor XRandR minor version + /// + /// \return RROutput of the primary monitor + /// + //////////////////////////////////////////////////////////// + RROutput getOutputPrimary(::Window& rootWindow, XRRScreenResources* res, int xRandRMajor, int xRandRMinor); + + //////////////////////////////////////////////////////////// + /// \brief Get coordinates of the primary monitor + /// + /// \return Position of the primary monitor + /// + //////////////////////////////////////////////////////////// + Vector2i getPrimaryMonitorPosition(); + + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// ::Window m_window; ///< X identifier defining our window @@ -275,6 +308,7 @@ private: std::deque<XEvent> m_events; ///< Queue we use to store pending events for this window bool m_isExternal; ///< Tell whether the window has been created externally or by SFML int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen + RRCrtc m_oldRRCrtc; ///< RRCrtc in use before we switch to fullscreen ::Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hiding, we must create a transparent one ::Cursor m_lastCursor; ///< Last cursor used -- this data is not owned by the window and is required to be always valid bool m_keyRepeat; ///< Is the KeyRepeat feature enabled? |