Implement X11 primary selection

This commit is contained in:
Desour 2022-08-23 17:19:23 +02:00 committed by sfan5
parent 1967d71cfb
commit 53b9eaa831
5 changed files with 128 additions and 32 deletions

@ -26,12 +26,23 @@ public:
} }
//! Copies text to the clipboard //! Copies text to the clipboard
//! \param text: text in utf-8
virtual void copyToClipboard(const c8* text) const = 0; virtual void copyToClipboard(const c8* text) const = 0;
//! Copies text to the primary selection
//! This is a no-op on some platforms.
//! \param text: text in utf-8
virtual void copyToPrimarySelection(const c8* text) const = 0;
//! Get text from the clipboard //! Get text from the clipboard
/** \return Returns 0 if no string is in there. */ //! \return Returns 0 if no string is in there, otherwise an utf-8 string.
virtual const c8* getTextFromClipboard() const = 0; virtual const c8* getTextFromClipboard() const = 0;
//! Get text from the primary selection
//! This is a no-op on some platforms.
//! \return Returns 0 if no string is in there, otherwise an utf-8 string.
virtual const c8* getTextFromPrimarySelection() const = 0;
//! Get the total and available system RAM //! Get the total and available system RAM
/** \param totalBytes: will contain the total system memory in Kilobytes (1024 B) /** \param totalBytes: will contain the total system memory in Kilobytes (1024 B)
\param availableBytes: will contain the available memory in Kilobytes (1024 B) \param availableBytes: will contain the available memory in Kilobytes (1024 B)

@ -1000,12 +1000,15 @@ bool CIrrDeviceLinux::run()
send_response(req->property); send_response(req->property);
}; };
if (req->selection != X_ATOM_CLIPBOARD || if ((req->selection != X_ATOM_CLIPBOARD &&
req->selection != XA_PRIMARY) ||
req->owner != XWindow) { req->owner != XWindow) {
// we are not the owner, refuse request // we are not the owner, refuse request
send_response_refuse(); send_response_refuse();
break; break;
} }
const core::stringc &text_buffer = req->selection == X_ATOM_CLIPBOARD ?
Clipboard : PrimarySelection;
// for debugging: // for debugging:
//~ { //~ {
@ -1026,8 +1029,8 @@ bool CIrrDeviceLinux::run()
req->target, X_ATOM_UTF8_STRING, req->target, X_ATOM_UTF8_STRING,
8, // format = 8-bit 8, // format = 8-bit
PropModeReplace, PropModeReplace,
(unsigned char *)Clipboard.c_str(), (unsigned char *)text_buffer.c_str(),
Clipboard.size()); text_buffer.size());
send_response(req->target); send_response(req->target);
break; break;
} }
@ -1052,8 +1055,8 @@ bool CIrrDeviceLinux::run()
set_property_and_notify( set_property_and_notify(
X_ATOM_UTF8_STRING, X_ATOM_UTF8_STRING,
8, 8,
Clipboard.c_str(), text_buffer.c_str(),
Clipboard.size() text_buffer.size()
); );
} else { } else {
@ -1676,47 +1679,49 @@ void CIrrDeviceLinux::pollJoysticks()
} }
#if defined(_IRR_COMPILE_WITH_X11_)
//! gets text from the clipboard //! gets text from the clipboard
//! \return Returns 0 if no string is in there, otherwise utf-8 text. //! \return Returns 0 if no string is in there, otherwise utf-8 text.
const c8 *CIrrDeviceLinux::getTextFromClipboard() const const c8 *CIrrDeviceLinux::getTextFromSelection(Atom selection, core::stringc &text_buffer) const
{ {
#if defined(_IRR_COMPILE_WITH_X11_) Window ownerWindow = XGetSelectionOwner(XDisplay, selection);
Window ownerWindow = XGetSelectionOwner(XDisplay, X_ATOM_CLIPBOARD);
if (ownerWindow == XWindow) { if (ownerWindow == XWindow) {
return Clipboard.c_str(); return text_buffer.c_str();
} }
Clipboard = ""; text_buffer = "";
if (ownerWindow == None) { if (ownerWindow == None) {
return Clipboard.c_str(); return text_buffer.c_str();
} }
// delete the property to be set beforehand // delete the property to be set beforehand
XDeleteProperty(XDisplay, XWindow, XA_PRIMARY); XDeleteProperty(XDisplay, XWindow, XA_PRIMARY);
XConvertSelection(XDisplay, X_ATOM_CLIPBOARD, X_ATOM_UTF8_STRING, XA_PRIMARY, XConvertSelection(XDisplay, selection, X_ATOM_UTF8_STRING, XA_PRIMARY,
XWindow, CurrentTime); XWindow, CurrentTime);
XFlush(XDisplay); XFlush(XDisplay);
// wait for event via a blocking call // wait for event via a blocking call
XEvent event_ret; XEvent event_ret;
std::pair<Window, Atom> args(XWindow, selection);
XIfEvent(XDisplay, &event_ret, [](Display *_display, XEvent *event, XPointer arg) { XIfEvent(XDisplay, &event_ret, [](Display *_display, XEvent *event, XPointer arg) {
auto p = reinterpret_cast<std::pair<Window, Atom> *>(arg);
return (Bool) (event->type == SelectionNotify && return (Bool) (event->type == SelectionNotify &&
event->xselection.requestor == *(Window *)arg && event->xselection.requestor == p->first &&
event->xselection.selection == X_ATOM_CLIPBOARD && event->xselection.selection == p->second &&
event->xselection.target == X_ATOM_UTF8_STRING); event->xselection.target == X_ATOM_UTF8_STRING);
}, (XPointer)&XWindow); }, (XPointer)&args);
_IRR_DEBUG_BREAK_IF(!(event_ret.type == SelectionNotify && _IRR_DEBUG_BREAK_IF(!(event_ret.type == SelectionNotify &&
event_ret.xselection.requestor == XWindow && event_ret.xselection.requestor == XWindow &&
event_ret.xselection.selection == X_ATOM_CLIPBOARD && event_ret.xselection.selection == selection &&
event_ret.xselection.target == X_ATOM_UTF8_STRING)); event_ret.xselection.target == X_ATOM_UTF8_STRING));
Atom property_set = event_ret.xselection.property; Atom property_set = event_ret.xselection.property;
if (event_ret.xselection.property == None) { if (event_ret.xselection.property == None) {
// request failed => empty string // request failed => empty string
return Clipboard.c_str(); return text_buffer.c_str();
} }
// check for data // check for data
@ -1743,15 +1748,15 @@ const c8 *CIrrDeviceLinux::getTextFromClipboard() const
// for debugging: // for debugging:
//~ { //~ {
//~ char *type_name = XGetAtomName(XDisplay, type); //~ char *type_name = XGetAtomName(XDisplay, type);
//~ fprintf(stderr, "CIrrDeviceLinux::getTextFromClipboard: actual type: %s (=%ld)\n", //~ fprintf(stderr, "CIrrDeviceLinux::getTextFromSelection: actual type: %s (=%ld)\n",
//~ type_name, type); //~ type_name, type);
//~ XFree(type_name); //~ XFree(type_name);
//~ } //~ }
if (type != X_ATOM_UTF8_STRING && type != X_ATOM_UTF8_MIME_TYPE) { if (type != X_ATOM_UTF8_STRING && type != X_ATOM_UTF8_MIME_TYPE) {
os::Printer::log("CIrrDeviceLinux::getTextFromClipboard: did not get utf-8 string", os::Printer::log("CIrrDeviceLinux::getTextFromSelection: did not get utf-8 string",
ELL_WARNING); ELL_WARNING);
return Clipboard.c_str(); return text_buffer.c_str();
} }
if (bytesLeft > 0) { if (bytesLeft > 0) {
@ -1760,20 +1765,49 @@ const c8 *CIrrDeviceLinux::getTextFromClipboard() const
bytesLeft, 0, AnyPropertyType, &type, &format, bytesLeft, 0, AnyPropertyType, &type, &format,
&numItems, &dummy, &data); &numItems, &dummy, &data);
if (result == Success) if (result == Success)
Clipboard = (irr::c8 *)data; text_buffer = (irr::c8 *)data;
XFree (data); XFree (data);
} }
// delete the property again, to inform the owner about the successful transfer // delete the property again, to inform the owner about the successful transfer
XDeleteProperty(XDisplay, XWindow, property_set); XDeleteProperty(XDisplay, XWindow, property_set);
return Clipboard.c_str(); return text_buffer.c_str();
}
#endif
//! gets text from the clipboard
//! \return Returns 0 if no string is in there, otherwise utf-8 text.
const c8 *CIrrDeviceLinux::getTextFromClipboard() const
{
#if defined(_IRR_COMPILE_WITH_X11_)
return getTextFromSelection(X_ATOM_CLIPBOARD, Clipboard);
#else #else
return nullptr; return nullptr;
#endif #endif
} }
//! gets text from the primary selection
//! \return Returns 0 if no string is in there, otherwise utf-8 text.
const c8 *CIrrDeviceLinux::getTextFromPrimarySelection() const
{
#if defined(_IRR_COMPILE_WITH_X11_)
return getTextFromSelection(XA_PRIMARY, PrimarySelection);
#else
return nullptr;
#endif
}
#if defined(_IRR_COMPILE_WITH_X11_)
bool CIrrDeviceLinux::becomeSelectionOwner(Atom selection) const
{
XSetSelectionOwner (XDisplay, selection, XWindow, CurrentTime);
XFlush (XDisplay);
Window owner = XGetSelectionOwner(XDisplay, selection);
return owner == XWindow;
}
#endif
//! copies text to the clipboard //! copies text to the clipboard
void CIrrDeviceLinux::copyToClipboard(const c8 *text) const void CIrrDeviceLinux::copyToClipboard(const c8 *text) const
{ {
@ -1781,15 +1815,23 @@ void CIrrDeviceLinux::copyToClipboard(const c8 *text) const
// Actually there is no clipboard on X but applications just say they own the clipboard and return text when asked. // Actually there is no clipboard on X but applications just say they own the clipboard and return text when asked.
// Which btw. also means that on X you lose clipboard content when closing applications. // Which btw. also means that on X you lose clipboard content when closing applications.
Clipboard = text; Clipboard = text;
XSetSelectionOwner (XDisplay, X_ATOM_CLIPBOARD, XWindow, CurrentTime); if (!becomeSelectionOwner(X_ATOM_CLIPBOARD)) {
XFlush (XDisplay);
Window owner = XGetSelectionOwner(XDisplay, X_ATOM_CLIPBOARD);
if (owner != XWindow) {
os::Printer::log("CIrrDeviceLinux::copyToClipboard: failed to set owner", ELL_WARNING); os::Printer::log("CIrrDeviceLinux::copyToClipboard: failed to set owner", ELL_WARNING);
} }
#endif #endif
} }
//! copies text to the primary selection
void CIrrDeviceLinux::copyToPrimarySelection(const c8 *text) const
{
#if defined(_IRR_COMPILE_WITH_X11_)
PrimarySelection = text;
if (!becomeSelectionOwner(XA_PRIMARY)) {
os::Printer::log("CIrrDeviceLinux::copyToPrimarySelection: failed to set owner", ELL_WARNING);
}
#endif
}
#ifdef _IRR_COMPILE_WITH_X11_ #ifdef _IRR_COMPILE_WITH_X11_
// return true if the passed event has the type passed in parameter arg // return true if the passed event has the type passed in parameter arg
Bool PredicateIsEventType(Display *display, XEvent *event, XPointer arg) Bool PredicateIsEventType(Display *display, XEvent *event, XPointer arg)

@ -97,11 +97,20 @@ namespace irr
//! \return Returns 0 if no string is in there, otherwise utf-8 text. //! \return Returns 0 if no string is in there, otherwise utf-8 text.
virtual const c8 *getTextFromClipboard() const; virtual const c8 *getTextFromClipboard() const;
//! gets text from the primary selection
//! \return Returns 0 if no string is in there, otherwise utf-8 text.
virtual const c8 *getTextFromPrimarySelection() const;
//! copies text to the clipboard //! copies text to the clipboard
//! This sets the clipboard selection and _not_ the primary selection which you have on X on the middle mouse button. //! This sets the clipboard selection and _not_ the primary selection.
//! @param text The text in utf-8 //! @param text The text in utf-8
virtual void copyToClipboard(const c8 *text) const; virtual void copyToClipboard(const c8 *text) const;
//! copies text to the primary selection
//! This sets the primary selection which you have on X on the middle mouse button.
//! @param text The text in utf-8
virtual void copyToPrimarySelection(const c8 *text) const;
//! Remove all messages pending in the system message loop //! Remove all messages pending in the system message loop
void clearSystemMessages() override; void clearSystemMessages() override;
@ -141,6 +150,9 @@ namespace irr
bool createInputContext(); bool createInputContext();
void destroyInputContext(); void destroyInputContext();
EKEY_CODE getKeyCode(XEvent &event); EKEY_CODE getKeyCode(XEvent &event);
const c8 *getTextFromSelection(Atom selection, core::stringc &text_buffer) const;
bool becomeSelectionOwner(Atom selection) const;
#endif #endif
//! Implementation of the linux cursor control //! Implementation of the linux cursor control
@ -413,6 +425,7 @@ namespace irr
bool HasNetWM; bool HasNetWM;
// text is utf-8 // text is utf-8
mutable core::stringc Clipboard; mutable core::stringc Clipboard;
mutable core::stringc PrimarySelection;
#endif #endif
#if defined(_IRR_LINUX_X11_XINPUT2_) #if defined(_IRR_LINUX_X11_XINPUT2_)
int currentTouchedCount; int currentTouchedCount;

@ -54,7 +54,6 @@ const core::stringc& COSOperator::getOperatingSystemVersion() const
//! copies text to the clipboard //! copies text to the clipboard
//! \param text: text in utf-8
void COSOperator::copyToClipboard(const c8 *text) const void COSOperator::copyToClipboard(const c8 *text) const
{ {
if (strlen(text)==0) if (strlen(text)==0)
@ -102,8 +101,20 @@ void COSOperator::copyToClipboard(const c8 *text) const
} }
//! copies text to the primary selection
void COSOperator::copyToPrimarySelection(const c8 *text) const
{
if (strlen(text)==0)
return;
#if defined(_IRR_COMPILE_WITH_X11_DEVICE_)
if ( IrrDeviceLinux )
IrrDeviceLinux->copyToPrimarySelection(text);
#endif
}
//! gets text from the clipboard //! gets text from the clipboard
//! \return Returns 0 if no string is in there, otherwise an utf-8 string.
const c8* COSOperator::getTextFromClipboard() const const c8* COSOperator::getTextFromClipboard() const
{ {
#if defined(_IRR_WINDOWS_API_) #if defined(_IRR_WINDOWS_API_)
@ -147,6 +158,21 @@ const c8* COSOperator::getTextFromClipboard() const
} }
//! gets text from the primary selection
const c8* COSOperator::getTextFromPrimarySelection() const
{
#if defined(_IRR_COMPILE_WITH_X11_DEVICE_)
if ( IrrDeviceLinux )
return IrrDeviceLinux->getTextFromPrimarySelection();
return 0;
#else
return 0;
#endif
}
bool COSOperator::getSystemMemory(u32* Total, u32* Avail) const bool COSOperator::getSystemMemory(u32* Total, u32* Avail) const
{ {
#if defined(_IRR_WINDOWS_API_) #if defined(_IRR_WINDOWS_API_)

@ -27,13 +27,17 @@ public:
const core::stringc& getOperatingSystemVersion() const override; const core::stringc& getOperatingSystemVersion() const override;
//! copies text to the clipboard //! copies text to the clipboard
//! \param text: text in utf-8
void copyToClipboard(const c8 *text) const override; void copyToClipboard(const c8 *text) const override;
//! copies text to the primary selection
void copyToPrimarySelection(const c8 *text) const override;
//! gets text from the clipboard //! gets text from the clipboard
//! \return Returns 0 if no string is in there, otherwise an utf-8 string.
const c8* getTextFromClipboard() const override; const c8* getTextFromClipboard() const override;
//! gets text from the primary selection
const c8* getTextFromPrimarySelection() const override;
//! gets the total and available system RAM in kB //! gets the total and available system RAM in kB
//! \param Total: will contain the total system memory //! \param Total: will contain the total system memory
//! \param Avail: will contain the available memory //! \param Avail: will contain the available memory