aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndre Weissflog <floooh@gmail.com>2024-09-17 19:08:53 +0200
committerAndre Weissflog <floooh@gmail.com>2024-09-17 19:08:53 +0200
commit15412a3c03a6a799d267599c5378a7422b14ab58 (patch)
tree49948f83ed6280f6625bb9f8d9af5598337059a2
parentf62817137961a1eedc3eac03f62012795d2c79c4 (diff)
parentb19ab8b92b7350ed7f63d24bccec43e51d374d16 (diff)
Merge branch 'qwx9-master'
-rw-r--r--CHANGELOG.md8
-rw-r--r--README.md2
-rw-r--r--sokol_app.h126
3 files changed, 134 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3e685621..05b7bd4f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
## Updates
+### 17-Sep-2024
+
+- The sokol_app.h Linux backend now has clipboard support. Many thanks to
+ @Dvad for the initial PR with most of the work and @qwx9 for the addtional
+ updates. See PR https://github.com/floooh/sokol/pull/1108 for details (this
+ isn't quite what ended up in sokol_app.h, because I did a couple of code
+ cleanup changes in during the merge).
+
### 10-Sep-2024
- Update sokol_imgui.h for Dear ImGui and cimgui version 1.91.1. This
diff --git a/README.md b/README.md
index cf4cab0b..4c456797 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
# Sokol
-[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**02-Sep-2024** in sokol_gfx.h, SG_FILTER_NONE has been removed)
+[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**17-Sep-2024** sokol_app.h: clipboard support for Linux)
[![Build](/../../actions/workflows/main.yml/badge.svg)](/../../actions/workflows/main.yml) [![Bindings](/../../actions/workflows/gen_bindings.yml/badge.svg)](/../../actions/workflows/gen_bindings.yml) [![build](https://github.com/floooh/sokol-zig/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/main.yml) [![build](https://github.com/floooh/sokol-nim/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-nim/actions/workflows/main.yml) [![Odin](https://github.com/floooh/sokol-odin/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-odin/actions/workflows/main.yml)[![Rust](https://github.com/floooh/sokol-rust/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-rust/actions/workflows/main.yml)[![Dlang](https://github.com/kassane/sokol-d/actions/workflows/build.yml/badge.svg)](https://github.com/kassane/sokol-d/actions/workflows/build.yml)
diff --git a/sokol_app.h b/sokol_app.h
index 7cd29bf8..97f72ab3 100644
--- a/sokol_app.h
+++ b/sokol_app.h
@@ -130,7 +130,7 @@
screen keyboard | --- | --- | --- | YES | TODO | YES
swap interval | YES | YES | YES | YES | TODO | YES
high-dpi | YES | YES | TODO | YES | YES | YES
- clipboard | YES | YES | TODO | --- | --- | YES
+ clipboard | YES | YES | YES | --- | --- | YES
MSAA | YES | YES | YES | YES | YES | YES
drag'n'drop | YES | YES | YES | --- | --- | YES
window icon | YES | YES(1)| YES | --- | --- | YES
@@ -1603,6 +1603,7 @@ typedef struct sapp_allocator {
_SAPP_LOGITEM_XMACRO(LINUX_X11_OPEN_DISPLAY_FAILED, "XOpenDisplay() failed") \
_SAPP_LOGITEM_XMACRO(LINUX_X11_QUERY_SYSTEM_DPI_FAILED, "failed to query system dpi value, assuming default 96.0") \
_SAPP_LOGITEM_XMACRO(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME, "dropped file URL doesn't start with 'file://'") \
+ _SAPP_LOGITEM_XMACRO(LINUX_X11_FAILED_TO_BECOME_OWNER_OF_CLIPBOARD, "X11: Failed to become owner of clipboard selection") \
_SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB, "unsupported input event encountered in _sapp_android_input_cb()") \
_SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB, "unsupported input event encountered in _sapp_android_main_cb()") \
_SAPP_LOGITEM_XMACRO(ANDROID_READ_MSG_FAILED, "failed to read message in _sapp_android_main_cb()") \
@@ -2162,6 +2163,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
#include <limits.h> /* LONG_MAX */
#include <pthread.h> /* only used a linker-guard, search for _sapp_linux_run() and see first comment */
#include <time.h>
+ #include <poll.h>
#endif
#if defined(_SAPP_APPLE)
@@ -2752,6 +2754,8 @@ typedef struct {
float dpi;
unsigned char error_code;
Atom UTF8_STRING;
+ Atom CLIPBOARD;
+ Atom TARGETS;
Atom WM_PROTOCOLS;
Atom WM_DELETE_WINDOW;
Atom WM_STATE;
@@ -9618,6 +9622,8 @@ _SOKOL_PRIVATE void _sapp_x11_init_extensions(void) {
_sapp.x11.NET_WM_ICON = XInternAtom(_sapp.x11.display, "_NET_WM_ICON", False);
_sapp.x11.NET_WM_STATE = XInternAtom(_sapp.x11.display, "_NET_WM_STATE", False);
_sapp.x11.NET_WM_STATE_FULLSCREEN = XInternAtom(_sapp.x11.display, "_NET_WM_STATE_FULLSCREEN", False);
+ _sapp.x11.CLIPBOARD = XInternAtom(_sapp.x11.display, "CLIPBOARD", False);
+ _sapp.x11.TARGETS = XInternAtom(_sapp.x11.display, "TARGETS", False);
if (_sapp.drop.enabled) {
_sapp.x11.xdnd.XdndAware = XInternAtom(_sapp.x11.display, "XdndAware", False);
_sapp.x11.xdnd.XdndEnter = XInternAtom(_sapp.x11.display, "XdndEnter", False);
@@ -10463,6 +10469,75 @@ _SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) {
XFlush(_sapp.x11.display);
}
+_SOKOL_PRIVATE void _sapp_x11_set_clipboard_string(const char* str) {
+ SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer);
+ if (strlen(str) >= (size_t)_sapp.clipboard.buf_size) {
+ _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG);
+ }
+ XSetSelectionOwner(_sapp.x11.display, _sapp.x11.CLIPBOARD, _sapp.x11.window, CurrentTime);
+ if (XGetSelectionOwner(_sapp.x11.display, _sapp.x11.CLIPBOARD) != _sapp.x11.window) {
+ _SAPP_ERROR(LINUX_X11_FAILED_TO_BECOME_OWNER_OF_CLIPBOARD);
+ }
+}
+
+_SOKOL_PRIVATE const char* _sapp_x11_get_clipboard_string(void) {
+ SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer);
+ Atom none = XInternAtom(_sapp.x11.display, "SAPP_SELECTION", False);
+ Atom incremental = XInternAtom(_sapp.x11.display, "INCR", False);
+ if (XGetSelectionOwner(_sapp.x11.display, _sapp.x11.CLIPBOARD) == _sapp.x11.window) {
+ // Instead of doing a large number of X round-trips just to put this
+ // string into a window property and then read it back, just return it
+ return _sapp.clipboard.buffer;
+ }
+ XConvertSelection(_sapp.x11.display,
+ _sapp.x11.CLIPBOARD,
+ _sapp.x11.UTF8_STRING,
+ none,
+ _sapp.x11.window,
+ CurrentTime);
+ XEvent event;
+ while (!XCheckTypedWindowEvent(_sapp.x11.display, _sapp.x11.window, SelectionNotify, &event)) {
+ // Wait for event data to arrive on the X11 display socket
+ struct pollfd fd = { ConnectionNumber(_sapp.x11.display), POLLIN };
+ while (!XPending(_sapp.x11.display)) {
+ poll(&fd, 1, -1);
+ }
+ }
+ if (event.xselection.property == None) {
+ return NULL;
+ }
+ char* data = NULL;
+ Atom actualType;
+ int actualFormat;
+ unsigned long itemCount, bytesAfter;
+ const bool ret = XGetWindowProperty(_sapp.x11.display,
+ event.xselection.requestor,
+ event.xselection.property,
+ 0,
+ LONG_MAX,
+ True,
+ _sapp.x11.UTF8_STRING,
+ &actualType,
+ &actualFormat,
+ &itemCount,
+ &bytesAfter,
+ (unsigned char**) &data);
+ if (ret != Success || data == NULL) {
+ if (data != NULL) {
+ XFree(data);
+ }
+ return NULL;
+ }
+ if ((actualType == incremental) || (itemCount >= (size_t)_sapp.clipboard.buf_size)) {
+ _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG);
+ XFree(data);
+ return NULL;
+ }
+ _sapp_strcpy(data, _sapp.clipboard.buffer, _sapp.clipboard.buf_size);
+ XFree(data);
+ return _sapp.clipboard.buffer;
+}
+
_SOKOL_PRIVATE void _sapp_x11_update_window_title(void) {
Xutf8SetWMProperties(_sapp.x11.display,
_sapp.x11.window,
@@ -11202,6 +11277,48 @@ _SOKOL_PRIVATE void _sapp_x11_on_clientmessage(XEvent* event) {
}
}
+_SOKOL_PRIVATE void _sapp_x11_on_selectionrequest(XEvent* event) {
+ XSelectionRequestEvent* req = &event->xselectionrequest;
+ if (req->selection != _sapp.x11.CLIPBOARD) {
+ return;
+ }
+ if (!_sapp.clipboard.enabled) {
+ return;
+ }
+ SOKOL_ASSERT(_sapp.clipboard.buffer);
+ XSelectionEvent reply;
+ _sapp_clear(&reply, sizeof(reply));
+ reply.type = SelectionNotify;
+ reply.display = req->display;
+ reply.requestor = req->requestor;
+ reply.selection = req->selection;
+ reply.target = req->target;
+ reply.property = req->property;
+ reply.time = req->time;
+ if (req->target == _sapp.x11.UTF8_STRING) {
+ XChangeProperty(_sapp.x11.display,
+ req->requestor,
+ req->property,
+ _sapp.x11.UTF8_STRING,
+ 8,
+ PropModeReplace,
+ (unsigned char*) _sapp.clipboard.buffer,
+ strlen(_sapp.clipboard.buffer));
+ } else if (req->target == _sapp.x11.TARGETS) {
+ XChangeProperty(_sapp.x11.display,
+ req->requestor,
+ req->property,
+ XA_ATOM,
+ 32,
+ PropModeReplace,
+ (unsigned char*) &_sapp.x11.UTF8_STRING,
+ 1);
+ } else {
+ reply.property = None;
+ }
+ XSendEvent(_sapp.x11.display, req->requestor, False, 0, (XEvent*) &reply);
+}
+
_SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) {
switch (event->type) {
case GenericEvent:
@@ -11243,6 +11360,9 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) {
case SelectionNotify:
_sapp_x11_on_selectionnotify(event);
break;
+ case SelectionRequest:
+ _sapp_x11_on_selectionrequest(event);
+ break;
case DestroyNotify:
// not a bug
break;
@@ -11723,6 +11843,8 @@ SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) {
_sapp_emsc_set_clipboard_string(str);
#elif defined(_SAPP_WIN32)
_sapp_win32_set_clipboard_string(str);
+ #elif defined(_SAPP_LINUX)
+ _sapp_x11_set_clipboard_string(str);
#else
/* not implemented */
#endif
@@ -11739,6 +11861,8 @@ SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) {
return _sapp.clipboard.buffer;
#elif defined(_SAPP_WIN32)
return _sapp_win32_get_clipboard_string();
+ #elif defined(_SAPP_LINUX)
+ return _sapp_x11_get_clipboard_string();
#else
/* not implemented */
return _sapp.clipboard.buffer;