"\t Shortcut to increase font size\n"
"\t --grab-zoom-out <grab> [<Ctrl>minus]\n"
"\t Shortcut to decrease font size\n"
+ "\t --grab-copy <grab> [<Logo>c]\n"
+ "\t Copy selected text\n"
+ "\t --grab-paste <grab> [<Logo>v]\n"
+ "\t Paste selection buffer\n"
"\n"
"Font Options:\n"
"\t --font-engine <engine> [pango]\n"
.keysym = XKB_KEY_minus,
};
+static struct conf_grab def_grab_copy = {
+ .mods = SHL_LOGO_MASK,
+ .keysym = XKB_KEY_c,
+};
+
+static struct conf_grab def_grab_paste = {
+ .mods = SHL_LOGO_MASK,
+ .keysym = XKB_KEY_v,
+};
+
struct conf_option options[] = {
CONF_OPTION_BOOL('h', "help", aftercheck_help, &wlt_conf.help, false),
CONF_OPTION_BOOL('v', "verbose", NULL, &wlt_conf.verbose, false),
CONF_OPTION_GRAB(0, "grab-fullscreen", NULL, &wlt_conf.grab_fullscreen, &def_grab_fullscreen),
CONF_OPTION_GRAB(0, "grab-zoom-in", NULL, &wlt_conf.grab_zoom_in, &def_grab_zoom_in),
CONF_OPTION_GRAB(0, "grab-zoom-out", NULL, &wlt_conf.grab_zoom_out, &def_grab_zoom_out),
+ CONF_OPTION_GRAB(0, "grab-copy", NULL, &wlt_conf.grab_copy, &def_grab_copy),
+ CONF_OPTION_GRAB(0, "grab-paste", NULL, &wlt_conf.grab_paste, &def_grab_paste),
CONF_OPTION_STRING(0, "font-engine", NULL, &wlt_conf.font_engine, "pango"),
CONF_OPTION_UINT(0, "font-size", NULL, &wlt_conf.font_size, 12),
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#include "conf.h"
struct wlt_terminal {
struct ev_eloop *eloop;
struct wlt_window *wnd;
+ struct wlt_display *disp;
struct wlt_widget *widget;
struct wlt_shm_buffer buffer;
struct wlt_rect alloc;
bool selection_started;
int sel_start_x;
int sel_start_y;
+
+ int paste_fd;
+ struct ev_fd *paste;
+ struct wl_data_source *copy;
+ char *copy_buf;
+ int copy_len;
};
static int draw_cell(struct tsm_screen *scr,
}
}
+static void paste_event(struct ev_fd *fd, int mask, void *data)
+{
+ struct wlt_terminal *term = data;
+ char buf[4096];
+ int ret;
+
+ if (mask & EV_READABLE) {
+ ret = read(term->paste_fd, buf, sizeof(buf));
+ if (ret == 0) {
+ goto err_close;
+ } else if (ret < 0) {
+ if (errno == EAGAIN)
+ return;
+ log_error("error on paste-fd (%d): %m", errno);
+ goto err_close;
+ }
+
+ kmscon_pty_write(term->pty, buf, ret);
+ return;
+ }
+
+ if (mask & EV_ERR) {
+ log_error("error on paste FD");
+ goto err_close;
+ }
+
+ if (mask & EV_HUP)
+ goto err_close;
+
+ return;
+
+err_close:
+ close(term->paste_fd);
+ ev_eloop_rm_fd(term->paste);
+ term->paste = NULL;
+}
+
+static void copy_target(void *data, struct wl_data_source *w_source,
+ const char *target)
+{
+}
+
+static void copy_send(void *data, struct wl_data_source *w_source,
+ const char *mime, int32_t fd)
+{
+ struct wlt_terminal *term = data;
+ int ret;
+
+ /* TODO: make this non-blocking */
+ ret = write(fd, term->copy_buf, term->copy_len);
+ if (ret != term->copy_len)
+ log_warning("cannot write whole selection: %d/%d", ret,
+ term->copy_len);
+ close(fd);
+}
+
+static void copy_cancelled(void *data, struct wl_data_source *w_source)
+{
+ struct wlt_terminal *term = data;
+
+ wl_data_source_destroy(w_source);
+ if (term->copy == w_source)
+ term->copy = NULL;
+}
+
+static const struct wl_data_source_listener copy_listener = {
+ .target = copy_target,
+ .send = copy_send,
+ .cancelled = copy_cancelled,
+};
+
static bool widget_key(struct wlt_widget *widget, unsigned int mask,
uint32_t sym, uint32_t state, bool handled, void *data)
{
return true;
}
+ if (SHL_HAS_BITS(mask, wlt_conf.grab_paste->mods) &&
+ sym == wlt_conf.grab_paste->keysym) {
+ if (term->paste) {
+ log_debug("cannot paste selection, previous paste still in progress");
+ return true;
+ }
+
+ ret = wlt_display_get_selection_fd(term->disp,
+ "text/plain;charset=utf-8");
+ if (ret == -ENOENT) {
+ log_debug("no selection to paste");
+ return true;
+ } else if (ret == -EAGAIN) {
+ log_debug("unknown mime-time for pasting selection");
+ return true;
+ } else if (ret < 0) {
+ log_error("cannot paste selection: %d", ret);
+ return true;
+ }
+
+ term->paste_fd = ret;
+ ret = ev_eloop_new_fd(term->eloop, &term->paste, ret,
+ EV_READABLE, paste_event, term);
+ if (ret) {
+ close(ret);
+ log_error("cannot create eloop fd: %d", ret);
+ return true;
+ }
+
+ return true;
+ }
+
+ if (SHL_HAS_BITS(mask, wlt_conf.grab_copy->mods) &&
+ sym == wlt_conf.grab_copy->keysym) {
+ if (term->copy) {
+ wl_data_source_destroy(term->copy);
+ free(term->copy_buf);
+ term->copy = NULL;
+ }
+
+ ret = wlt_display_new_data_source(term->disp, &term->copy);
+ if (ret) {
+ log_error("cannot create data source");
+ return true;
+ }
+
+ term->copy_len = tsm_screen_selection_copy(term->scr,
+ &term->copy_buf);
+ if (term->copy_len < 0) {
+ if (term->copy_len != -ENOENT)
+ log_error("cannot copy TSM selection: %d",
+ term->copy_len);
+ wl_data_source_destroy(term->copy);
+ term->copy = NULL;
+ return true;
+ }
+
+ wl_data_source_offer(term->copy, "text/plain;charset=utf-8");
+ wl_data_source_add_listener(term->copy, ©_listener, term);
+ wlt_display_set_selection(term->disp, term->copy);
+
+ return true;
+ }
+
if (tsm_vte_handle_keyboard(term->vte, sym, mask, ucs4)) {
tsm_screen_sb_reset(term->scr);
wlt_window_schedule_redraw(term->wnd);
{
struct wlt_terminal *term = data;
+ if (term->paste) {
+ ev_eloop_rm_fd(term->paste);
+ close(term->paste_fd);
+ }
tsm_vte_unref(term->vte);
tsm_screen_unref(term->scr);
free(term);
return -ENOMEM;
memset(term, 0, sizeof(*term));
term->wnd = wnd;
+ term->disp = wlt_window_get_display(wnd);
term->eloop = wlt_window_get_eloop(wnd);
term->cols = 80;
term->rows = 24;