From f87af06ad69af16702ceb96477470607be060111 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 16 Jun 2018 18:12:19 -0700 Subject: [PATCH] GUACAMOLE-573: Move terminal text selection code into own file. --- src/terminal/Makefile.am | 2 + src/terminal/select.c | 228 +++++++++++++++++++++++++++++++ src/terminal/terminal.c | 179 +----------------------- src/terminal/terminal/select.h | 85 ++++++++++++ src/terminal/terminal/terminal.h | 17 --- 5 files changed, 316 insertions(+), 195 deletions(-) create mode 100644 src/terminal/select.c create mode 100644 src/terminal/terminal/select.h diff --git a/src/terminal/Makefile.am b/src/terminal/Makefile.am index 8121bd3e..1fbded1a 100644 --- a/src/terminal/Makefile.am +++ b/src/terminal/Makefile.am @@ -30,6 +30,7 @@ noinst_HEADERS = \ terminal/named-colors.h \ terminal/palette.h \ terminal/scrollbar.h \ + terminal/select.h \ terminal/terminal.h \ terminal/terminal_handlers.h \ terminal/types.h \ @@ -44,6 +45,7 @@ libguac_terminal_la_SOURCES = \ named-colors.c \ palette.c \ scrollbar.c \ + select.c \ terminal.c \ terminal_handlers.c \ typescript.c \ diff --git a/src/terminal/select.c b/src/terminal/select.c new file mode 100644 index 00000000..b9e8474b --- /dev/null +++ b/src/terminal/select.c @@ -0,0 +1,228 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" + +#include "common/clipboard.h" +#include "common/cursor.h" +#include "terminal/buffer.h" +#include "terminal/common.h" +#include "terminal/display.h" +#include "terminal/palette.h" +#include "terminal/terminal.h" +#include "terminal/terminal_handlers.h" +#include "terminal/types.h" +#include "terminal/typescript.h" +#include "terminal/xparsecolor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void guac_terminal_select_redraw(guac_terminal* terminal) { + + int start_row = terminal->selection_start_row + terminal->scroll_offset; + int start_column = terminal->selection_start_column; + + int end_row = terminal->selection_end_row + terminal->scroll_offset; + int end_column = terminal->selection_end_column; + + /* Update start/end columns to include character width */ + if (start_row > end_row || (start_row == end_row && start_column > end_column)) + start_column += terminal->selection_start_width - 1; + else + end_column += terminal->selection_end_width - 1; + + guac_terminal_display_select(terminal->display, start_row, start_column, end_row, end_column); + +} + +/** + * Locates the beginning of the character at the given row and column, updating + * the column to the starting column of that character. The width, if available, + * is returned. If the character has no defined width, 1 is returned. + */ +static int __guac_terminal_find_char(guac_terminal* terminal, int row, int* column) { + + int start_column = *column; + + guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0); + if (start_column < buffer_row->length) { + + /* Find beginning of character */ + guac_terminal_char* start_char = &(buffer_row->characters[start_column]); + while (start_column > 0 && start_char->value == GUAC_CHAR_CONTINUATION) { + start_char--; + start_column--; + } + + /* Use width, if available */ + if (start_char->value != GUAC_CHAR_CONTINUATION) { + *column = start_column; + return start_char->width; + } + + } + + /* Default to one column wide */ + return 1; + +} + +void guac_terminal_select_start(guac_terminal* terminal, int row, int column) { + + int width = __guac_terminal_find_char(terminal, row, &column); + + terminal->selection_start_row = + terminal->selection_end_row = row; + + terminal->selection_start_column = + terminal->selection_end_column = column; + + terminal->selection_start_width = + terminal->selection_end_width = width; + + terminal->text_selected = true; + + guac_terminal_select_redraw(terminal); + +} + +void guac_terminal_select_update(guac_terminal* terminal, int row, int column) { + + /* Only update if selection has changed */ + if (row != terminal->selection_end_row + || column < terminal->selection_end_column + || column >= terminal->selection_end_column + terminal->selection_end_width) { + + int width = __guac_terminal_find_char(terminal, row, &column); + + terminal->selection_end_row = row; + terminal->selection_end_column = column; + terminal->selection_end_width = width; + + guac_terminal_select_redraw(terminal); + } + +} + +int __guac_terminal_buffer_string(guac_terminal_buffer_row* row, int start, int end, char* string) { + + int length = 0; + int i; + for (i=start; i<=end; i++) { + + int codepoint = row->characters[i].value; + + /* If not null (blank), add to string */ + if (codepoint != 0 && codepoint != GUAC_CHAR_CONTINUATION) { + int bytes = guac_terminal_encode_utf8(codepoint, string); + string += bytes; + length += bytes; + } + + } + + return length; + +} + +void guac_terminal_select_end(guac_terminal* terminal, char* string) { + + /* Deselect */ + terminal->text_selected = false; + guac_terminal_display_commit_select(terminal->display); + + guac_terminal_buffer_row* buffer_row; + + int row; + + int start_row, start_col; + int end_row, end_col; + + /* Ensure proper ordering of start and end coords */ + if (terminal->selection_start_row < terminal->selection_end_row + || (terminal->selection_start_row == terminal->selection_end_row + && terminal->selection_start_column < terminal->selection_end_column)) { + + start_row = terminal->selection_start_row; + start_col = terminal->selection_start_column; + end_row = terminal->selection_end_row; + end_col = terminal->selection_end_column + terminal->selection_end_width - 1; + + } + else { + end_row = terminal->selection_start_row; + end_col = terminal->selection_start_column + terminal->selection_start_width - 1; + start_row = terminal->selection_end_row; + start_col = terminal->selection_end_column; + } + + /* If only one row, simply copy */ + buffer_row = guac_terminal_buffer_get_row(terminal->buffer, start_row, 0); + if (end_row == start_row) { + 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 */ + else { + + /* Store first row */ + string += __guac_terminal_buffer_string(buffer_row, start_col, buffer_row->length - 1, string); + + /* Store all middle rows */ + for (row=start_row+1; rowbuffer, row, 0); + + *(string++) = '\n'; + string += __guac_terminal_buffer_string(buffer_row, 0, buffer_row->length - 1, string); + + } + + /* Store last row */ + buffer_row = guac_terminal_buffer_get_row(terminal->buffer, end_row, 0); + if (buffer_row->length - 1 < end_col) + end_col = buffer_row->length - 1; + + *(string++) = '\n'; + string += __guac_terminal_buffer_string(buffer_row, 0, end_col, string); + + } + + /* Null terminator */ + *string = 0; + +} + diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c index 5ae2ee3d..e6c99d43 100644 --- a/src/terminal/terminal.c +++ b/src/terminal/terminal.c @@ -25,6 +25,7 @@ #include "terminal/common.h" #include "terminal/display.h" #include "terminal/palette.h" +#include "terminal/select.h" #include "terminal/terminal.h" #include "terminal/terminal_handlers.h" #include "terminal/types.h" @@ -1269,184 +1270,6 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, } -void guac_terminal_select_redraw(guac_terminal* terminal) { - - int start_row = terminal->selection_start_row + terminal->scroll_offset; - int start_column = terminal->selection_start_column; - - int end_row = terminal->selection_end_row + terminal->scroll_offset; - int end_column = terminal->selection_end_column; - - /* Update start/end columns to include character width */ - if (start_row > end_row || (start_row == end_row && start_column > end_column)) - start_column += terminal->selection_start_width - 1; - else - end_column += terminal->selection_end_width - 1; - - guac_terminal_display_select(terminal->display, start_row, start_column, end_row, end_column); - -} - -/** - * Locates the beginning of the character at the given row and column, updating - * the column to the starting column of that character. The width, if available, - * is returned. If the character has no defined width, 1 is returned. - */ -static int __guac_terminal_find_char(guac_terminal* terminal, int row, int* column) { - - int start_column = *column; - - guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0); - if (start_column < buffer_row->length) { - - /* Find beginning of character */ - guac_terminal_char* start_char = &(buffer_row->characters[start_column]); - while (start_column > 0 && start_char->value == GUAC_CHAR_CONTINUATION) { - start_char--; - start_column--; - } - - /* Use width, if available */ - if (start_char->value != GUAC_CHAR_CONTINUATION) { - *column = start_column; - return start_char->width; - } - - } - - /* Default to one column wide */ - return 1; - -} - -void guac_terminal_select_start(guac_terminal* terminal, int row, int column) { - - int width = __guac_terminal_find_char(terminal, row, &column); - - terminal->selection_start_row = - terminal->selection_end_row = row; - - terminal->selection_start_column = - terminal->selection_end_column = column; - - terminal->selection_start_width = - terminal->selection_end_width = width; - - terminal->text_selected = true; - - guac_terminal_select_redraw(terminal); - -} - -void guac_terminal_select_update(guac_terminal* terminal, int row, int column) { - - /* Only update if selection has changed */ - if (row != terminal->selection_end_row - || column < terminal->selection_end_column - || column >= terminal->selection_end_column + terminal->selection_end_width) { - - int width = __guac_terminal_find_char(terminal, row, &column); - - terminal->selection_end_row = row; - terminal->selection_end_column = column; - terminal->selection_end_width = width; - - guac_terminal_select_redraw(terminal); - } - -} - -int __guac_terminal_buffer_string(guac_terminal_buffer_row* row, int start, int end, char* string) { - - int length = 0; - int i; - for (i=start; i<=end; i++) { - - int codepoint = row->characters[i].value; - - /* If not null (blank), add to string */ - if (codepoint != 0 && codepoint != GUAC_CHAR_CONTINUATION) { - int bytes = guac_terminal_encode_utf8(codepoint, string); - string += bytes; - length += bytes; - } - - } - - return length; - -} - -void guac_terminal_select_end(guac_terminal* terminal, char* string) { - - /* Deselect */ - terminal->text_selected = false; - guac_terminal_display_commit_select(terminal->display); - - guac_terminal_buffer_row* buffer_row; - - int row; - - int start_row, start_col; - int end_row, end_col; - - /* Ensure proper ordering of start and end coords */ - if (terminal->selection_start_row < terminal->selection_end_row - || (terminal->selection_start_row == terminal->selection_end_row - && terminal->selection_start_column < terminal->selection_end_column)) { - - start_row = terminal->selection_start_row; - start_col = terminal->selection_start_column; - end_row = terminal->selection_end_row; - end_col = terminal->selection_end_column + terminal->selection_end_width - 1; - - } - else { - end_row = terminal->selection_start_row; - end_col = terminal->selection_start_column + terminal->selection_start_width - 1; - start_row = terminal->selection_end_row; - start_col = terminal->selection_end_column; - } - - /* If only one row, simply copy */ - buffer_row = guac_terminal_buffer_get_row(terminal->buffer, start_row, 0); - if (end_row == start_row) { - 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 */ - else { - - /* Store first row */ - string += __guac_terminal_buffer_string(buffer_row, start_col, buffer_row->length - 1, string); - - /* Store all middle rows */ - for (row=start_row+1; rowbuffer, row, 0); - - *(string++) = '\n'; - string += __guac_terminal_buffer_string(buffer_row, 0, buffer_row->length - 1, string); - - } - - /* Store last row */ - buffer_row = guac_terminal_buffer_get_row(terminal->buffer, end_row, 0); - if (buffer_row->length - 1 < end_col) - end_col = buffer_row->length - 1; - - *(string++) = '\n'; - string += __guac_terminal_buffer_string(buffer_row, 0, end_col, string); - - } - - /* Null terminator */ - *string = 0; - -} - void guac_terminal_copy_columns(guac_terminal* terminal, int row, int start_column, int end_column, int offset) { diff --git a/src/terminal/terminal/select.h b/src/terminal/terminal/select.h new file mode 100644 index 00000000..936ae7a4 --- /dev/null +++ b/src/terminal/terminal/select.h @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#ifndef GUAC_TERMINAL_SELECT_H +#define GUAC_TERMINAL_SELECT_H + +#include "config.h" +#include "terminal.h" + +/** + * Marks the start of text selection at the given row and column. Any existing + * selection is cleared. This function should only be invoked while the + * guac_terminal is locked through a call to guac_terminal_lock(). + * + * @param terminal + * The guac_terminal instance associated with the text being selected. + * + * @param row + * The row number of the character at the start of the text selection, + * 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 column + * The column number of the character at the start of the text selection, + * where the first (left-most) column in the terminal is column 0. + */ +void guac_terminal_select_start(guac_terminal* terminal, int row, int column); + +/** + * Updates the end of text selection at the given row and column. This function + * should only be invoked while the guac_terminal is locked through a call to + * guac_terminal_lock(). + * + * @param terminal + * The guac_terminal instance associated with the text being selected. + * + * @param row + * The row number of the character at the current end of the text + * selection, 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 column + * The column number of the character at the current end of the text + * selection, where the first (left-most) column in the terminal is + * column 0. + */ +void guac_terminal_select_update(guac_terminal* terminal, int row, int column); + +/** + * Ends text selection, removing any highlight and storing the selected + * character data within the provided string buffer. This function should only + * be invoked while the guac_terminal is locked through a call to + * guac_terminal_lock(). + * + * @param terminal + * 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); + +#endif + diff --git a/src/terminal/terminal/terminal.h b/src/terminal/terminal/terminal.h index 6094c393..67b5b3ee 100644 --- a/src/terminal/terminal/terminal.h +++ b/src/terminal/terminal/terminal.h @@ -697,23 +697,6 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, int amount); */ void guac_terminal_scroll_display_up(guac_terminal* terminal, int amount); -/** - * Marks the start of text selection at the given row and column. - */ -void guac_terminal_select_start(guac_terminal* terminal, int row, int column); - -/** - * Updates the end of text selection at the given row and column. - */ -void guac_terminal_select_update(guac_terminal* terminal, int row, int column); - -/** - * Ends text selection, removing any highlight. Character data is stored in the - * string buffer provided. - */ -void guac_terminal_select_end(guac_terminal* terminal, char* string); - - /* LOW-LEVEL TERMINAL OPERATIONS */