mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-24 09:05:48 -04:00
Merge branch 'block-input-events-while-pasting' into master
Closes #101
This commit is contained in:
commit
0e89302da2
4 changed files with 220 additions and 66 deletions
|
|
@ -86,6 +86,8 @@
|
||||||
(https://codeberg.org/dnkl/foot/issues/97).
|
(https://codeberg.org/dnkl/foot/issues/97).
|
||||||
* Mouse events from being sent to client application when a mouse
|
* Mouse events from being sent to client application when a mouse
|
||||||
binding has consumed it.
|
binding has consumed it.
|
||||||
|
* Input events from getting mixed with paste data
|
||||||
|
(https://codeberg.org/dnkl/foot/issues/101).
|
||||||
|
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
|
||||||
117
selection.c
117
selection.c
|
|
@ -8,6 +8,7 @@
|
||||||
#include <wctype.h>
|
#include <wctype.h>
|
||||||
|
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
|
||||||
#define LOG_MODULE "selection"
|
#define LOG_MODULE "selection"
|
||||||
#define LOG_ENABLE_DBG 0
|
#define LOG_ENABLE_DBG 0
|
||||||
|
|
@ -993,12 +994,50 @@ selection_to_clipboard(struct seat *seat, struct terminal *term, uint32_t serial
|
||||||
}
|
}
|
||||||
|
|
||||||
struct clipboard_receive {
|
struct clipboard_receive {
|
||||||
|
int read_fd;
|
||||||
|
int timeout_fd;
|
||||||
|
struct itimerspec timeout;
|
||||||
|
|
||||||
/* Callback data */
|
/* Callback data */
|
||||||
void (*cb)(const char *data, size_t size, void *user);
|
void (*cb)(const char *data, size_t size, void *user);
|
||||||
void (*done)(void *user);
|
void (*done)(void *user);
|
||||||
void *user;
|
void *user;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
clipboard_receive_done(struct fdm *fdm, struct clipboard_receive *ctx)
|
||||||
|
{
|
||||||
|
fdm_del(fdm, ctx->timeout_fd);
|
||||||
|
fdm_del(fdm, ctx->read_fd);
|
||||||
|
ctx->done(ctx->user);
|
||||||
|
free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
fdm_receive_timeout(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
{
|
||||||
|
struct clipboard_receive *ctx = data;
|
||||||
|
if (events & EPOLLHUP)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
assert(events & EPOLLIN);
|
||||||
|
|
||||||
|
uint64_t expire_count;
|
||||||
|
ssize_t ret = read(fd, &expire_count, sizeof(expire_count));
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
LOG_ERRNO("failed to read clipboard timeout timer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_WARN("no data received from clipboard in %llu seconds, aborting",
|
||||||
|
(unsigned long long)ctx->timeout.it_value.tv_sec);
|
||||||
|
|
||||||
|
clipboard_receive_done(fdm, ctx);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
fdm_receive(struct fdm *fdm, int fd, int events, void *data)
|
fdm_receive(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
|
@ -1008,6 +1047,12 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data)
|
||||||
if ((events & EPOLLHUP) && !(events & EPOLLIN))
|
if ((events & EPOLLHUP) && !(events & EPOLLIN))
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
/* Reset timeout timer */
|
||||||
|
if (timerfd_settime(ctx->timeout_fd, 0, &ctx->timeout, NULL) < 0) {
|
||||||
|
LOG_ERRNO("failed to re-arm clipboard timeout timer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read until EOF */
|
/* Read until EOF */
|
||||||
while (true) {
|
while (true) {
|
||||||
char text[256];
|
char text[256];
|
||||||
|
|
@ -1044,9 +1089,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
fdm_del(fdm, fd);
|
clipboard_receive_done(fdm, ctx);
|
||||||
ctx->done(ctx->user);
|
|
||||||
free(ctx);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1055,28 +1098,52 @@ begin_receive_clipboard(struct terminal *term, int read_fd,
|
||||||
void (*cb)(const char *data, size_t size, void *user),
|
void (*cb)(const char *data, size_t size, void *user),
|
||||||
void (*done)(void *user), void *user)
|
void (*done)(void *user), void *user)
|
||||||
{
|
{
|
||||||
|
int timeout_fd = -1;
|
||||||
|
struct clipboard_receive *ctx = NULL;
|
||||||
|
|
||||||
int flags;
|
int flags;
|
||||||
if ((flags = fcntl(read_fd, F_GETFL)) < 0 ||
|
if ((flags = fcntl(read_fd, F_GETFL)) < 0 ||
|
||||||
fcntl(read_fd, F_SETFL, flags | O_NONBLOCK) < 0)
|
fcntl(read_fd, F_SETFL, flags | O_NONBLOCK) < 0)
|
||||||
{
|
{
|
||||||
LOG_ERRNO("failed to set O_NONBLOCK");
|
LOG_ERRNO("failed to set O_NONBLOCK");
|
||||||
close(read_fd);
|
goto err;
|
||||||
done(user);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct clipboard_receive *ctx = xmalloc(sizeof(*ctx));
|
timeout_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||||
|
if (timeout_fd < 0) {
|
||||||
|
LOG_ERRNO("failed to create clipboard timeout timer FD");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct itimerspec timeout = {.it_value = {.tv_sec = 2}};
|
||||||
|
if (timerfd_settime(timeout_fd, 0, &timeout, NULL) < 0) {
|
||||||
|
LOG_ERRNO("faild to arm clipboard timeout timer");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = xmalloc(sizeof(*ctx));
|
||||||
*ctx = (struct clipboard_receive) {
|
*ctx = (struct clipboard_receive) {
|
||||||
|
.read_fd = read_fd,
|
||||||
|
.timeout_fd = timeout_fd,
|
||||||
|
.timeout = timeout,
|
||||||
.cb = cb,
|
.cb = cb,
|
||||||
.done = done,
|
.done = done,
|
||||||
.user = user,
|
.user = user,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!fdm_add(term->fdm, read_fd, EPOLLIN, &fdm_receive, ctx)) {
|
if (!fdm_add(term->fdm, read_fd, EPOLLIN, &fdm_receive, ctx) ||
|
||||||
close(read_fd);
|
!fdm_add(term->fdm, timeout_fd, EPOLLIN, &fdm_receive_timeout, ctx))
|
||||||
free(ctx);
|
{
|
||||||
done(user);
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
free(ctx);
|
||||||
|
fdm_del(term->fdm, timeout_fd);
|
||||||
|
fdm_del(term->fdm, read_fd);
|
||||||
|
done(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -1115,7 +1182,8 @@ static void
|
||||||
from_clipboard_cb(const char *data, size_t size, void *user)
|
from_clipboard_cb(const char *data, size_t size, void *user)
|
||||||
{
|
{
|
||||||
struct terminal *term = user;
|
struct terminal *term = user;
|
||||||
term_to_slave(term, data, size);
|
assert(term->is_sending_paste_data);
|
||||||
|
term_paste_data_to_slave(term, data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -1124,18 +1192,31 @@ from_clipboard_done(void *user)
|
||||||
struct terminal *term = user;
|
struct terminal *term = user;
|
||||||
|
|
||||||
if (term->bracketed_paste)
|
if (term->bracketed_paste)
|
||||||
term_to_slave(term, "\033[201~", 6);
|
term_paste_data_to_slave(term, "\033[201~", 6);
|
||||||
|
|
||||||
|
term->is_sending_paste_data = false;
|
||||||
|
|
||||||
|
/* Make sure we send any queued up non-paste data */
|
||||||
|
if (tll_length(term->ptmx_buffers) > 0)
|
||||||
|
fdm_event_add(term->fdm, term->ptmx, EPOLLOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
selection_from_clipboard(struct seat *seat, struct terminal *term, uint32_t serial)
|
selection_from_clipboard(struct seat *seat, struct terminal *term, uint32_t serial)
|
||||||
{
|
{
|
||||||
|
if (term->is_sending_paste_data) {
|
||||||
|
/* We're already pasting... */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct wl_clipboard *clipboard = &seat->clipboard;
|
struct wl_clipboard *clipboard = &seat->clipboard;
|
||||||
if (clipboard->data_offer == NULL)
|
if (clipboard->data_offer == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
term->is_sending_paste_data = true;
|
||||||
|
|
||||||
if (term->bracketed_paste)
|
if (term->bracketed_paste)
|
||||||
term_to_slave(term, "\033[200~", 6);
|
term_paste_data_to_slave(term, "\033[200~", 6);
|
||||||
|
|
||||||
text_from_clipboard(
|
text_from_clipboard(
|
||||||
seat, term, &from_clipboard_cb, &from_clipboard_done, term);
|
seat, term, &from_clipboard_cb, &from_clipboard_done, term);
|
||||||
|
|
@ -1241,12 +1322,18 @@ selection_from_primary(struct seat *seat, struct terminal *term)
|
||||||
if (term->wl->primary_selection_device_manager == NULL)
|
if (term->wl->primary_selection_device_manager == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (term->is_sending_paste_data) {
|
||||||
|
/* We're already pasting... */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct wl_clipboard *clipboard = &seat->clipboard;
|
struct wl_clipboard *clipboard = &seat->clipboard;
|
||||||
if (clipboard->data_offer == NULL)
|
if (clipboard->data_offer == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
term->is_sending_paste_data = true;
|
||||||
if (term->bracketed_paste)
|
if (term->bracketed_paste)
|
||||||
term_to_slave(term, "\033[200~", 6);
|
term_paste_data_to_slave(term, "\033[200~", 6);
|
||||||
|
|
||||||
text_from_primary(seat, term, &from_clipboard_cb, &from_clipboard_done, term);
|
text_from_primary(seat, term, &from_clipboard_cb, &from_clipboard_done, term);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
159
terminal.c
159
terminal.c
|
|
@ -50,31 +50,38 @@ const char *const XCURSOR_RIGHT_SIDE = "right_side";
|
||||||
const char *const XCURSOR_TOP_SIDE = "top_side";
|
const char *const XCURSOR_TOP_SIDE = "top_side";
|
||||||
const char *const XCURSOR_BOTTOM_SIDE = "bottom_side";
|
const char *const XCURSOR_BOTTOM_SIDE = "bottom_side";
|
||||||
|
|
||||||
bool
|
static void
|
||||||
term_to_slave(struct terminal *term, const void *_data, size_t len)
|
enqueue_data_for_slave(const void *data, size_t len, size_t offset,
|
||||||
|
ptmx_buffer_list_t *buffer_list)
|
||||||
{
|
{
|
||||||
if (term->ptmx < 0) {
|
void *copy = xmalloc(len);
|
||||||
/* We're probably in "hold" */
|
memcpy(copy, data, len);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t async_idx = 0;
|
struct ptmx_buffer queued = {
|
||||||
if (tll_length(term->ptmx_buffer) > 0) {
|
.data = copy,
|
||||||
/* With a non-empty queue, EPOLLOUT has already been enabled */
|
.len = len,
|
||||||
goto enqueue_data;
|
.idx = offset,
|
||||||
}
|
};
|
||||||
|
tll_push_back(*buffer_list, queued);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
data_to_slave(struct terminal *term, const void *data, size_t len,
|
||||||
|
ptmx_buffer_list_t *buffer_list)
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* Try a synchronous write first. If we fail to write everything,
|
* Try a synchronous write first. If we fail to write everything,
|
||||||
* switch to asynchronous.
|
* switch to asynchronous.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
switch (async_write(term->ptmx, _data, len, &async_idx)) {
|
size_t async_idx = 0;
|
||||||
|
switch (async_write(term->ptmx, data, len, &async_idx)) {
|
||||||
case ASYNC_WRITE_REMAIN:
|
case ASYNC_WRITE_REMAIN:
|
||||||
/* Switch to asynchronous mode; let FDM write the remaining data */
|
/* Switch to asynchronous mode; let FDM write the remaining data */
|
||||||
if (!fdm_event_add(term->fdm, term->ptmx, EPOLLOUT))
|
if (!fdm_event_add(term->fdm, term->ptmx, EPOLLOUT))
|
||||||
return false;
|
return false;
|
||||||
goto enqueue_data;
|
enqueue_data_for_slave(data, len, async_idx, buffer_list);
|
||||||
|
return true;
|
||||||
|
|
||||||
case ASYNC_WRITE_DONE:
|
case ASYNC_WRITE_DONE:
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -87,24 +94,52 @@ term_to_slave(struct terminal *term, const void *_data, size_t len)
|
||||||
/* Shouldn't get here */
|
/* Shouldn't get here */
|
||||||
assert(false);
|
assert(false);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
enqueue_data:
|
bool
|
||||||
/*
|
term_paste_data_to_slave(struct terminal *term, const void *data, size_t len)
|
||||||
* We're in asynchronous mode - push data to queue and let the FDM
|
{
|
||||||
* handler take care of it
|
assert(term->is_sending_paste_data);
|
||||||
*/
|
|
||||||
{
|
|
||||||
void *copy = xmalloc(len);
|
|
||||||
memcpy(copy, _data, len);
|
|
||||||
|
|
||||||
struct ptmx_buffer queued = {
|
if (term->ptmx < 0) {
|
||||||
.data = copy,
|
/* We're probably in "hold" */
|
||||||
.len = len,
|
return false;
|
||||||
.idx = async_idx,
|
|
||||||
};
|
|
||||||
tll_push_back(term->ptmx_buffer, queued);
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
if (tll_length(term->ptmx_paste_buffers) > 0) {
|
||||||
|
/* Don't even try to send data *now* if there's queued up
|
||||||
|
* data, since that would result in events arriving out of
|
||||||
|
* order. */
|
||||||
|
enqueue_data_for_slave(data, len, 0, &term->ptmx_paste_buffers);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data_to_slave(term, data, len, &term->ptmx_paste_buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
term_to_slave(struct terminal *term, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
if (term->ptmx < 0) {
|
||||||
|
/* We're probably in "hold" */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tll_length(term->ptmx_buffers) > 0 || term->is_sending_paste_data) {
|
||||||
|
/*
|
||||||
|
* Don't even try to send data *now* if there's queued up
|
||||||
|
* data, since that would result in events arriving out of
|
||||||
|
* order.
|
||||||
|
*
|
||||||
|
* Furthermore, if we're currently sending paste data to the
|
||||||
|
* client, do *not* mix that stream with other events
|
||||||
|
* (https://codeberg.org/dnkl/foot/issues/101).
|
||||||
|
*/
|
||||||
|
enqueue_data_for_slave(data, len, 0, &term->ptmx_buffers);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data_to_slave(term, data, len, &term->ptmx_buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -113,28 +148,48 @@ fdm_ptmx_out(struct fdm *fdm, int fd, int events, void *data)
|
||||||
struct terminal *term = data;
|
struct terminal *term = data;
|
||||||
|
|
||||||
/* If there is no queued data, then we shouldn't be in asynchronous mode */
|
/* If there is no queued data, then we shouldn't be in asynchronous mode */
|
||||||
assert(tll_length(term->ptmx_buffer) > 0);
|
assert(tll_length(term->ptmx_buffers) > 0 ||
|
||||||
|
tll_length(term->ptmx_paste_buffers) > 0);
|
||||||
|
|
||||||
/* Don't use pop() since we may not be able to write the entire buffer */
|
/* Writes a single buffer, returns if not all of it could be written */
|
||||||
tll_foreach(term->ptmx_buffer, it) {
|
#define write_one_buffer(buffer_list) \
|
||||||
switch (async_write(term->ptmx, it->item.data, it->item.len, &it->item.idx)) {
|
{ \
|
||||||
case ASYNC_WRITE_DONE:
|
switch (async_write(term->ptmx, it->item.data, it->item.len, &it->item.idx)) { \
|
||||||
free(it->item.data);
|
case ASYNC_WRITE_DONE: \
|
||||||
tll_remove(term->ptmx_buffer, it);
|
free(it->item.data); \
|
||||||
break;
|
tll_remove(buffer_list, it); \
|
||||||
|
break; \
|
||||||
case ASYNC_WRITE_REMAIN:
|
case ASYNC_WRITE_REMAIN: \
|
||||||
/* to_slave() updated it->item.idx */
|
/* to_slave() updated it->item.idx */ \
|
||||||
return true;
|
return true; \
|
||||||
|
case ASYNC_WRITE_ERR: \
|
||||||
case ASYNC_WRITE_ERR:
|
LOG_ERRNO("failed to asynchronously write %zu bytes to slave", \
|
||||||
LOG_ERRNO("failed to asynchronously write %zu bytes to slave",
|
it->item.len - it->item.idx); \
|
||||||
it->item.len - it->item.idx);
|
return false; \
|
||||||
return false;
|
} \
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No more queued data, switch back to synchronous mode */
|
tll_foreach(term->ptmx_paste_buffers, it)
|
||||||
|
write_one_buffer(term->ptmx_paste_buffers);
|
||||||
|
|
||||||
|
/* If we get here, *all* paste data buffers were successfully
|
||||||
|
* flushed */
|
||||||
|
|
||||||
|
if (!term->is_sending_paste_data) {
|
||||||
|
tll_foreach(term->ptmx_buffers, it)
|
||||||
|
write_one_buffer(term->ptmx_buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we get here, *all* buffers were successfully flushed.
|
||||||
|
*
|
||||||
|
* Or, we're still sending paste data, in which case we do *not*
|
||||||
|
* want to send the "normal" queued up data
|
||||||
|
*
|
||||||
|
* In both cases, we want to *disable* the FDM callback since
|
||||||
|
* otherwise we'd just be called right away again, with nothing to
|
||||||
|
* write.
|
||||||
|
*/
|
||||||
fdm_event_del(term->fdm, term->ptmx, EPOLLOUT);
|
fdm_event_del(term->fdm, term->ptmx, EPOLLOUT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -840,7 +895,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
.conf = conf,
|
.conf = conf,
|
||||||
.quit = false,
|
.quit = false,
|
||||||
.ptmx = ptmx,
|
.ptmx = ptmx,
|
||||||
.ptmx_buffer = tll_init(),
|
.ptmx_buffers = tll_init(),
|
||||||
|
.ptmx_paste_buffers = tll_init(),
|
||||||
.font_sizes = xmalloc(sizeof(term->font_sizes[0]) * tll_length(conf->fonts)),
|
.font_sizes = xmalloc(sizeof(term->font_sizes[0]) * tll_length(conf->fonts)),
|
||||||
.font_dpi = 0.,
|
.font_dpi = 0.,
|
||||||
.font_subpixel = (conf->colors.alpha == 0xffff /* Can't do subpixel rendering on transparent background */
|
.font_subpixel = (conf->colors.alpha == 0xffff /* Can't do subpixel rendering on transparent background */
|
||||||
|
|
@ -1206,9 +1262,12 @@ term_destroy(struct terminal *term)
|
||||||
assert(tll_length(term->render.workers.queue) == 0);
|
assert(tll_length(term->render.workers.queue) == 0);
|
||||||
tll_free(term->render.workers.queue);
|
tll_free(term->render.workers.queue);
|
||||||
|
|
||||||
tll_foreach(term->ptmx_buffer, it)
|
tll_foreach(term->ptmx_buffers, it)
|
||||||
free(it->item.data);
|
free(it->item.data);
|
||||||
tll_free(term->ptmx_buffer);
|
tll_free(term->ptmx_buffers);
|
||||||
|
tll_foreach(term->ptmx_paste_buffers, it)
|
||||||
|
free(it->item.data);
|
||||||
|
tll_free(term->ptmx_paste_buffers);
|
||||||
tll_free(term->tab_stops);
|
tll_free(term->tab_stops);
|
||||||
|
|
||||||
tll_foreach(term->normal.sixel_images, it)
|
tll_foreach(term->normal.sixel_images, it)
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,8 @@ enum term_surface {
|
||||||
TERM_SURF_BUTTON_CLOSE,
|
TERM_SURF_BUTTON_CLOSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef tll(struct ptmx_buffer) ptmx_buffer_list_t;
|
||||||
|
|
||||||
struct terminal {
|
struct terminal {
|
||||||
struct fdm *fdm;
|
struct fdm *fdm;
|
||||||
struct reaper *reaper;
|
struct reaper *reaper;
|
||||||
|
|
@ -226,7 +228,9 @@ struct terminal {
|
||||||
float font_dpi;
|
float font_dpi;
|
||||||
enum fcft_subpixel font_subpixel;
|
enum fcft_subpixel font_subpixel;
|
||||||
|
|
||||||
tll(struct ptmx_buffer) ptmx_buffer;
|
bool is_sending_paste_data;
|
||||||
|
ptmx_buffer_list_t ptmx_buffers;
|
||||||
|
ptmx_buffer_list_t ptmx_paste_buffers;
|
||||||
|
|
||||||
enum cursor_origin origin;
|
enum cursor_origin origin;
|
||||||
enum cursor_keys cursor_keys_mode;
|
enum cursor_keys cursor_keys_mode;
|
||||||
|
|
@ -489,6 +493,8 @@ int term_destroy(struct terminal *term);
|
||||||
|
|
||||||
void term_reset(struct terminal *term, bool hard);
|
void term_reset(struct terminal *term, bool hard);
|
||||||
bool term_to_slave(struct terminal *term, const void *data, size_t len);
|
bool term_to_slave(struct terminal *term, const void *data, size_t len);
|
||||||
|
bool term_paste_data_to_slave(
|
||||||
|
struct terminal *term, const void *data, size_t len);
|
||||||
|
|
||||||
bool term_font_size_increase(struct terminal *term);
|
bool term_font_size_increase(struct terminal *term);
|
||||||
bool term_font_size_decrease(struct terminal *term);
|
bool term_font_size_decrease(struct terminal *term);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue