aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2022-09-09 22:59:06 +0200
committerdec05eba <dec05eba@protonmail.com>2022-09-09 23:01:03 +0200
commitbe7e74fe9b51123dec29f9ed9c0441dc8725dd1a (patch)
tree5eb7c73910e2526cc4b009bc677e76d2b66f7ec4 /src
parente6167bc9300d497b1f2edf3a307164081e24f2bd (diff)
Support png,jpg and gif clipboard. Use callback for clipboard
Diffstat (limited to 'src')
-rw-r--r--src/window/window.c180
1 files changed, 132 insertions, 48 deletions
diff --git a/src/window/window.c b/src/window/window.c
index c03d681..b4443e2 100644
--- a/src/window/window.c
+++ b/src/window/window.c
@@ -9,6 +9,7 @@
#include <string.h>
#include <errno.h>
#include <stdio.h>
+#include <assert.h>
#include <unistd.h>
/* TODO: Handle XIM better. Set XIM position to text position on screen (for text input) and reset input when selecting a new text input, etc */
@@ -57,7 +58,10 @@ typedef struct {
Atom targets_atom;
Atom text_atom;
Atom utf8_string_atom;
- Atom target_property_atom;
+ Atom image_png;
+ Atom image_jpg;
+ Atom image_jpeg;
+ Atom image_gif;
Atom incr_atom;
Cursor default_cursor;
Cursor invisible_cursor;
@@ -87,7 +91,10 @@ static int x11_context_init(x11_context *self) {
self->targets_atom = XInternAtom(context->connection, "TARGETS", False);
self->text_atom = XInternAtom(context->connection, "TEXT", False);
self->utf8_string_atom = XInternAtom(context->connection, "UTF8_STRING", True);
- self->target_property_atom = XInternAtom(context->connection, "MGL_CLIPBOARD_TARGET_PROPERTY", False);
+ self->image_png = XInternAtom(context->connection, "image/png", True);
+ self->image_jpg = XInternAtom(context->connection, "image/jpg", True);
+ self->image_jpeg = XInternAtom(context->connection, "image/jpeg", True);
+ self->image_gif = XInternAtom(context->connection, "image/gif", True);
self->incr_atom = XInternAtom(context->connection, "INCR", False);
self->default_cursor = None;
self->invisible_cursor = None;
@@ -873,59 +880,76 @@ void mgl_window_set_clipboard(mgl_window *self, const char *str, size_t size) {
self->clipboard_size = size;
}
-/* TODO: Right now the returned str is free'd with free, but shouldn't it be free'd with free? */
-bool mgl_window_get_clipboard(mgl_window *self, char **str, size_t *size) {
+static Atom find_matching_atom(const Atom *supported_atoms, size_t num_supported_atoms, const Atom *atoms, size_t num_atoms) {
+ for(size_t j = 0; j < num_supported_atoms; ++j) {
+ for(size_t i = 0; i < num_atoms; ++i) {
+ if(atoms[i] == supported_atoms[j])
+ return atoms[i];
+ }
+ }
+ return None;
+}
+
+static mgl_clipboard_type atom_type_to_supported_clipboard_type(x11_context *x11_context, Atom type, int format) {
+ if((type == x11_context->utf8_string_atom || type == XA_STRING || type == x11_context->text_atom) && format == 8) {
+ return MGL_CLIPBOARD_TYPE_STRING;
+ } else if(type == x11_context->image_png && format == 8){
+ return MGL_CLIPBOARD_TYPE_IMAGE_PNG;
+ } else if((type == x11_context->image_jpg || type == x11_context->image_jpeg) && format == 8){
+ return MGL_CLIPBOARD_TYPE_IMAGE_JPG;
+ } else if(type == x11_context->image_gif && format == 8){
+ return MGL_CLIPBOARD_TYPE_IMAGE_GIF;
+ } else {
+ return -1;
+ }
+}
+
+bool mgl_window_get_clipboard(mgl_window *self, mgl_clipboard_callback callback, void *userdata) {
+ assert(callback);
+
mgl_context *context = mgl_get_context();
x11_context *x11_context = self->context;
- *str = NULL;
- *size = 0;
-
Window selection_owner = XGetSelectionOwner(context->connection, x11_context->clipboard_atom);
- if(!selection_owner) {
- char *empty_str = malloc(1);
- if(!empty_str)
- return false;
-
- empty_str[0] = '\0';
- return true;
- }
+ if(!selection_owner)
+ return false;
/* Return data immediately if we are the owner of the clipboard, because we can't process other events in the below event loop */
if(selection_owner == self->window) {
- if(!self->clipboard_string) {
- char *empty_str = malloc(1);
- if(!empty_str)
- return false;
-
- empty_str[0] = '\0';
- return true;
- }
-
- char *data = malloc(self->clipboard_size + 1);
- if(!data)
+ if(!self->clipboard_string)
return false;
- memcpy(data, self->clipboard_string, self->clipboard_size);
- data[self->clipboard_size] = '\0';
- *str = data;
- *size = self->clipboard_size;
- return true;
+ return callback((const unsigned char*)self->clipboard_string, self->clipboard_size, MGL_CLIPBOARD_TYPE_STRING, userdata);
}
XEvent xev;
while(XCheckTypedWindowEvent(context->connection, self->window, SelectionNotify, &xev)) {}
- XConvertSelection(context->connection, x11_context->clipboard_atom, x11_context->utf8_string_atom ? x11_context->utf8_string_atom : XA_STRING, x11_context->target_property_atom, self->window, CurrentTime);
+ /* Sorted by preference */
+ /* TODO: Support more types (BITMAP?, PIXMAP?) */
+ const Atom supported_clipboard_types[] = {
+ x11_context->utf8_string_atom,
+ XA_STRING,
+ x11_context->text_atom,
+ x11_context->image_png,
+ x11_context->image_jpg,
+ x11_context->image_jpeg,
+ x11_context->image_gif,
+ };
+ const unsigned long num_supported_clipboard_types = sizeof(supported_clipboard_types) / sizeof(supported_clipboard_types[0]);
+
+ Atom requested_clipboard_type = None;
+ const Atom XA_TARGETS = XInternAtom(context->connection, "TARGETS", False);
+ XConvertSelection(context->connection, x11_context->clipboard_atom, XA_TARGETS, x11_context->clipboard_atom, self->window, CurrentTime);
mgl_clock timeout_timer;
mgl_clock_init(&timeout_timer);
- const double timeout_seconds = 1.0;
+ const double timeout_seconds = 5.0;
while(mgl_clock_get_elapsed_time_seconds(&timeout_timer) < timeout_seconds) {
while(XCheckTypedWindowEvent(context->connection, self->window, SelectionNotify, &xev)) {
- if(!xev.xselection.property || xev.xselection.selection != x11_context->clipboard_atom)
+ if(!xev.xselection.property || !xev.xselection.target || xev.xselection.selection != x11_context->clipboard_atom)
continue;
Atom type;
@@ -933,23 +957,41 @@ bool mgl_window_get_clipboard(mgl_window *self, char **str, size_t *size) {
unsigned long items;
unsigned long remaining_bytes;
unsigned char *data = NULL;
- if(XGetWindowProperty(context->connection, self->window, x11_context->target_property_atom, 0, INT32_MAX, False, AnyPropertyType, &type, &format, &items, &remaining_bytes, &data) == Success) {
- bool got_clipboard = false;
- if(type != x11_context->incr_atom) {
- if(type == x11_context->utf8_string_atom && format == 8) {
- *str = (char*)data;
- *size = items;
- got_clipboard = true;
- } else if(type == XA_STRING && format == 8) {
- *str = (char*)data;
- *size = items;
- got_clipboard = true;
+
+ if(xev.xselection.target == XA_TARGETS && requested_clipboard_type == None) {
+ if(XGetWindowProperty(context->connection, self->window, x11_context->clipboard_atom, 0, 1024, False, AnyPropertyType, &type, &format, &items, &remaining_bytes, &data) == Success && data) {
+ bool got_clipboard = false;
+ if(type != x11_context->incr_atom && type == XA_ATOM && format == 32) {
+ requested_clipboard_type = find_matching_atom(supported_clipboard_types, num_supported_clipboard_types, (Atom*)data, items);
+ if(requested_clipboard_type == None) {
+ /* Pasting clipboard data type we dont support */
+ return false;
+ } else {
+ XConvertSelection(context->connection, x11_context->clipboard_atom, requested_clipboard_type, x11_context->clipboard_atom, self->window, CurrentTime);
+ }
}
+ XFree(data);
}
+ } else if(xev.xselection.target == requested_clipboard_type) {
+ bool success = true;
+ unsigned long offset = 0;
+ while(XGetWindowProperty(context->connection, self->window, x11_context->clipboard_atom, offset, 32768, False, AnyPropertyType, &type, &format, &items, &remaining_bytes, &data) == Success && data) {
+ const mgl_clipboard_type clipboard_type = atom_type_to_supported_clipboard_type(x11_context, type, format);
+ if(type != x11_context->incr_atom && clipboard_type != -1) {
+ if(!callback(data, items, clipboard_type, userdata)) {
+ success = false;
+ break;
+ }
+ }
+ offset += (items * (format/8)); // format is the bit size of the data
+ XFree(data);
- XDeleteProperty(context->connection, self->window, x11_context->target_property_atom);
- if(got_clipboard)
- return true;
+ if(mgl_clock_get_elapsed_time_seconds(&timeout_timer) >= timeout_seconds) {
+ success = false;
+ break;
+ }
+ }
+ return success;
}
}
}
@@ -958,6 +1000,48 @@ bool mgl_window_get_clipboard(mgl_window *self, char **str, size_t *size) {
return false;
}
+typedef struct {
+ char **str;
+ size_t *size;
+} ClipboardStringCallbackData;
+
+static bool clipboard_copy_string_callback(const unsigned char *data, size_t size, mgl_clipboard_type clipboard_type, void *userdata) {
+ ClipboardStringCallbackData *callback_data = userdata;
+ if(clipboard_type != MGL_CLIPBOARD_TYPE_STRING) {
+ free(*callback_data->str);
+ *callback_data->str = NULL;
+ *callback_data->size = 0;
+ return false;
+ }
+
+ char *new_data = realloc(*callback_data->str, *callback_data->size + size);
+ if(!new_data) {
+ free(*callback_data->str);
+ *callback_data->str = NULL;
+ *callback_data->size = 0;
+ return false;
+ }
+
+ memcpy(new_data + *callback_data->size, data, size);
+
+ *callback_data->str = new_data;
+ *callback_data->size += size;
+ return true;
+}
+
+bool mgl_window_get_clipboard_string(mgl_window *self, char **str, size_t *size) {
+ assert(str);
+ assert(size);
+
+ *str = NULL;
+ *size = 0;
+
+ ClipboardStringCallbackData callback_data;
+ callback_data.str = str;
+ callback_data.size = size;
+ return mgl_window_get_clipboard(self, clipboard_copy_string_callback, &callback_data);
+}
+
void mgl_window_set_key_repeat_enabled(mgl_window *self, bool enabled) {
self->key_repeat_enabled = enabled;
}