Fixed the Unix clipboard implementation causing an abort due to internal data races in Xlib.
This commit is contained in:
parent
b97a5be615
commit
235abae134
@ -60,7 +60,12 @@ public:
|
||||
/// This function sets the content of the clipboard as a
|
||||
/// string.
|
||||
///
|
||||
/// \param text sf::String containing the data to be sent
|
||||
/// \warning Due to limitations on some operating systems,
|
||||
/// setting the clipboard contents is only
|
||||
/// guaranteed to work if there is currently an
|
||||
/// open window for which events are being handled.
|
||||
///
|
||||
/// \param text sf::String containing the data to be sent
|
||||
/// to the clipboard
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
@ -80,6 +85,11 @@ public:
|
||||
/// sf::Clipboard provides an interface for getting and
|
||||
/// setting the contents of the system clipboard.
|
||||
///
|
||||
/// It is important to note that due to limitations on some
|
||||
/// operating systems, setting the clipboard contents is
|
||||
/// only guaranteed to work if there is currently an open
|
||||
/// window for which events are being handled.
|
||||
///
|
||||
/// Usage example:
|
||||
/// \code
|
||||
/// // get the clipboard content as a string
|
||||
@ -96,11 +106,12 @@ public:
|
||||
/// // Using Ctrl + V to paste a string into SFML
|
||||
/// if(event.key.control && event.key.code == sf::Keyboard::V)
|
||||
/// string = sf::Clipboard::getString();
|
||||
///
|
||||
/// // Using Ctrl + C to copy a string out of SFML
|
||||
/// if(event.key.control && event.key.code == sf::Keyboard::C)
|
||||
/// sf::Clipboard::setString("Hello World!");
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // set the clipboard to a string
|
||||
/// sf::Clipboard::setString("Hello World!");
|
||||
/// \endcode
|
||||
///
|
||||
/// \see sf::String, sf::Event
|
||||
|
@ -27,143 +27,20 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
#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
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
void initClipboard();
|
||||
void* hostSelection(void*);
|
||||
|
||||
sf::String string;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
pthread_t host_thread;
|
||||
|
||||
bool is_fail = false;
|
||||
bool is_init = false;
|
||||
bool is_host = false;
|
||||
|
||||
Display* display = NULL;
|
||||
Window window = 0;
|
||||
|
||||
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()
|
||||
{
|
||||
is_init = true;
|
||||
|
||||
display = XOpenDisplay(NULL);
|
||||
int screen = DefaultScreen(display);
|
||||
window = XCreateSimpleWindow(display, RootWindow(display, screen),
|
||||
0, 0, 1, 1, 0, BlackPixel(display, screen), WhitePixel(display, screen));
|
||||
|
||||
selection = XInternAtom(display, "CLIPBOARD", false);
|
||||
atom_targ = XInternAtom(display, "TARGETS", false);
|
||||
atom_text = XInternAtom(display, "TEXT", false);
|
||||
utf8_text = XInternAtom(display, "UTF8_STRING", true);
|
||||
|
||||
if(utf8_text == None)
|
||||
// Filter the events received by windows (only allow those matching a specific window)
|
||||
Bool checkEvent(::Display*, XEvent* event, XPointer userData)
|
||||
{
|
||||
std::cerr << "UTF-8 format unavailable on clipboard." << std::endl;
|
||||
utf8_text = xa_string;
|
||||
// Just check if the event matches the window
|
||||
return event->xany.window == reinterpret_cast< ::Window >(userData);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
is_fail = true;
|
||||
std::cerr << "Unable to create host thread. Failed to initialize clipboard." << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void* hostSelection(void*)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
if(XPending(display) && is_host)
|
||||
{
|
||||
XEvent event;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
XNextEvent(display, &event);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace sf
|
||||
@ -174,86 +51,332 @@ namespace priv
|
||||
////////////////////////////////////////////////////////////
|
||||
String ClipboardImpl::getString()
|
||||
{
|
||||
if(!is_init)
|
||||
initClipboard();
|
||||
|
||||
if(is_fail || is_host)
|
||||
return string;
|
||||
|
||||
// Dangerous! Wipes all previous events!
|
||||
XSync(display, true);
|
||||
XConvertSelection(display, selection, utf8_text, atom_text, window, CurrentTime);
|
||||
|
||||
XEvent event;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
XNextEvent(display, &event);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
if(event.type == SelectionNotify)
|
||||
{
|
||||
if(event.xselection.selection != selection || event.xselection.target != utf8_text)
|
||||
{
|
||||
std::cerr << "Failed to convert selection." << std::endl;
|
||||
return string;
|
||||
}
|
||||
|
||||
if(event.xselection.property)
|
||||
{
|
||||
Atom target;
|
||||
int format;
|
||||
unsigned long size;
|
||||
unsigned long byte_left;
|
||||
unsigned char* data;
|
||||
|
||||
XGetWindowProperty(event.xselection.display,
|
||||
event.xselection.requestor, event.xselection.property,
|
||||
0L, (~0L), false, AnyPropertyType,
|
||||
&target, &format, &size, &byte_left, &data);
|
||||
|
||||
if(target == utf8_text)
|
||||
{
|
||||
std::basic_string<unsigned char> str(data, size);
|
||||
string = sf::String::fromUtf8(str.begin(), str.end());
|
||||
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
return getInstance().getStringImpl();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void ClipboardImpl::setString(const String& text)
|
||||
{
|
||||
if(!is_init)
|
||||
initClipboard();
|
||||
getInstance().setStringImpl(text);
|
||||
}
|
||||
|
||||
if(is_fail)
|
||||
return;
|
||||
|
||||
if(!is_host)
|
||||
////////////////////////////////////////////////////////////
|
||||
void ClipboardImpl::processEvents()
|
||||
{
|
||||
getInstance().processEventsImpl();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
ClipboardImpl::ClipboardImpl() :
|
||||
m_window (0),
|
||||
m_requestResponded(false)
|
||||
{
|
||||
// Open a connection with the X server
|
||||
m_display = OpenDisplay();
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Register the events we are interested in
|
||||
XSelectInput(m_display, m_window, SelectionNotify | SelectionClear | SelectionRequest);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
ClipboardImpl::~ClipboardImpl()
|
||||
{
|
||||
// Destroy the window
|
||||
if (m_window)
|
||||
{
|
||||
XSetSelectionOwner(display, selection, window, CurrentTime);
|
||||
|
||||
if(XGetSelectionOwner(display, selection) != window)
|
||||
{
|
||||
std::cerr << "Unable to get ownership of selection." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
is_host = true;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
XDestroyWindow(m_display, m_window);
|
||||
XFlush(m_display);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
string = text;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
// Close the connection with the X server
|
||||
CloseDisplay(m_display);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
ClipboardImpl& ClipboardImpl::getInstance()
|
||||
{
|
||||
static ClipboardImpl instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void ClipboardImpl::setStringImpl(const String& text)
|
||||
{
|
||||
m_clipboardContents = text;
|
||||
|
||||
// Set our window as the current owner of the selection
|
||||
XSetSelectionOwner(m_display, m_clipboard, m_window, CurrentTime);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void ClipboardImpl::processEventsImpl()
|
||||
{
|
||||
XEvent event;
|
||||
|
||||
// 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);
|
||||
|
||||
// Handle the events for this window that we just picked out
|
||||
while (!m_events.empty())
|
||||
{
|
||||
event = m_events.front();
|
||||
m_events.pop_front();
|
||||
processEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void ClipboardImpl::processEvent(XEvent& windowEvent)
|
||||
{
|
||||
switch (windowEvent.type)
|
||||
{
|
||||
case SelectionClear:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
case SelectionNotify:
|
||||
{
|
||||
// Notification that the current selection owner
|
||||
// has responded to our request
|
||||
|
||||
XSelectionEvent& selectionEvent = *reinterpret_cast<XSelectionEvent*>(&windowEvent.xselection);
|
||||
|
||||
m_clipboardContents.clear();
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
m_requestResponded = true;
|
||||
|
||||
break;
|
||||
}
|
||||
case SelectionRequest:
|
||||
{
|
||||
// Respond to a request for our clipboard contents
|
||||
XSelectionRequestEvent& selectionRequestEvent = *reinterpret_cast<XSelectionRequestEvent*>(&windowEvent.xselectionrequest);
|
||||
|
||||
// Our reply
|
||||
XSelectionEvent selectionEvent;
|
||||
|
||||
selectionEvent.type = SelectionNotify;
|
||||
selectionEvent.requestor = selectionRequestEvent.requestor;
|
||||
selectionEvent.selection = selectionRequestEvent.selection;
|
||||
selectionEvent.property = selectionRequestEvent.property;
|
||||
selectionEvent.time = selectionRequestEvent.time;
|
||||
|
||||
if (selectionRequestEvent.selection == m_clipboard)
|
||||
{
|
||||
if (selectionRequestEvent.target == m_targets)
|
||||
{
|
||||
// Respond to a request for our valid conversion targets
|
||||
std::vector<Atom> targets;
|
||||
|
||||
targets.push_back(m_targets);
|
||||
targets.push_back(m_text);
|
||||
targets.push_back(XA_STRING);
|
||||
|
||||
if (m_utf8String != None)
|
||||
targets.push_back(m_utf8String);
|
||||
|
||||
XChangeProperty(
|
||||
m_display,
|
||||
selectionRequestEvent.requestor,
|
||||
selectionRequestEvent.property,
|
||||
XA_ATOM,
|
||||
32,
|
||||
PropModeReplace,
|
||||
reinterpret_cast<unsigned char*>(&targets[0]),
|
||||
targets.size()
|
||||
);
|
||||
|
||||
// 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()
|
||||
);
|
||||
|
||||
// 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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
@ -779,6 +780,9 @@ void WindowImplX11::processEvents()
|
||||
m_events.pop_front();
|
||||
processEvent(event);
|
||||
}
|
||||
|
||||
// Process clipboard window events
|
||||
priv::ClipboardImpl::processEvents();
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user