Introduction
Introduction Statistics Contact Development Disclaimer Help
tRefactor clipboard handling - ledit - Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
Log
Files
Refs
README
LICENSE
---
commit af728a8b3107fc9c18562adc9ed7dbca46dbda4d
parent b195be7aa66957888ba1f996b8c24fde7179fbc8
Author: lumidify <[email protected]>
Date: Thu, 1 Sep 2022 22:02:13 +0200
Refactor clipboard handling
Diffstat:
M Makefile | 2 ++
M buffer.c | 3 ++-
M buffer.h | 6 ++++--
A clipboard.c | 372 +++++++++++++++++++++++++++++…
A clipboard.h | 28 ++++++++++++++++++++++++++++
M keys_basic.c | 2 +-
M ledit.c | 16 +++++++++-------
M txtbuf.c | 37 +++++++++++++++++++++++++++++…
M txtbuf.h | 26 ++++++++++++++++++++++++++
M view.c | 30 ++++++++++++++----------------
M window.c | 247 +----------------------------…
M window.h | 52 +++--------------------------…
12 files changed, 502 insertions(+), 319 deletions(-)
---
diff --git a/Makefile b/Makefile
t@@ -31,6 +31,7 @@ OBJ = \
util.o \
draw_util.o \
window.o \
+ clipboard.o \
pango-compat.o
SRC = ${OBJ:.o=.c}
t@@ -55,6 +56,7 @@ HDR = \
cleanup.h \
macros.h \
pango-compat.h \
+ clipboard.h \
uglycrap.h
CONFIGHDR = \
diff --git a/buffer.c b/buffer.c
t@@ -225,9 +225,10 @@ marklist_create(void) {
}
ledit_buffer *
-buffer_create(ledit_common *common) {
+buffer_create(ledit_common *common, ledit_clipboard *clipboard) {
ledit_buffer *buffer = ledit_malloc(sizeof(ledit_buffer));
buffer->common = common;
+ buffer->clipboard = clipboard;
buffer->undo = undo_stack_create();
buffer->marklist = marklist_create();
diff --git a/buffer.h b/buffer.h
t@@ -34,7 +34,9 @@ typedef struct {
/* TODO: advisory lock on file */
struct ledit_buffer {
- ledit_common *common; /* common stuff, e.g. display, etc. */
+ ledit_common *common; /* common stuff, e.g. display, etc. -…
+ ledit_clipboard *clipboard; /* this also doesn't really belong he…
+ /* FIXME: add some sort of manager that holds shared stuff like clipbo…
char *filename; /* last opened filename */
struct timespec file_mtime; /* last modified time of file */
undo_stack *undo; /* undo manager */
t@@ -55,7 +57,7 @@ struct ledit_buffer {
/*
* Create a new buffer with one empty line
*/
-ledit_buffer *buffer_create(ledit_common *common);
+ledit_buffer *buffer_create(ledit_common *common, ledit_clipboard *clipboard);
/*
* Lock all views except the given view.
diff --git a/clipboard.c b/clipboard.c
t@@ -0,0 +1,372 @@
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include "util.h"
+#include "memory.h"
+#include "common.h"
+#include "clipboard.h"
+#include "macros.h"
+#include "config.h"
+
+/* clipboard handling largely stolen from st (https://st.suckless.org),
+ with some *inspiration* taken from SDL (https://libsdl.org), mainly
+ the idea to create a separate window just for clipboard handling */
+
+static Window get_clipboard_window(ledit_clipboard *clip);
+static Bool check_window(Display *dpy, XEvent *event, XPointer arg);
+static txtbuf *get_text(ledit_clipboard *clip, int primary);
+static int clipboard_propnotify(ledit_clipboard *clip, XEvent *e, txtbuf *buf);
+static int clipboard_selnotify(ledit_clipboard *clip, XEvent *e, txtbuf *buf);
+static void clipboard_selrequest(ledit_clipboard *clip, XEvent *e);
+
+#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+
+struct ledit_clipboard {
+ txtbuf *primary;
+ txtbuf *clipboard;
+ ledit_common *common;
+ Window window;
+ Atom xtarget;
+ XSetWindowAttributes wattrs;
+};
+
+ledit_clipboard *
+clipboard_create(ledit_common *common) {
+ ledit_clipboard *clip = ledit_malloc(sizeof(ledit_clipboard));
+ clip->primary = txtbuf_new();
+ clip->clipboard = txtbuf_new();
+ clip->common = common;
+ clip->window = None;
+ clip->xtarget = None;
+ #ifdef X_HAVE_UTF8_STRING
+ clip->xtarget = XInternAtom(common->dpy, "UTF8_STRING", False);
+ #endif
+ if (clip->xtarget == None)
+ clip->xtarget = XA_STRING;
+ clip->wattrs.event_mask = 0;
+ return clip;
+}
+
+void
+clipboard_destroy(ledit_clipboard *clip) {
+ txtbuf_destroy(clip->primary);
+ txtbuf_destroy(clip->clipboard);
+ if (clip->window != None)
+ XDestroyWindow(clip->common->dpy, clip->window);
+ free(clip);
+}
+
+static Window
+get_clipboard_window(ledit_clipboard *clip) {
+ if (clip->window == None) {
+ clip->window = XCreateWindow(
+ clip->common->dpy, DefaultRootWindow(clip->common->dpy),
+ -10, -10, 1, 1, 0, CopyFromParent, InputOnly, CopyFromPare…
+ );
+ XFlush(clip->common->dpy);
+ }
+ return clip->window;
+}
+
+void
+clipboard_set_primary_text(ledit_clipboard *clip, char *text) {
+ Window window = get_clipboard_window(clip);
+ txtbuf_set_text(clip->primary, text);
+ XSetSelectionOwner(clip->common->dpy, XA_PRIMARY, window, CurrentTime);
+}
+
+txtbuf *
+clipboard_get_primary_buffer(ledit_clipboard *clip) {
+ return clip->primary;
+}
+
+void
+clipboard_set_primary_selection_owner(ledit_clipboard *clip) {
+ Window window = get_clipboard_window(clip);
+ XSetSelectionOwner(clip->common->dpy, XA_PRIMARY, window, CurrentTime);
+}
+
+void
+clipboard_set_clipboard_text(ledit_clipboard *clip, char *text) {
+ Atom clip_atom;
+ Window window = get_clipboard_window(clip);
+ clip_atom = XInternAtom(clip->common->dpy, "CLIPBOARD", False);
+ txtbuf_set_text(clip->clipboard, text);
+ XSetSelectionOwner(clip->common->dpy, clip_atom, window, CurrentTime);
+}
+
+txtbuf *
+clipboard_get_clipboard_buffer(ledit_clipboard *clip) {
+ return clip->clipboard;
+}
+
+void
+clipboard_set_clipboard_selection_owner(ledit_clipboard *clip) {
+ Atom clip_atom;
+ Window window = get_clipboard_window(clip);
+ clip_atom = XInternAtom(clip->common->dpy, "CLIPBOARD", False);
+ XSetSelectionOwner(clip->common->dpy, clip_atom, window, CurrentTime);
+}
+
+void
+clipboard_primary_to_clipboard(ledit_clipboard *clip) {
+ Atom clip_atom;
+ if (clip->primary->len > 0) {
+ Window window = get_clipboard_window(clip);
+ txtbuf_copy(clip->clipboard, clip->primary);
+ clip_atom = XInternAtom(clip->common->dpy, "CLIPBOARD", False);
+ XSetSelectionOwner(clip->common->dpy, clip_atom, window, Curre…
+ }
+}
+
+int
+clipboard_filter_event(ledit_clipboard *clip, XEvent *e) {
+ if (clip->window != None && e->xany.window == clip->window) {
+ if (e->type == SelectionRequest)
+ clipboard_selrequest(clip, e);
+ /* other events are discarded since there
+ was no request to get the clipboard text */
+ return 1;
+ }
+ return 0;
+}
+
+static Bool
+check_window(Display *dpy, XEvent *event, XPointer arg) {
+ (void)dpy;
+ return *(Window *)arg == event->xany.window;
+}
+
+/* WARNING: The returned txtbuf needs to be copied before further processing! …
+static txtbuf *
+get_text(ledit_clipboard *clip, int primary) {
+ txtbuf *buf = primary ? clip->primary : clip->clipboard;
+ txtbuf_clear(buf);
+ Window window = get_clipboard_window(clip);
+ struct timespec now, elapsed, last, start, sleep_time;
+ sleep_time.tv_sec = 0;
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ last = start;
+ XEvent event;
+ while (1) {
+ /* FIXME: I have no idea how inefficient this is */
+ if (XCheckIfEvent(clip->common->dpy, &event, &check_window, (X…
+ switch (event.type) {
+ case SelectionNotify:
+ if (!clipboard_selnotify(clip, &event,…
+ return buf;
+ break;
+ case PropertyNotify:
+ if (!clipboard_propnotify(clip, &event…
+ return buf;
+ break;
+ case SelectionRequest:
+ clipboard_selrequest(clip, &event);
+ break;
+ default:
+ break;
+ }
+ }
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ledit_timespecsub(&now, &start, &elapsed);
+ if (elapsed.tv_sec > 0) {
+ if (primary)
+ clipboard_set_primary_text(clip, "");
+ else
+ clipboard_set_clipboard_text(clip, "");
+ return NULL;
+ }
+ ledit_timespecsub(&now, &last, &elapsed);
+ if (elapsed.tv_sec == 0 && elapsed.tv_nsec < TICK) {
+ sleep_time.tv_nsec = TICK - elapsed.tv_nsec;
+ nanosleep(&sleep_time, NULL);
+ }
+ last = now;
+ }
+ return NULL;
+}
+
+txtbuf *
+clipboard_get_clipboard_text(ledit_clipboard *clip) {
+ Atom clip_atom;
+ clip_atom = XInternAtom(clip->common->dpy, "CLIPBOARD", False);
+ Window window = get_clipboard_window(clip);
+ Window owner = XGetSelectionOwner(clip->common->dpy, clip_atom);
+ if (owner == None) {
+ return NULL;
+ } else if (owner == window) {
+ return clip->clipboard;
+ } else {
+ XConvertSelection(clip->common->dpy, clip_atom, clip->xtarget,…
+ return get_text(clip, 0);
+ }
+}
+
+txtbuf *
+clipboard_get_primary_text(ledit_clipboard *clip) {
+ Window window = get_clipboard_window(clip);
+ Window owner = XGetSelectionOwner(clip->common->dpy, XA_PRIMARY);
+ if (owner == None) {
+ return NULL;
+ } else if (owner == window) {
+ return clip->primary;
+ } else {
+ XConvertSelection(clip->common->dpy, XA_PRIMARY, clip->xtarget…
+ return get_text(clip, 1);
+ }
+}
+
+/* 0 means the transfer is done, 1 means there might be more */
+static int
+clipboard_propnotify(ledit_clipboard *clip, XEvent *e, txtbuf *buf) {
+ XPropertyEvent *xpev;
+ Atom clipboard = XInternAtom(clip->common->dpy, "CLIPBOARD", False);
+ xpev = &e->xproperty;
+ if (xpev->state == PropertyNewValue && (xpev->atom == XA_PRIMARY || xp…
+ return clipboard_selnotify(clip, e, buf);
+ }
+ return 1;
+}
+
+/* FIXME: test this properly because I don't really understand all of it */
+/* 0 means the transfer is done, 1 means there might be more */
+static int
+clipboard_selnotify(ledit_clipboard *clip, XEvent *e, txtbuf *buf) {
+ unsigned long nitems, ofs, rem;
+ int format;
+ unsigned char *data;
+ Atom type, incratom, property = None;
+
+ incratom = XInternAtom(clip->common->dpy, "INCR", 0);
+
+ ofs = 0;
+ if (e->type == SelectionNotify) {
+ property = e->xselection.property;
+ } else if (e->type == PropertyNotify) {
+ property = e->xproperty.atom;
+ }
+
+ if (property == None)
+ return 1;
+
+ Window window = get_clipboard_window(clip);
+ do {
+ /* FIXME: proper error logging */
+ /* FIXME: show error message in window */
+ if (XGetWindowProperty(
+ clip->common->dpy, window, property, ofs, BUFSIZ/4, False,
+ AnyPropertyType, &type, &format, &nitems, &rem, &data)) {
+ fprintf(stderr, "Clipboard allocation failed\n");
+ return 0;
+ }
+
+ if (e->type == PropertyNotify && nitems == 0 && rem == 0) {
+ /*
+ * If there is some PropertyNotify with no data, then
+ * this is the signal of the selection owner that all
+ * data has been transferred. We won't need to receive
+ * PropertyNotify events anymore.
+ */
+ MODBIT(clip->wattrs.event_mask, 0, PropertyChangeMask);
+ XChangeWindowAttributes(clip->common->dpy, window, CWE…
+ return 0;
+ }
+
+ if (type == incratom) {
+ /*
+ * Activate the PropertyNotify events so we receive
+ * when the selection owner sends us the next
+ * chunk of data.
+ */
+ MODBIT(clip->wattrs.event_mask, 1, PropertyChangeMask);
+ XChangeWindowAttributes(clip->common->dpy, window, CWE…
+
+ /*
+ * Deleting the property is the transfer start signal.
+ */
+ XDeleteProperty(clip->common->dpy, window, (int)proper…
+ continue;
+ }
+
+ /* FIXME: XGetWindowProperty takes data as unsigned char, so i…
+ txtbuf_appendn(buf, (char *)data, (size_t)(nitems * format / 8…
+ XFree(data);
+ if (!(clip->wattrs.event_mask & PropertyChangeMask))
+ return 0;
+ /* number of 32-bit chunks returned */
+ ofs += nitems * format / 32;
+ } while (rem > 0);
+
+ /*
+ * Deleting the property again tells the selection owner to send the
+ * next data chunk in the property.
+ */
+ XDeleteProperty(clip->common->dpy, window, (int)property);
+ return 1;
+}
+
+static void
+clipboard_selrequest(ledit_clipboard *clip, XEvent *e)
+{
+ XSelectionRequestEvent *xsre;
+ XSelectionEvent xev;
+ Atom xa_targets, string, clip_atom;
+ char *seltext;
+
+ xsre = (XSelectionRequestEvent *) e;
+ xev.type = SelectionNotify;
+ xev.requestor = xsre->requestor;
+ xev.selection = xsre->selection;
+ xev.target = xsre->target;
+ xev.time = xsre->time;
+ if (xsre->property == None)
+ xsre->property = xsre->target;
+
+ /* reject */
+ xev.property = None;
+
+ xa_targets = XInternAtom(clip->common->dpy, "TARGETS", 0);
+ if (xsre->target == xa_targets) {
+ /* respond with the supported type */
+ string = clip->xtarget;
+ XChangeProperty(
+ xsre->display, xsre->requestor, xsre->property,
+ XA_ATOM, 32, PropModeReplace, (unsigned char *) &string, 1
+ );
+ xev.property = xsre->property;
+ } else if (xsre->target == clip->xtarget || xsre->target == XA_STRING)…
+ /*
+ * xith XA_STRING non ascii characters may be incorrect in the
+ * requestor. It is not our problem, use utf8.
+ */
+ clip_atom = XInternAtom(clip->common->dpy, "CLIPBOARD", 0);
+ if (xsre->selection == XA_PRIMARY) {
+ seltext = clip->primary->text;
+ } else if (xsre->selection == clip_atom) {
+ seltext = clip->clipboard->text;
+ } else {
+ fprintf(
+ stderr,
+ "Unhandled clipboard selection 0x%lx\n", xsre->sel…
+ );
+ return;
+ }
+ /* FIXME: Should this handle sending data in multiple chunks? …
+ if (seltext != NULL) {
+ XChangeProperty(
+ xsre->display, xsre->requestor, xsre->property, xs…
+ 8, PropModeReplace, (unsigned char *)seltext, strl…
+ );
+ xev.property = xsre->property;
+ }
+ }
+
+ /* all done, send a notification to the listener */
+ if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *)&xev))
+ fprintf(stderr, "Error sending SelectionNotify event\n");
+}
diff --git a/clipboard.h b/clipboard.h
t@@ -0,0 +1,28 @@
+#ifndef _CLIPBOARD_H_
+#define _CLIPBOARD_H_
+
+#include <X11/Xlib.h>
+#include "common.h"
+#include "txtbuf.h"
+
+typedef struct ledit_clipboard ledit_clipboard;
+
+ledit_clipboard *clipboard_create(ledit_common *common);
+void clipboard_destroy(ledit_clipboard *clip);
+void clipboard_set_primary_text(ledit_clipboard *clip, char *text);
+txtbuf *clipboard_get_primary_buffer(ledit_clipboard *clip);
+void clipboard_set_primary_selection_owner(ledit_clipboard *clip);
+void clipboard_set_clipboard_text(ledit_clipboard *clip, char *text);
+txtbuf *clipboard_get_clipboard_buffer(ledit_clipboard *clip);
+void clipboard_set_clipboard_selection_owner(ledit_clipboard *clip);
+void clipboard_primary_to_clipboard(ledit_clipboard *clip);
+/* 1 means the event was used by the clipboard, 0 means it wasn't */
+int clipboard_filter_event(ledit_clipboard *clip, XEvent *e);
+
+/* WARNING: The returned txtbuf is owned by the clipboard and must
+ be copied before further processing and especially before any
+ further clipboard functions are called. */
+txtbuf *clipboard_get_clipboard_text(ledit_clipboard *clip);
+txtbuf *clipboard_get_primary_text(ledit_clipboard *clip);
+
+#endif /* _CLIPBOARD_H_ */
diff --git a/keys_basic.c b/keys_basic.c
t@@ -2330,7 +2330,7 @@ clipcopy(ledit_view *view, char *text, size_t len) {
if (!key_stack_empty())
return err_invalid_key(view);
/* FIXME: abstract this through view */
- clipboard_primary_to_clipboard(view->window);
+ clipboard_primary_to_clipboard(view->buffer->clipboard);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
diff --git a/ledit.c b/ledit.c
t@@ -47,8 +47,9 @@ static void redraw(void);
static void change_keyboard(char *lang);
static void key_press(ledit_view *view, XEvent *event);
-ledit_buffer *buffer = NULL;
ledit_common common;
+ledit_clipboard *clipboard = NULL;
+ledit_buffer *buffer = NULL;
size_t cur_lang = 0;
static void
t@@ -114,6 +115,8 @@ mainloop(void) {
change_kbd = 1;
continue;
}
+ if (clipboard_filter_event(clipboard, &event))
+ continue;
if (XFilterEvent(&event, None))
continue;
ledit_view *view = NULL;
t@@ -153,11 +156,6 @@ mainloop(void) {
running = 0;
}
break;
- case SelectionNotify:
- case PropertyNotify:
- case SelectionRequest:
- window_clipboard_event(view->window, &event);
- break;
default:
break;
}
t@@ -320,7 +318,9 @@ setup(int argc, char *argv[]) {
ledit_debug_fmt("Time to load config (total): %lld seconds, %ld nanose…
#endif
- buffer = buffer_create(&common);
+ clipboard = clipboard_create(&common);
+
+ buffer = buffer_create(&common, clipboard);
buffer_add_view(buffer, NORMAL, 0, 0, 0);
/* FIXME: don't access view directly here */
ledit_view *view = buffer->views[0];
t@@ -450,6 +450,8 @@ ledit_cleanup(void) {
basic_key_cleanup();
command_key_cleanup();
key_processing_cleanup();
+ if (clipboard)
+ clipboard_destroy(clipboard);
if (buffer)
buffer_destroy(buffer);
config_cleanup(&common);
diff --git a/txtbuf.c b/txtbuf.c
t@@ -52,6 +52,35 @@ txtbuf_fmt(txtbuf *buf, char *fmt, ...) {
}
void
+txtbuf_set_text(txtbuf *buf, char *text) {
+ txtbuf_set_textn(buf, text, strlen(text));
+}
+
+void
+txtbuf_set_textn(txtbuf *buf, char *text, size_t len) {
+ txtbuf_resize(buf, len);
+ buf->len = len;
+ memmove(buf->text, text, len);
+ buf->text[buf->len] = '\0';
+}
+
+void
+txtbuf_append(txtbuf *buf, char *text) {
+ txtbuf_appendn(buf, text, strlen(text));
+}
+
+/* FIXME: some sort of append that does not resize until there's not enough
+ space so a buffer that will be filled up anyways doesn't have to be
+ constantly resized */
+void
+txtbuf_appendn(txtbuf *buf, char *text, size_t len) {
+ txtbuf_resize(buf, add_sz(buf->len, len));
+ memmove(buf->text + buf->len, text, len);
+ buf->len += len;
+ buf->text[buf->len] = '\0';
+}
+
+void
txtbuf_resize(txtbuf *buf, size_t sz) {
/* always leave room for extra \0 */
size_t cap = ideal_array_size(buf->cap, add_sz(sz, 1));
t@@ -104,3 +133,11 @@ int
txtbuf_eql(txtbuf *buf1, txtbuf *buf2) {
return txtbuf_cmp(buf1, buf2) == 0;
}
+
+void
+txtbuf_clear(txtbuf *buf) {
+ if (buf->len > 0) {
+ buf->len = 0;
+ buf->text[0] = '\0';
+ }
+}
diff --git a/txtbuf.h b/txtbuf.h
t@@ -37,6 +37,26 @@ txtbuf *txtbuf_new_from_char_len(char *str, size_t len);
void txtbuf_fmt(txtbuf *buf, char *fmt, ...);
/*
+ * Replace the stored text in 'buf' with 'text'.
+ */
+void txtbuf_set_text(txtbuf *buf, char *text);
+
+/*
+ * Same as txtbuf_set_text, but with explicit length for 'text'.
+ */
+void txtbuf_set_textn(txtbuf *buf, char *text, size_t len);
+
+/*
+ * Append 'text' to the text stored in 'buf'.
+ */
+void txtbuf_append(txtbuf *buf, char *text);
+
+/*
+ * Same as txtbuf_append, but with explicit length for 'text'.
+ */
+void txtbuf_appendn(txtbuf *buf, char *text, size_t len);
+
+/*
* Compare the text of two txtbuf's like 'strcmp'.
*/
int txtbuf_cmp(txtbuf *buf1, txtbuf *buf2);
t@@ -68,4 +88,10 @@ void txtbuf_copy(txtbuf *dst, txtbuf *src);
*/
txtbuf *txtbuf_dup(txtbuf *src);
+/*
+ * Clear the text, but do not reduce the internal capacity
+ * (for efficiency if it will be filled up again anyways).
+ */
+void txtbuf_clear(txtbuf *buf);
+
#endif
diff --git a/view.c b/view.c
t@@ -15,6 +15,7 @@
#include "pango-compat.h"
#include "memory.h"
#include "common.h"
+#include "clipboard.h"
#include "txtbuf.h"
#include "undo.h"
#include "cache.h"
t@@ -48,7 +49,6 @@ static void view_redraw_text(ledit_view *view);
/* Callbacks */
static void view_button_handler(void *data, XEvent *event);
static void view_scroll_handler(void *view, long pos);
-static void paste_callback(void *data, char *text, size_t len);
/* Render a line onto a pixmap that is assigned from the cache. */
static void render_line(ledit_view *view, size_t line_index);
t@@ -108,7 +108,7 @@ view_create(ledit_buffer *buffer, ledit_mode mode, size_t …
ledit_view *view = ledit_malloc(sizeof(ledit_view));
view->mode = mode;
view->buffer = buffer;
- view->window = window_create(buffer->common, mode);
+ view->window = window_create(buffer->common, buffer->clipboard, mode);
view->cache = cache_create(buffer->common->dpy);
view->lock_text = NULL;
view->cur_action = (struct action){ACTION_NONE, NULL};
t@@ -1727,10 +1727,9 @@ view_ensure_cursor_shown(ledit_view *view) {
/* lines and bytes need to be sorted already! */
static void
copy_selection_to_x_primary(ledit_view *view, size_t line1, size_t byte1, size…
- /* FIXME: let window handle this */
- txtbuf *primary = window_get_primary_clipboard_buffer();
+ txtbuf *primary = clipboard_get_primary_buffer(view->buffer->clipboard…
buffer_copy_text_to_txtbuf(view->buffer, primary, line1, byte1, line2,…
- XSetSelectionOwner(view->buffer->common->dpy, XA_PRIMARY, view->window…
+ clipboard_set_primary_selection_owner(view->buffer->clipboard);
/*
FIXME
if (XGetSelectionOwner(state.dpy, XA_PRIMARY) != state.win)
t@@ -2044,11 +2043,8 @@ view_redo(ledit_view *view, int num) {
view_wipe_line_cursor_attrs(view, view->cur_line);
}
-/* FIXME: this could give weird results if the paste occurs in multiple
- chunks and something else happens inbetween */
static void
-paste_callback(void *data, char *text, size_t len) {
- ledit_view *view = (ledit_view *)data;
+paste_txtbuf(ledit_view *view, txtbuf *buf) {
if (view->mode == NORMAL)
view_wipe_line_cursor_attrs(view, view->cur_line);
ledit_range cur_range;
t@@ -2058,7 +2054,7 @@ paste_callback(void *data, char *text, size_t len) {
cur_range.line2 = cur_range.byte2 = 0;
buffer_insert_with_undo(
view->buffer, cur_range, 1, 1, view->mode,
- view->cur_line, view->cur_index, text, len,
+ view->cur_line, view->cur_index, buf->text, buf->len,
&view->cur_line, &view->cur_index
);
view_ensure_cursor_shown(view);
t@@ -2066,16 +2062,18 @@ paste_callback(void *data, char *text, size_t len) {
view_set_line_cursor_attrs(view, view->cur_line, view->cur_ind…
}
-/* FIXME: guard against view being destroyed before paste callback is nulled */
-
void
view_paste_clipboard(ledit_view *view) {
- window_set_paste_callback(view->window, &paste_callback, view);
- clipboard_paste_clipboard(view->window);
+ txtbuf *buf = clipboard_get_clipboard_text(view->buffer->clipboard);
+ if (!buf)
+ return; /* FIXME: warning? */
+ paste_txtbuf(view, buf);
}
void
view_paste_primary(ledit_view *view) {
- window_set_paste_callback(view->window, &paste_callback, view);
- clipboard_paste_primary(view->window);
+ txtbuf *buf = clipboard_get_primary_text(view->buffer->clipboard);
+ if (!buf)
+ return; /* FIXME: warning? */
+ paste_txtbuf(view, buf);
}
diff --git a/window.c b/window.c
t@@ -47,19 +47,6 @@ struct bottom_bar {
int min_pos; /* minimum position cursor can be at */
};
-/* clipboard handling largely stolen from st (simple terminal) */
-
-#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
-
-/* FIXME: maybe move this to the window struct just in case any
- conflicts happen */
-/* FIXME: the paste handling is a bit weird because the text can come
- in several chunks - can this cause issues in certain cases? */
-struct {
- txtbuf *primary;
- char *clipboard;
-} xsel = {NULL, NULL};
-
/*
* Recalculate the size of the actual text area (which is managed by the view).
*/
t@@ -79,17 +66,6 @@ static void get_scroll_pos_height(ledit_window *windown, do…
*/
static void set_scroll_pos(ledit_window *window, double pos);
-/* event handling for clipboard events */
-static void clipboard_propnotify(ledit_window *window, XEvent *e);
-static void clipboard_selnotify(ledit_window *window, XEvent *e);
-static void clipboard_selrequest(ledit_window *window, XEvent *e);
-
-txtbuf *
-window_get_primary_clipboard_buffer(void) {
- /* FIXME: check if NULL */
- return xsel.primary;
-}
-
/* FIXME: shouldn't window->bottom_text_shown also be true when message_shown?…
/* FIXME: guard against negative width/height */
static void
t@@ -410,12 +386,6 @@ window_set_scroll_callback(ledit_window *window, void (*c…
}
void
-window_set_paste_callback(ledit_window *window, void (*cb)(void *, char *, siz…
- window->paste_callback = cb;
- window->paste_cb_data = data;
-}
-
-void
window_set_button_callback(ledit_window *window, void (*cb)(void *, XEvent *),…
window->button_callback = cb;
window->button_cb_data = data;
t@@ -514,7 +484,7 @@ xximspot(ledit_window *window, int x, int y) {
}
ledit_window *
-window_create(ledit_common *common, ledit_mode mode) {
+window_create(ledit_common *common, ledit_clipboard *clipboard, ledit_mode mod…
XGCValues gcv;
ledit_theme *theme = config_get_theme();
t@@ -528,11 +498,9 @@ window_create(ledit_common *common, ledit_mode mode) {
window->w = 500;
window->h = 500;
window->mode_extra_text = NULL;
- window->paste_callback = NULL;
window->scroll_callback = NULL;
window->button_callback = NULL;
window->resize_callback = NULL;
- window->paste_cb_data = NULL;
window->scroll_cb_data = NULL;
window->button_cb_data = NULL;
window->resize_cb_data = NULL;
t@@ -576,6 +544,8 @@ window_create(ledit_common *common, ledit_mode mode) {
XSetWMProtocols(common->dpy, window->xwin, &window->wm_delete_msg, 1);
window->common = common;
+ /* FIXME: not used yet - this will be used later when clipboard suppor…
+ window->clipboard = clipboard;
window->bb = ledit_malloc(sizeof(bottom_bar));
window->bb->mode = pango_layout_new(window->context);
t@@ -612,19 +582,7 @@ window_create(ledit_common *common, ledit_mode mode) {
XMapWindow(common->dpy, window->xwin);
- /* FIXME: why is this part of the window? */
- window->xtarget = XInternAtom(common->dpy, "UTF8_STRING", 0);
- window->paste_callback = NULL;
- if (window->xtarget == None)
- window->xtarget = XA_STRING;
-
window->cursor_text = XCreateFontCursor(window->common->dpy, XC_xterm);
- /*
- ledit_clear_window(window);
- ledit_redraw_window(window);
- */
- if (xsel.primary == NULL)
- xsel.primary = txtbuf_new();
/* FIXME: maybe delay this (i.e. move to different function)? */
XMapWindow(common->dpy, window->xwin);
t@@ -671,12 +629,6 @@ window_destroy(ledit_window *window) {
}
void
-window_cleanup(void) {
- txtbuf_destroy(xsel.primary);
- free(xsel.clipboard);
-}
-
-void
window_clear(ledit_window *window) {
ledit_theme *theme = config_get_theme();
XSetForeground(window->common->dpy, window->gc, theme->text_bg.pixel);
t@@ -829,181 +781,6 @@ window_resize(ledit_window *window, int w, int h) {
}
void
-clipboard_primary_to_clipboard(ledit_window *window) {
- Atom clipboard;
-
- free(xsel.clipboard);
- xsel.clipboard = NULL;
-
- /* FIXME: don't copy if text empty (no selection)? */
- if (xsel.primary->text != NULL) {
- xsel.clipboard = ledit_strdup(xsel.primary->text);
- clipboard = XInternAtom(window->common->dpy, "CLIPBOARD", 0);
- XSetSelectionOwner(window->common->dpy, clipboard, window->xwi…
- }
-}
-
-void
-clipboard_paste_clipboard(ledit_window *window) {
- Atom clipboard;
-
- clipboard = XInternAtom(window->common->dpy, "CLIPBOARD", 0);
- XConvertSelection(window->common->dpy, clipboard, window->xtarget, cli…
-}
-
-void
-clipboard_paste_primary(ledit_window *window) {
- XConvertSelection(window->common->dpy, XA_PRIMARY, window->xtarget, XA…
-}
-
-static void
-clipboard_propnotify(ledit_window *window, XEvent *e) {
- XPropertyEvent *xpev;
- Atom clipboard = XInternAtom(window->common->dpy, "CLIPBOARD", 0);
-
- xpev = &e->xproperty;
- if (xpev->state == PropertyNewValue &&
- (xpev->atom == XA_PRIMARY ||
- xpev->atom == clipboard)) {
- clipboard_selnotify(window, e);
- }
-}
-
-static void
-clipboard_selnotify(ledit_window *window, XEvent *e) {
- unsigned long nitems, ofs, rem;
- int format;
- unsigned char *data;
- Atom type, incratom, property = None;
-
- incratom = XInternAtom(window->common->dpy, "INCR", 0);
-
- ofs = 0;
- if (e->type == SelectionNotify) {
- property = e->xselection.property;
- } else if (e->type == PropertyNotify) {
- property = e->xproperty.atom;
- }
-
- if (property == None)
- return;
-
- do {
- /* FIXME: proper error logging */
- if (XGetWindowProperty(window->common->dpy, window->xwin, prop…
- BUFSIZ/4, False, AnyPropertyType,
- &type, &format, &nitems, &rem,
- &data)) {
- fprintf(stderr, "Clipboard allocation failed\n");
- return;
- }
-
- if (e->type == PropertyNotify && nitems == 0 && rem == 0) {
- /*
- * If there is some PropertyNotify with no data, then
- * this is the signal of the selection owner that all
- * data has been transferred. We won't need to receive
- * PropertyNotify events anymore.
- */
- MODBIT(window->wattrs.event_mask, 0, PropertyChangeMas…
- XChangeWindowAttributes(window->common->dpy, window->x…
- /* remove callback - this has to be set again the next…
- something is pasted */
- window->paste_callback = NULL;
- window->paste_cb_data = NULL;
- }
-
- if (type == incratom) {
- /*
- * Activate the PropertyNotify events so we receive
- * when the selection owner sends us the next
- * chunk of data.
- */
- MODBIT(window->wattrs.event_mask, 1, PropertyChangeMas…
- XChangeWindowAttributes(window->common->dpy, window->x…
-
- /*
- * Deleting the property is the transfer start signal.
- */
- XDeleteProperty(window->common->dpy, window->xwin, (in…
- continue;
- }
-
- /* FIXME: XGetWindowProperty takes data as unsigned char, so i…
- /* FIXME: buffer all pasted text and only send it in one go in…
- if (window->paste_callback)
- window->paste_callback(window->paste_cb_data, (char *)…
- XFree(data);
- /* number of 32-bit chunks returned */
- ofs += nitems * format / 32;
- } while (rem > 0);
-
- /*
- * Deleting the property again tells the selection owner to send the
- * next data chunk in the property.
- */
- XDeleteProperty(window->common->dpy, window->xwin, (int)property);
-}
-
-static void
-clipboard_selrequest(ledit_window *window, XEvent *e)
-{
- XSelectionRequestEvent *xsre;
- XSelectionEvent xev;
- Atom xa_targets, string, clipboard;
- char *seltext;
-
- xsre = (XSelectionRequestEvent *) e;
- xev.type = SelectionNotify;
- xev.requestor = xsre->requestor;
- xev.selection = xsre->selection;
- xev.target = xsre->target;
- xev.time = xsre->time;
- if (xsre->property == None)
- xsre->property = xsre->target;
-
- /* reject */
- xev.property = None;
-
- xa_targets = XInternAtom(window->common->dpy, "TARGETS", 0);
- if (xsre->target == xa_targets) {
- /* respond with the supported type */
- string = window->xtarget;
- XChangeProperty(xsre->display, xsre->requestor, xsre->property,
- XA_ATOM, 32, PropModeReplace,
- (unsigned char *) &string, 1);
- xev.property = xsre->property;
- } else if (xsre->target == window->xtarget || xsre->target == XA_STRIN…
- /*
- * xith XA_STRING non ascii characters may be incorrect in the
- * requestor. It is not our problem, use utf8.
- */
- clipboard = XInternAtom(window->common->dpy, "CLIPBOARD", 0);
- if (xsre->selection == XA_PRIMARY) {
- seltext = xsel.primary->text;
- } else if (xsre->selection == clipboard) {
- seltext = xsel.clipboard;
- } else {
- fprintf(stderr,
- "Unhandled clipboard selection 0x%lx\n",
- xsre->selection);
- return;
- }
- if (seltext != NULL) {
- XChangeProperty(xsre->display, xsre->requestor,
- xsre->property, xsre->target,
- 8, PropModeReplace,
- (unsigned char *)seltext, strlen(selte…
- xev.property = xsre->property;
- }
- }
-
- /* all done, send a notification to the listener */
- if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev))
- fprintf(stderr, "Error sending SelectionNotify event\n");
-}
-
-void
window_register_button_press(ledit_window *window, XEvent *event) {
int scroll_delta;
if (event->xbutton.button == Button4 ||
t@@ -1114,21 +891,3 @@ window_drag_motion(ledit_window *window, XEvent *event) {
window->redraw = 1;
}
}
-
-void
-window_clipboard_event(ledit_window *window, XEvent *event) {
- /* FIXME: paste in bottom bar */
- switch (event->type) {
- case SelectionNotify:
- clipboard_selnotify(window, event);
- break;
- case PropertyNotify:
- clipboard_propnotify(window, event);
- break;
- case SelectionRequest:
- clipboard_selrequest(window, event);
- break;
- default:
- break;
- }
-}
diff --git a/window.h b/window.h
t@@ -17,6 +17,7 @@
#include <pango/pangoxft.h>
#include "common.h"
+#include "clipboard.h"
#include "txtbuf.h"
typedef struct bottom_bar bottom_bar;
t@@ -37,7 +38,6 @@ typedef struct {
but that might be changed at some point */
XSetWindowAttributes wattrs;
Atom wm_delete_msg; /* used to check when window is closed */
- Atom xtarget; /* used for clipboard handling */
int w; /* width of window */
int h; /* height of window */
t@@ -79,14 +79,13 @@ typedef struct {
XPoint spot;
XVaNestedList spotlist;
- ledit_common *common;
+ ledit_common *common; /* shared with others */
+ ledit_clipboard *clipboard; /* also shared */
/* various callbacks */
- void (*paste_callback)(void *, char *, size_t);
void (*scroll_callback)(void *, long);
void (*button_callback)(void *, XEvent *);
void (*resize_callback)(void *);
- void *paste_cb_data;
void *scroll_cb_data;
void *button_cb_data;
void *resize_cb_data;
t@@ -98,18 +97,13 @@ typedef struct {
/*
* Create a window with initial mode 'mode'.
*/
-ledit_window *window_create(ledit_common *common, ledit_mode mode);
+ledit_window *window_create(ledit_common *common, ledit_clipboard *clipboard, …
/*
* Destroy a window.
*/
void window_destroy(ledit_window *window);
-/*
- * Clean up global data.
- */
-void window_cleanup(void);
-
/* FIXME: this is a bit confusing because there's a difference between editable
text shown and non-editable message shown */
t@@ -246,14 +240,6 @@ void window_set_scroll_pos(ledit_window *window, long pos…
void window_set_scroll_callback(ledit_window *window, void (*cb)(void *, long)…
/*
- * Set a callback that is called with text from the clipboard/primary
- * selection when it becomes available after 'clipboard_paste_clipboard'
- * or 'clipboard_paste_primary' is called.
- * The callback is called with 'data', the text, and the length of the text.
- */
-void window_set_paste_callback(ledit_window *window, void (*cb)(void *, char *…
-
-/*
* Set a callback that is called when Button1 is clicked or released
* in the text area.
*/
t@@ -304,31 +290,6 @@ void window_resize(ledit_window *window, int w, int h);
void window_get_textview_size(ledit_window *window, int *w_ret, int *h_ret);
/*
- * Move the X primary selection the the clipboard.
- */
-void clipboard_primary_to_clipboard(ledit_window *window);
-
-/*
- * Paste the X clipboard. The text is handed to the paste callback
- * when it becomes available.
- */
-void clipboard_paste_clipboard(ledit_window *window);
-
-/*
- * Paste the X primary selection. The text is handed to the paste
- * callback when it becomes available.
- */
-void clipboard_paste_primary(ledit_window *window);
-
-/*
- * Get the buffer used to store the text of the primary selection.
- * This is sort of hacky because the view uses it to write the
- * selection directly to this buffer and then set the selection
- * owner. That should probably be changed at some point.
- */
-txtbuf *window_get_primary_clipboard_buffer(void);
-
-/*
* Handle a button press.
* This does not filter events like the register function!
*/
t@@ -346,11 +307,6 @@ void window_button_release(ledit_window *window, XEvent *…
void window_drag_motion(ledit_window *window, XEvent *event);
/*
- * Handle a clipboard event (SelectionRequest, {Selection,Property}Notify).
- */
-void window_clipboard_event(ledit_window *window, XEvent *event);
-
-/*
* Set the pixel position of the input method context.
* This is used by some input method editors to show an editor at
* the position that text is being inserted at.
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.