GUACAMOLE-573: Copy terminal data directly into clipboard. Do not assume selected region will be strictly visible.
This commit is contained in:
parent
f87af06ad6
commit
c0d323828e
@ -20,33 +20,15 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "common/clipboard.h"
|
#include "common/clipboard.h"
|
||||||
#include "common/cursor.h"
|
|
||||||
#include "terminal/buffer.h"
|
#include "terminal/buffer.h"
|
||||||
#include "terminal/common.h"
|
|
||||||
#include "terminal/display.h"
|
#include "terminal/display.h"
|
||||||
#include "terminal/palette.h"
|
#include "terminal/select.h"
|
||||||
#include "terminal/terminal.h"
|
#include "terminal/terminal.h"
|
||||||
#include "terminal/terminal_handlers.h"
|
|
||||||
#include "terminal/types.h"
|
#include "terminal/types.h"
|
||||||
#include "terminal/typescript.h"
|
|
||||||
#include "terminal/xparsecolor.h"
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wchar.h>
|
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/error.h>
|
|
||||||
#include <guacamole/protocol.h>
|
|
||||||
#include <guacamole/socket.h>
|
#include <guacamole/socket.h>
|
||||||
#include <guacamole/timestamp.h>
|
#include <guacamole/unicode.h>
|
||||||
|
|
||||||
void guac_terminal_select_redraw(guac_terminal* terminal) {
|
void guac_terminal_select_redraw(guac_terminal* terminal) {
|
||||||
|
|
||||||
@ -135,37 +117,96 @@ void guac_terminal_select_update(guac_terminal* terminal, int row, int column) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int __guac_terminal_buffer_string(guac_terminal_buffer_row* row, int start, int end, char* string) {
|
/**
|
||||||
|
* Appends the text within the given subsection of a terminal row to the
|
||||||
|
* clipboard. The provided coordinates are considered inclusiveley (the
|
||||||
|
* characters at the start and end column are included in the copied
|
||||||
|
* text). Any out-of-bounds coordinates will be automatically clipped within
|
||||||
|
* the bounds of the given row.
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal instance associated with the buffer containing the
|
||||||
|
* text being copied and the clipboard receiving the copied text.
|
||||||
|
*
|
||||||
|
* @param row
|
||||||
|
* The row number of the text within the terminal to be copied into the
|
||||||
|
* clipboard, where the first (top-most) row in the terminal is row 0. Rows
|
||||||
|
* within the scrollback buffer (above the top-most row of the terminal)
|
||||||
|
* will be negative.
|
||||||
|
*
|
||||||
|
* @param start
|
||||||
|
* The first column of the text to be copied from the given row into the
|
||||||
|
* clipboard associated with the given terminal, where 0 is the first
|
||||||
|
* (left-most) column within the row.
|
||||||
|
*
|
||||||
|
* @param end
|
||||||
|
* The last column of the text to be copied from the given row into the
|
||||||
|
* clipboard associated with the given terminal, where 0 is the first
|
||||||
|
* (left-most) column within the row.
|
||||||
|
*/
|
||||||
|
static void guac_terminal_clipboard_append_row(guac_terminal* terminal,
|
||||||
|
int row, int start, int end) {
|
||||||
|
|
||||||
int length = 0;
|
char buffer[1024];
|
||||||
int i;
|
int i = start;
|
||||||
for (i=start; i<=end; i++) {
|
|
||||||
|
|
||||||
int codepoint = row->characters[i].value;
|
guac_terminal_buffer_row* buffer_row =
|
||||||
|
guac_terminal_buffer_get_row(terminal->buffer, row, 0);
|
||||||
|
|
||||||
/* If not null (blank), add to string */
|
/* If selection is entirely outside the bounds of the row, then there is
|
||||||
if (codepoint != 0 && codepoint != GUAC_CHAR_CONTINUATION) {
|
* nothing to append */
|
||||||
int bytes = guac_terminal_encode_utf8(codepoint, string);
|
if (start > buffer_row->length - 1)
|
||||||
string += bytes;
|
return;
|
||||||
length += bytes;
|
|
||||||
}
|
/* Clip given range to actual bounds of row */
|
||||||
|
if (end == -1 || end > buffer_row->length - 1)
|
||||||
|
end = buffer_row->length - 1;
|
||||||
|
|
||||||
|
/* Repeatedly convert chunks of terminal buffer rows until entire specified
|
||||||
|
* region has been appended to clipboard */
|
||||||
|
while (i <= end) {
|
||||||
|
|
||||||
|
int remaining = sizeof(buffer);
|
||||||
|
char* current = buffer;
|
||||||
|
|
||||||
|
/* Convert as many codepoints within the given range as possible */
|
||||||
|
for (i = start; i <= end; i++) {
|
||||||
|
|
||||||
|
int codepoint = buffer_row->characters[i].value;
|
||||||
|
|
||||||
|
/* Ignore null (blank) characters */
|
||||||
|
if (codepoint == 0 || codepoint == GUAC_CHAR_CONTINUATION)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Encode current codepoint as UTF-8 */
|
||||||
|
int bytes = guac_utf8_write(codepoint, current, remaining);
|
||||||
|
if (bytes == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
current += bytes;
|
||||||
|
remaining -= bytes;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return length;
|
/* Append converted buffer to clipboard */
|
||||||
|
guac_common_clipboard_append(terminal->clipboard, buffer, current - buffer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_terminal_select_end(guac_terminal* terminal, char* string) {
|
void guac_terminal_select_end(guac_terminal* terminal) {
|
||||||
|
|
||||||
|
guac_client* client = terminal->client;
|
||||||
|
guac_socket* socket = client->socket;
|
||||||
|
|
||||||
|
/* Reset current clipboard contents */
|
||||||
|
guac_common_clipboard_reset(terminal->clipboard, "text/plain");
|
||||||
|
|
||||||
/* Deselect */
|
/* Deselect */
|
||||||
terminal->text_selected = false;
|
terminal->text_selected = false;
|
||||||
guac_terminal_display_commit_select(terminal->display);
|
guac_terminal_display_commit_select(terminal->display);
|
||||||
|
|
||||||
guac_terminal_buffer_row* buffer_row;
|
|
||||||
|
|
||||||
int row;
|
|
||||||
|
|
||||||
int start_row, start_col;
|
int start_row, start_col;
|
||||||
int end_row, end_col;
|
int end_row, end_col;
|
||||||
|
|
||||||
@ -188,41 +229,30 @@ void guac_terminal_select_end(guac_terminal* terminal, char* string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If only one row, simply copy */
|
/* If only one row, simply copy */
|
||||||
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, start_row, 0);
|
if (end_row == start_row)
|
||||||
if (end_row == start_row) {
|
guac_terminal_clipboard_append_row(terminal, start_row, start_col, end_col);
|
||||||
if (buffer_row->length - 1 < end_col)
|
|
||||||
end_col = buffer_row->length - 1;
|
|
||||||
string += __guac_terminal_buffer_string(buffer_row, start_col, end_col, string);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise, copy multiple rows */
|
/* Otherwise, copy multiple rows */
|
||||||
else {
|
else {
|
||||||
|
|
||||||
/* Store first row */
|
/* Store first row */
|
||||||
string += __guac_terminal_buffer_string(buffer_row, start_col, buffer_row->length - 1, string);
|
guac_terminal_clipboard_append_row(terminal, start_row, start_col, -1);
|
||||||
|
|
||||||
/* Store all middle rows */
|
/* Store all middle rows */
|
||||||
for (row=start_row+1; row<end_row; row++) {
|
for (int row = start_row + 1; row < end_row; row++) {
|
||||||
|
guac_common_clipboard_append(terminal->clipboard, "\n", 1);
|
||||||
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
|
guac_terminal_clipboard_append_row(terminal, row, 0, -1);
|
||||||
|
|
||||||
*(string++) = '\n';
|
|
||||||
string += __guac_terminal_buffer_string(buffer_row, 0, buffer_row->length - 1, string);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store last row */
|
/* Store last row */
|
||||||
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, end_row, 0);
|
guac_common_clipboard_append(terminal->clipboard, "\n", 1);
|
||||||
if (buffer_row->length - 1 < end_col)
|
guac_terminal_clipboard_append_row(terminal, end_row, 0, end_col);
|
||||||
end_col = buffer_row->length - 1;
|
|
||||||
|
|
||||||
*(string++) = '\n';
|
|
||||||
string += __guac_terminal_buffer_string(buffer_row, 0, end_col, string);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Null terminator */
|
/* Send data */
|
||||||
*string = 0;
|
guac_common_clipboard_send(terminal->clipboard, client);
|
||||||
|
guac_socket_flush(socket);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1729,9 +1729,6 @@ int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
|
|||||||
static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
|
static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
|
||||||
int x, int y, int mask) {
|
int x, int y, int mask) {
|
||||||
|
|
||||||
guac_client* client = term->client;
|
|
||||||
guac_socket* socket = client->socket;
|
|
||||||
|
|
||||||
/* Determine which buttons were just released and pressed */
|
/* Determine which buttons were just released and pressed */
|
||||||
int released_mask = term->mouse_mask & ~mask;
|
int released_mask = term->mouse_mask & ~mask;
|
||||||
int pressed_mask = ~term->mouse_mask & mask;
|
int pressed_mask = ~term->mouse_mask & mask;
|
||||||
@ -1771,27 +1768,8 @@ static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
|
|||||||
if (term->text_selected) {
|
if (term->text_selected) {
|
||||||
|
|
||||||
/* If mouse button released, stop selection */
|
/* If mouse button released, stop selection */
|
||||||
if (released_mask & GUAC_CLIENT_MOUSE_LEFT) {
|
if (released_mask & GUAC_CLIENT_MOUSE_LEFT)
|
||||||
|
guac_terminal_select_end(term);
|
||||||
int selected_length;
|
|
||||||
|
|
||||||
/* End selection and get selected text */
|
|
||||||
int selectable_size = term->term_width * term->term_height * sizeof(char);
|
|
||||||
char* string = malloc(selectable_size);
|
|
||||||
guac_terminal_select_end(term, string);
|
|
||||||
|
|
||||||
selected_length = strnlen(string, selectable_size);
|
|
||||||
|
|
||||||
/* Store new data */
|
|
||||||
guac_common_clipboard_reset(term->clipboard, "text/plain");
|
|
||||||
guac_common_clipboard_append(term->clipboard, string, selected_length);
|
|
||||||
free(string);
|
|
||||||
|
|
||||||
/* Send data */
|
|
||||||
guac_common_clipboard_send(term->clipboard, client);
|
|
||||||
guac_socket_flush(socket);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise, just update */
|
/* Otherwise, just update */
|
||||||
else
|
else
|
||||||
|
@ -67,19 +67,16 @@ void guac_terminal_select_update(guac_terminal* terminal, int row, int column);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Ends text selection, removing any highlight and storing the selected
|
* Ends text selection, removing any highlight and storing the selected
|
||||||
* character data within the provided string buffer. This function should only
|
* character data within the clipboard associated with the given terminal. If
|
||||||
* be invoked while the guac_terminal is locked through a call to
|
* more text is selected than can fit within the clipboard, text at the end of
|
||||||
|
* the selected area will be dropped as necessary. This function should only be
|
||||||
|
* invoked while the guac_terminal is locked through a call to
|
||||||
* guac_terminal_lock().
|
* guac_terminal_lock().
|
||||||
*
|
*
|
||||||
* @param terminal
|
* @param terminal
|
||||||
* The guac_terminal instance associated with the text being selected.
|
* The guac_terminal instance associated with the text being selected.
|
||||||
*
|
|
||||||
* @param string
|
|
||||||
* The buffer which should receive the characters within the selected
|
|
||||||
* area. This buffer must already have been allocated with sufficient
|
|
||||||
* space to store the selected text.
|
|
||||||
*/
|
*/
|
||||||
void guac_terminal_select_end(guac_terminal* terminal, char* string);
|
void guac_terminal_select_end(guac_terminal* terminal);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user