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,6 +60,11 @@ public:
|
|||||||
/// This function sets the content of the clipboard as a
|
/// This function sets the content of the clipboard as a
|
||||||
/// string.
|
/// string.
|
||||||
///
|
///
|
||||||
|
/// \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
|
/// \param text sf::String containing the data to be sent
|
||||||
/// to the clipboard
|
/// to the clipboard
|
||||||
///
|
///
|
||||||
@ -80,6 +85,11 @@ public:
|
|||||||
/// sf::Clipboard provides an interface for getting and
|
/// sf::Clipboard provides an interface for getting and
|
||||||
/// setting the contents of the system clipboard.
|
/// 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:
|
/// Usage example:
|
||||||
/// \code
|
/// \code
|
||||||
/// // get the clipboard content as a string
|
/// // get the clipboard content as a string
|
||||||
@ -96,11 +106,12 @@ public:
|
|||||||
/// // Using Ctrl + V to paste a string into SFML
|
/// // Using Ctrl + V to paste a string into SFML
|
||||||
/// if(event.key.control && event.key.code == sf::Keyboard::V)
|
/// if(event.key.control && event.key.code == sf::Keyboard::V)
|
||||||
/// string = sf::Clipboard::getString();
|
/// string = sf::Clipboard::getString();
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
///
|
||||||
/// // set the clipboard to a string
|
/// // 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!");
|
/// sf::Clipboard::setString("Hello World!");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
/// \endcode
|
/// \endcode
|
||||||
///
|
///
|
||||||
/// \see sf::String, sf::Event
|
/// \see sf::String, sf::Event
|
||||||
|
@ -27,142 +27,19 @@
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
#include <SFML/Window/Unix/ClipboardImpl.hpp>
|
#include <SFML/Window/Unix/ClipboardImpl.hpp>
|
||||||
#include <SFML/Window/Unix/Display.hpp>
|
#include <SFML/Window/Unix/Display.hpp>
|
||||||
#include <SFML/System/String.hpp>
|
#include <SFML/System/Clock.hpp>
|
||||||
#include <SFML/System/Sleep.hpp>
|
#include <SFML/System/Err.hpp>
|
||||||
#include <iostream>
|
#include <X11/Xatom.h>
|
||||||
#include <string>
|
#include <vector>
|
||||||
#include <X11/Xlib.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////
|
// Filter the events received by windows (only allow those matching a specific window)
|
||||||
void initClipboard();
|
Bool checkEvent(::Display*, XEvent* event, XPointer userData)
|
||||||
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;
|
// Just check if the event matches the window
|
||||||
|
return event->xany.window == reinterpret_cast< ::Window >(userData);
|
||||||
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)
|
|
||||||
{
|
|
||||||
std::cerr << "UTF-8 format unavailable on clipboard." << std::endl;
|
|
||||||
utf8_text = xa_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,86 +51,332 @@ namespace priv
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
String ClipboardImpl::getString()
|
String ClipboardImpl::getString()
|
||||||
{
|
{
|
||||||
if(!is_init)
|
return getInstance().getStringImpl();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void ClipboardImpl::setString(const String& text)
|
void ClipboardImpl::setString(const String& text)
|
||||||
{
|
{
|
||||||
if(!is_init)
|
getInstance().setStringImpl(text);
|
||||||
initClipboard();
|
|
||||||
|
|
||||||
if(is_fail)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(!is_host)
|
|
||||||
{
|
|
||||||
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);
|
void ClipboardImpl::processEvents()
|
||||||
|
{
|
||||||
|
getInstance().processEventsImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&mutex);
|
|
||||||
string = text;
|
////////////////////////////////////////////////////////////
|
||||||
pthread_mutex_unlock(&mutex);
|
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)
|
||||||
|
{
|
||||||
|
XDestroyWindow(m_display, m_window);
|
||||||
|
XFlush(m_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
} // namespace priv
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
// Headers
|
// Headers
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
#include <SFML/System/String.hpp>
|
#include <SFML/System/String.hpp>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
|
||||||
namespace sf
|
namespace sf
|
||||||
@ -67,6 +69,82 @@ public:
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
static void setString(const String& text);
|
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
|
} // namespace priv
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
// Headers
|
// Headers
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
#include <SFML/Window/Unix/WindowImplX11.hpp>
|
#include <SFML/Window/Unix/WindowImplX11.hpp>
|
||||||
|
#include <SFML/Window/Unix/ClipboardImpl.hpp>
|
||||||
#include <SFML/Window/Unix/Display.hpp>
|
#include <SFML/Window/Unix/Display.hpp>
|
||||||
#include <SFML/Window/Unix/InputImpl.hpp>
|
#include <SFML/Window/Unix/InputImpl.hpp>
|
||||||
#include <SFML/System/Utf.hpp>
|
#include <SFML/System/Utf.hpp>
|
||||||
@ -779,6 +780,9 @@ void WindowImplX11::processEvents()
|
|||||||
m_events.pop_front();
|
m_events.pop_front();
|
||||||
processEvent(event);
|
processEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process clipboard window events
|
||||||
|
priv::ClipboardImpl::processEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user