Merge pull request #42685 from nekomatata/x11-clipboard-fixes-3.2
[3.2] Fix issues with Linux clipboard
This commit is contained in:
commit
797850321b
|
@ -813,7 +813,7 @@ void OS_X11::finalize() {
|
||||||
events_thread_done = true;
|
events_thread_done = true;
|
||||||
Thread::wait_to_finish(events_thread);
|
Thread::wait_to_finish(events_thread);
|
||||||
memdelete(events_thread);
|
memdelete(events_thread);
|
||||||
events_thread = nullptr;
|
events_thread = NULL;
|
||||||
|
|
||||||
if (main_loop)
|
if (main_loop)
|
||||||
memdelete(main_loop);
|
memdelete(main_loop);
|
||||||
|
@ -2028,53 +2028,106 @@ void OS_X11::_handle_key_event(XKeyEvent *p_event, LocalVector<XEvent> &p_events
|
||||||
input->accumulate_input_event(k);
|
input->accumulate_input_event(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_X11::_handle_selection_request_event(XSelectionRequestEvent *p_event) {
|
Atom OS_X11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const {
|
||||||
XEvent respond;
|
if (p_target == XInternAtom(x11_display, "TARGETS", 0)) {
|
||||||
if (p_event->target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
|
// Request to list all supported targets.
|
||||||
p_event->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
|
Atom data[9];
|
||||||
p_event->target == XInternAtom(x11_display, "TEXT", 0) ||
|
|
||||||
p_event->target == XA_STRING ||
|
|
||||||
p_event->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
|
|
||||||
p_event->target == XInternAtom(x11_display, "text/plain", 0)) {
|
|
||||||
// Directly using internal clipboard because we know our window
|
|
||||||
// is the owner during a selection request.
|
|
||||||
CharString clip = OS::get_clipboard().utf8();
|
|
||||||
XChangeProperty(x11_display,
|
|
||||||
p_event->requestor,
|
|
||||||
p_event->property,
|
|
||||||
p_event->target,
|
|
||||||
8,
|
|
||||||
PropModeReplace,
|
|
||||||
(unsigned char *)clip.get_data(),
|
|
||||||
clip.length());
|
|
||||||
respond.xselection.property = p_event->property;
|
|
||||||
} else if (p_event->target == XInternAtom(x11_display, "TARGETS", 0)) {
|
|
||||||
Atom data[7];
|
|
||||||
data[0] = XInternAtom(x11_display, "TARGETS", 0);
|
data[0] = XInternAtom(x11_display, "TARGETS", 0);
|
||||||
data[1] = XInternAtom(x11_display, "UTF8_STRING", 0);
|
data[1] = XInternAtom(x11_display, "SAVE_TARGETS", 0);
|
||||||
data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
|
data[2] = XInternAtom(x11_display, "MULTIPLE", 0);
|
||||||
data[3] = XInternAtom(x11_display, "TEXT", 0);
|
data[3] = XInternAtom(x11_display, "UTF8_STRING", 0);
|
||||||
data[4] = XA_STRING;
|
data[4] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
|
||||||
data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
|
data[5] = XInternAtom(x11_display, "TEXT", 0);
|
||||||
data[6] = XInternAtom(x11_display, "text/plain", 0);
|
data[6] = XA_STRING;
|
||||||
|
data[7] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
|
||||||
|
data[8] = XInternAtom(x11_display, "text/plain", 0);
|
||||||
|
|
||||||
XChangeProperty(x11_display,
|
XChangeProperty(x11_display,
|
||||||
p_event->requestor,
|
p_requestor,
|
||||||
p_event->property,
|
p_property,
|
||||||
XA_ATOM,
|
XA_ATOM,
|
||||||
32,
|
32,
|
||||||
PropModeReplace,
|
PropModeReplace,
|
||||||
(unsigned char *)&data,
|
(unsigned char *)&data,
|
||||||
sizeof(data) / sizeof(data[0]));
|
sizeof(data) / sizeof(data[0]));
|
||||||
respond.xselection.property = p_event->property;
|
|
||||||
|
|
||||||
|
return p_property;
|
||||||
|
} else if (p_target == XInternAtom(x11_display, "SAVE_TARGETS", 0)) {
|
||||||
|
// Request to check if SAVE_TARGETS is supported, nothing special to do.
|
||||||
|
XChangeProperty(x11_display,
|
||||||
|
p_requestor,
|
||||||
|
p_property,
|
||||||
|
XInternAtom(x11_display, "NULL", False),
|
||||||
|
32,
|
||||||
|
PropModeReplace,
|
||||||
|
NULL,
|
||||||
|
0);
|
||||||
|
return p_property;
|
||||||
|
} else if (p_target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
|
||||||
|
p_target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
|
||||||
|
p_target == XInternAtom(x11_display, "TEXT", 0) ||
|
||||||
|
p_target == XA_STRING ||
|
||||||
|
p_target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
|
||||||
|
p_target == XInternAtom(x11_display, "text/plain", 0)) {
|
||||||
|
// Directly using internal clipboard because we know our window
|
||||||
|
// is the owner during a selection request.
|
||||||
|
CharString clip = OS::get_clipboard().utf8();
|
||||||
|
XChangeProperty(x11_display,
|
||||||
|
p_requestor,
|
||||||
|
p_property,
|
||||||
|
p_target,
|
||||||
|
8,
|
||||||
|
PropModeReplace,
|
||||||
|
(unsigned char *)clip.get_data(),
|
||||||
|
clip.length());
|
||||||
|
return p_property;
|
||||||
} else {
|
} else {
|
||||||
char *targetname = XGetAtomName(x11_display, p_event->target);
|
char *target_name = XGetAtomName(x11_display, p_target);
|
||||||
printf("No Target '%s'\n", targetname);
|
printf("Target '%s' not supported.\n", target_name);
|
||||||
if (targetname) {
|
if (target_name) {
|
||||||
XFree(targetname);
|
XFree(target_name);
|
||||||
}
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OS_X11::_handle_selection_request_event(XSelectionRequestEvent *p_event) const {
|
||||||
|
XEvent respond;
|
||||||
|
if (p_event->target == XInternAtom(x11_display, "MULTIPLE", 0)) {
|
||||||
|
// Request for multiple target conversions at once.
|
||||||
|
Atom atom_pair = XInternAtom(x11_display, "ATOM_PAIR", False);
|
||||||
respond.xselection.property = None;
|
respond.xselection.property = None;
|
||||||
|
|
||||||
|
Atom type;
|
||||||
|
int format;
|
||||||
|
unsigned long len;
|
||||||
|
unsigned long remaining;
|
||||||
|
unsigned char *data = NULL;
|
||||||
|
if (XGetWindowProperty(x11_display, p_event->requestor, p_event->property, 0, LONG_MAX, False, atom_pair, &type, &format, &len, &remaining, &data) == Success) {
|
||||||
|
if ((len >= 2) && data) {
|
||||||
|
Atom *targets = (Atom *)data;
|
||||||
|
for (uint64_t i = 0; i < len; i += 2) {
|
||||||
|
Atom target = targets[i];
|
||||||
|
Atom &property = targets[i + 1];
|
||||||
|
property = _process_selection_request_target(target, p_event->requestor, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
XChangeProperty(x11_display,
|
||||||
|
p_event->requestor,
|
||||||
|
p_event->property,
|
||||||
|
atom_pair,
|
||||||
|
32,
|
||||||
|
PropModeReplace,
|
||||||
|
(unsigned char *)targets,
|
||||||
|
len);
|
||||||
|
|
||||||
|
respond.xselection.property = p_event->property;
|
||||||
|
}
|
||||||
|
XFree(data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Request for target conversion.
|
||||||
|
respond.xselection.property = _process_selection_request_target(p_event->target, p_event->requestor, p_event->property);
|
||||||
}
|
}
|
||||||
|
|
||||||
respond.xselection.type = SelectionNotify;
|
respond.xselection.type = SelectionNotify;
|
||||||
|
@ -2176,32 +2229,43 @@ Bool OS_X11::_predicate_all_events(Display *display, XEvent *event, XPointer arg
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_X11::_poll_events() {
|
bool OS_X11::_wait_for_events() const {
|
||||||
int x11_fd = ConnectionNumber(x11_display);
|
int x11_fd = ConnectionNumber(x11_display);
|
||||||
fd_set in_fds;
|
fd_set in_fds;
|
||||||
|
|
||||||
while (!events_thread_done) {
|
XFlush(x11_display);
|
||||||
XFlush(x11_display);
|
|
||||||
|
|
||||||
FD_ZERO(&in_fds);
|
FD_ZERO(&in_fds);
|
||||||
FD_SET(x11_fd, &in_fds);
|
FD_SET(x11_fd, &in_fds);
|
||||||
|
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
tv.tv_sec = 1;
|
tv.tv_sec = 1;
|
||||||
|
|
||||||
// Wait for next event or timeout.
|
// Wait for next event or timeout.
|
||||||
int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
|
int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
|
||||||
|
|
||||||
|
if (num_ready_fds > 0) {
|
||||||
|
// Event received.
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Error or timeout.
|
||||||
if (num_ready_fds < 0) {
|
if (num_ready_fds < 0) {
|
||||||
ERR_PRINT("_poll_events: select error: " + itos(errno));
|
ERR_PRINT("_wait_for_events: select error: " + itos(errno));
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OS_X11::_poll_events() {
|
||||||
|
while (!events_thread_done) {
|
||||||
|
_wait_for_events();
|
||||||
|
|
||||||
// Process events from the queue.
|
// Process events from the queue.
|
||||||
{
|
{
|
||||||
MutexLock mutex_lock(events_mutex);
|
MutexLock mutex_lock(events_mutex);
|
||||||
|
|
||||||
// Non-blocking wait for next event
|
// Non-blocking wait for next event and remove it from the queue.
|
||||||
// and remove it from the queue.
|
|
||||||
XEvent ev;
|
XEvent ev;
|
||||||
while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, NULL)) {
|
while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, NULL)) {
|
||||||
// Check if the input manager wants to process the event.
|
// Check if the input manager wants to process the event.
|
||||||
|
@ -2806,6 +2870,10 @@ MainLoop *OS_X11::get_main_loop() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_X11::delete_main_loop() {
|
void OS_X11::delete_main_loop() {
|
||||||
|
// Send owned clipboard data to clipboard manager before exit.
|
||||||
|
// This has to be done here because the clipboard data is cleared before finalize().
|
||||||
|
_clipboard_transfer_ownership(XA_PRIMARY, x11_window);
|
||||||
|
_clipboard_transfer_ownership(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window);
|
||||||
|
|
||||||
if (main_loop)
|
if (main_loop)
|
||||||
memdelete(main_loop);
|
memdelete(main_loop);
|
||||||
|
@ -2842,60 +2910,137 @@ Bool OS_X11::_predicate_clipboard_selection(Display *display, XEvent *event, XPo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String OS_X11::_get_clipboard_impl(Atom p_source, Window x11_window, Atom target) const {
|
Bool OS_X11::_predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg) {
|
||||||
|
if (event->type == PropertyNotify && event->xproperty.state == PropertyNewValue) {
|
||||||
|
return True;
|
||||||
|
} else {
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String OS_X11::_get_clipboard_impl(Atom p_source, Window x11_window, Atom target) const {
|
||||||
String ret;
|
String ret;
|
||||||
|
|
||||||
Atom type;
|
|
||||||
Atom selection = XA_PRIMARY;
|
|
||||||
int format, result;
|
|
||||||
unsigned long len, bytes_left, dummy;
|
|
||||||
unsigned char *data;
|
|
||||||
Window selection_owner = XGetSelectionOwner(x11_display, p_source);
|
Window selection_owner = XGetSelectionOwner(x11_display, p_source);
|
||||||
|
|
||||||
if (selection_owner == x11_window) {
|
if (selection_owner == x11_window) {
|
||||||
|
|
||||||
return OS::get_clipboard();
|
return OS::get_clipboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selection_owner != None) {
|
if (selection_owner != None) {
|
||||||
{
|
// Block events polling while processing selection events.
|
||||||
// Block events polling while processing selection events.
|
MutexLock mutex_lock(events_mutex);
|
||||||
MutexLock mutex_lock(events_mutex);
|
|
||||||
|
|
||||||
XConvertSelection(x11_display, p_source, target, selection,
|
Atom selection = XA_PRIMARY;
|
||||||
x11_window, CurrentTime);
|
XConvertSelection(x11_display, p_source, target, selection,
|
||||||
XFlush(x11_display);
|
x11_window, CurrentTime);
|
||||||
|
|
||||||
// Blocking wait for predicate to be True
|
XFlush(x11_display);
|
||||||
// and remove the event from the queue.
|
|
||||||
XEvent event;
|
|
||||||
XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
// Blocking wait for predicate to be True and remove the event from the queue.
|
||||||
// Do not get any data, see how much data is there
|
XEvent event;
|
||||||
//
|
XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window);
|
||||||
|
|
||||||
|
// Do not get any data, see how much data is there.
|
||||||
|
Atom type;
|
||||||
|
int format, result;
|
||||||
|
unsigned long len, bytes_left, dummy;
|
||||||
|
unsigned char *data;
|
||||||
XGetWindowProperty(x11_display, x11_window,
|
XGetWindowProperty(x11_display, x11_window,
|
||||||
selection, // Tricky..
|
selection, // Tricky..
|
||||||
0, 0, // offset - len
|
0, 0, // offset - len
|
||||||
0, // Delete 0==FALSE
|
0, // Delete 0==FALSE
|
||||||
AnyPropertyType, //flag
|
AnyPropertyType, // flag
|
||||||
&type, // return type
|
&type, // return type
|
||||||
&format, // return format
|
&format, // return format
|
||||||
&len, &bytes_left, //that
|
&len, &bytes_left, // data length
|
||||||
&data);
|
&data);
|
||||||
// DATA is There
|
|
||||||
if (bytes_left > 0) {
|
if (data) {
|
||||||
|
XFree(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == XInternAtom(x11_display, "INCR", 0)) {
|
||||||
|
// Data is going to be received incrementally.
|
||||||
|
LocalVector<uint8_t> incr_data;
|
||||||
|
uint32_t data_size = 0;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
// Delete INCR property to notify the owner.
|
||||||
|
XDeleteProperty(x11_display, x11_window, type);
|
||||||
|
|
||||||
|
// Process events from the queue.
|
||||||
|
bool done = false;
|
||||||
|
while (!done) {
|
||||||
|
if (!_wait_for_events()) {
|
||||||
|
// Error or timeout, abort.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-blocking wait for next event and remove it from the queue.
|
||||||
|
XEvent ev;
|
||||||
|
while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, NULL)) {
|
||||||
|
result = XGetWindowProperty(x11_display, x11_window,
|
||||||
|
selection, // selection type
|
||||||
|
0, LONG_MAX, // offset - len
|
||||||
|
True, // delete property to notify the owner
|
||||||
|
AnyPropertyType, // flag
|
||||||
|
&type, // return type
|
||||||
|
&format, // return format
|
||||||
|
&len, &bytes_left, // data length
|
||||||
|
&data);
|
||||||
|
|
||||||
|
if (result == Success) {
|
||||||
|
if (data && (len > 0)) {
|
||||||
|
uint32_t prev_size = incr_data.size();
|
||||||
|
if (prev_size == 0) {
|
||||||
|
// First property contains initial data size.
|
||||||
|
unsigned long initial_size = *(unsigned long *)data;
|
||||||
|
incr_data.resize(initial_size);
|
||||||
|
} else {
|
||||||
|
// New chunk, resize to be safe and append data.
|
||||||
|
incr_data.resize(MAX(data_size + len, prev_size));
|
||||||
|
memcpy(incr_data.ptr() + data_size, data, len);
|
||||||
|
data_size += len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Last chunk, process finished.
|
||||||
|
done = true;
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Failed to get selection data chunk.\n");
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
XFree(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success && (data_size > 0)) {
|
||||||
|
ret.parse_utf8((const char *)incr_data.ptr(), data_size);
|
||||||
|
}
|
||||||
|
} else if (bytes_left > 0) {
|
||||||
|
// Data is ready and can be processed all at once.
|
||||||
result = XGetWindowProperty(x11_display, x11_window,
|
result = XGetWindowProperty(x11_display, x11_window,
|
||||||
selection, 0, bytes_left, 0,
|
selection, 0, bytes_left, 0,
|
||||||
AnyPropertyType, &type, &format,
|
AnyPropertyType, &type, &format,
|
||||||
&len, &dummy, &data);
|
&len, &dummy, &data);
|
||||||
|
|
||||||
if (result == Success) {
|
if (result == Success) {
|
||||||
ret.parse_utf8((const char *)data);
|
ret.parse_utf8((const char *)data);
|
||||||
} else
|
} else {
|
||||||
printf("FAIL\n");
|
printf("Failed to get selection data.\n");
|
||||||
XFree(data);
|
}
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
XFree(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2926,6 +3071,58 @@ String OS_X11::get_clipboard() const {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool OS_X11::_predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg) {
|
||||||
|
if (event->xany.window == *(Window *)arg) {
|
||||||
|
return (event->type == SelectionRequest) ||
|
||||||
|
(event->type == SelectionNotify);
|
||||||
|
} else {
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OS_X11::_clipboard_transfer_ownership(Atom p_source, Window x11_window) const {
|
||||||
|
Window selection_owner = XGetSelectionOwner(x11_display, p_source);
|
||||||
|
|
||||||
|
if (selection_owner != x11_window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block events polling while processing selection events.
|
||||||
|
MutexLock mutex_lock(events_mutex);
|
||||||
|
|
||||||
|
Atom clipboard_manager = XInternAtom(x11_display, "CLIPBOARD_MANAGER", False);
|
||||||
|
Atom save_targets = XInternAtom(x11_display, "SAVE_TARGETS", False);
|
||||||
|
XConvertSelection(x11_display, clipboard_manager, save_targets, None,
|
||||||
|
x11_window, CurrentTime);
|
||||||
|
|
||||||
|
// Process events from the queue.
|
||||||
|
while (true) {
|
||||||
|
if (!_wait_for_events()) {
|
||||||
|
// Error or timeout, abort.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-blocking wait for next event and remove it from the queue.
|
||||||
|
XEvent ev;
|
||||||
|
while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_save_targets, (XPointer)&x11_window)) {
|
||||||
|
switch (ev.type) {
|
||||||
|
case SelectionRequest:
|
||||||
|
_handle_selection_request_event(&(ev.xselectionrequest));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SelectionNotify: {
|
||||||
|
if (ev.xselection.target == save_targets) {
|
||||||
|
// Once SelectionNotify is received, we're done whether it succeeded or not.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String OS_X11::get_name() const {
|
String OS_X11::get_name() const {
|
||||||
|
|
||||||
return "X11";
|
return "X11";
|
||||||
|
|
|
@ -157,20 +157,26 @@ class OS_X11 : public OS_Unix {
|
||||||
Point2i center;
|
Point2i center;
|
||||||
|
|
||||||
void _handle_key_event(XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo = false);
|
void _handle_key_event(XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo = false);
|
||||||
void _handle_selection_request_event(XSelectionRequestEvent *p_event);
|
|
||||||
|
Atom _process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const;
|
||||||
|
void _handle_selection_request_event(XSelectionRequestEvent *p_event) const;
|
||||||
|
|
||||||
String _get_clipboard_impl(Atom p_source, Window x11_window, Atom target) const;
|
String _get_clipboard_impl(Atom p_source, Window x11_window, Atom target) const;
|
||||||
String _get_clipboard(Atom p_source, Window x11_window) const;
|
String _get_clipboard(Atom p_source, Window x11_window) const;
|
||||||
|
void _clipboard_transfer_ownership(Atom p_source, Window x11_window) const;
|
||||||
|
|
||||||
mutable Mutex *events_mutex;
|
mutable Mutex *events_mutex;
|
||||||
Thread *events_thread = nullptr;
|
Thread *events_thread = nullptr;
|
||||||
bool events_thread_done = false;
|
bool events_thread_done = false;
|
||||||
LocalVector<XEvent> polled_events;
|
LocalVector<XEvent> polled_events;
|
||||||
static void _poll_events_thread(void *ud);
|
static void _poll_events_thread(void *ud);
|
||||||
|
bool _wait_for_events() const;
|
||||||
void _poll_events();
|
void _poll_events();
|
||||||
|
|
||||||
static Bool _predicate_all_events(Display *display, XEvent *event, XPointer arg);
|
static Bool _predicate_all_events(Display *display, XEvent *event, XPointer arg);
|
||||||
static Bool _predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg);
|
static Bool _predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg);
|
||||||
|
static Bool _predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg);
|
||||||
|
static Bool _predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg);
|
||||||
|
|
||||||
void process_xevents();
|
void process_xevents();
|
||||||
virtual void delete_main_loop();
|
virtual void delete_main_loop();
|
||||||
|
|
Loading…
Reference in New Issue