summaryrefslogtreecommitdiff
path: root/clients/terminal.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/terminal.c')
-rw-r--r--clients/terminal.c383
1 files changed, 283 insertions, 100 deletions
diff --git a/clients/terminal.c b/clients/terminal.c
index cec1d67e..e2a6236c 100644
--- a/clients/terminal.c
+++ b/clients/terminal.c
@@ -38,6 +38,8 @@
#include <wchar.h>
#include <locale.h>
+#include <linux/input.h>
+
#include <wayland-client.h>
#include "../shared/config-parser.h"
@@ -426,6 +428,7 @@ struct terminal {
struct window *window;
struct widget *widget;
struct display *display;
+ char *title;
union utf8_char *data;
struct task io_task;
char *tab_ruler;
@@ -441,8 +444,11 @@ struct terminal {
character_set saved_cs, saved_g0, saved_g1;
keyboard_mode key_mode;
int data_pitch, attr_pitch; /* The width in bytes of a line */
- int width, height, start, row, column;
+ int width, height, row, column, max_width;
+ uint32_t buffer_height;
+ uint32_t start, end, saved_start, log_size;
int saved_row, saved_column;
+ int scrolling;
int send_cursor_position;
int fd, master;
uint32_t modifiers;
@@ -461,7 +467,7 @@ struct terminal {
uint32_t hide_cursor_serial;
struct wl_data_source *selection;
- uint32_t button_time;
+ uint32_t click_time;
int dragging, click_count;
int selection_start_x, selection_start_y;
int selection_end_x, selection_end_y;
@@ -545,9 +551,9 @@ terminal_get_row(struct terminal *terminal, int row)
{
int index;
- index = (row + terminal->start) % terminal->height;
+ index = (row + terminal->start) & (terminal->buffer_height - 1);
- return &terminal->data[index * terminal->width];
+ return (void *) terminal->data + index * terminal->data_pitch;
}
static struct attr*
@@ -555,9 +561,9 @@ terminal_get_attr_row(struct terminal *terminal, int row)
{
int index;
- index = (row + terminal->start) % terminal->height;
+ index = (row + terminal->start) & (terminal->buffer_height - 1);
- return &terminal->data_attr[index * terminal->width];
+ return (void *) terminal->data_attr + index * terminal->attr_pitch;
}
union decoded_attr {
@@ -620,18 +626,16 @@ terminal_scroll_buffer(struct terminal *terminal, int d)
{
int i;
- d = d % (terminal->height + 1);
- terminal->start = (terminal->start + d) % terminal->height;
- if (terminal->start < 0) terminal->start = terminal->height + terminal->start;
- if(d < 0) {
+ terminal->start += d;
+ if (d < 0) {
d = 0 - d;
- for(i = 0; i < d; i++) {
+ for (i = 0; i < d; i++) {
memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
attr_init(terminal_get_attr_row(terminal, i),
terminal->curr_attr, terminal->width);
}
} else {
- for(i = terminal->height - d; i < terminal->height; i++) {
+ for (i = terminal->height - d; i < terminal->height; i++) {
memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
attr_init(terminal_get_attr_row(terminal, i),
terminal->curr_attr, terminal->width);
@@ -733,68 +737,90 @@ terminal_shift_line(struct terminal *terminal, int d)
}
static void
-terminal_resize_cells(struct terminal *terminal, int width, int height)
+terminal_resize_cells(struct terminal *terminal,
+ int width, int height)
{
- size_t size;
union utf8_char *data;
struct attr *data_attr;
char *tab_ruler;
int data_pitch, attr_pitch;
int i, l, total_rows;
+ uint32_t d, uheight = height;
struct rectangle allocation;
struct winsize ws;
+ if (uheight > terminal->buffer_height)
+ height = terminal->buffer_height;
+
if (terminal->width == width && terminal->height == height)
return;
- data_pitch = width * sizeof(union utf8_char);
- size = data_pitch * height;
- data = zalloc(size);
- attr_pitch = width * sizeof(struct attr);
- data_attr = malloc(attr_pitch * height);
- tab_ruler = zalloc(width);
- attr_init(data_attr, terminal->curr_attr, width * height);
- if (terminal->data && terminal->data_attr) {
- if (width > terminal->width)
- l = terminal->width;
- else
- l = width;
-
- if (terminal->height > height) {
- total_rows = height;
- i = 1 + terminal->row - height;
- if (i > 0) {
- terminal->start = (terminal->start + i) % terminal->height;
- terminal->row = terminal->row - i;
- }
- } else {
- total_rows = terminal->height;
+ if (terminal->data && width <= terminal->max_width) {
+ d = 0;
+ if (height < terminal->height && height <= terminal->row)
+ d = terminal->height - height;
+ else if (height > terminal->height &&
+ terminal->height - 1 == terminal->row) {
+ d = terminal->height - height;
+ if (terminal->log_size < uheight)
+ d = -terminal->start;
}
- for (i = 0; i < total_rows; i++) {
- memcpy(&data[width * i],
- terminal_get_row(terminal, i),
- l * sizeof(union utf8_char));
- memcpy(&data_attr[width * i],
- terminal_get_attr_row(terminal, i),
- l * sizeof(struct attr));
+ terminal->start += d;
+ terminal->row -= d;
+ } else {
+ terminal->max_width = width;
+ data_pitch = width * sizeof(union utf8_char);
+ data = zalloc(data_pitch * terminal->buffer_height);
+ attr_pitch = width * sizeof(struct attr);
+ data_attr = malloc(attr_pitch * terminal->buffer_height);
+ tab_ruler = zalloc(width);
+ attr_init(data_attr, terminal->curr_attr,
+ width * terminal->buffer_height);
+
+ if (terminal->data && terminal->data_attr) {
+ if (width > terminal->width)
+ l = terminal->width;
+ else
+ l = width;
+
+ if (terminal->height > height) {
+ total_rows = height;
+ i = 1 + terminal->row - height;
+ if (i > 0) {
+ terminal->start += i;
+ terminal->row = terminal->row - i;
+ }
+ } else {
+ total_rows = terminal->height;
+ }
+
+ for (i = 0; i < total_rows; i++) {
+ memcpy(&data[width * i],
+ terminal_get_row(terminal, i),
+ l * sizeof(union utf8_char));
+ memcpy(&data_attr[width * i],
+ terminal_get_attr_row(terminal, i),
+ l * sizeof(struct attr));
+ }
+
+ free(terminal->data);
+ free(terminal->data_attr);
+ free(terminal->tab_ruler);
}
- free(terminal->data);
- free(terminal->data_attr);
- free(terminal->tab_ruler);
+ terminal->data_pitch = data_pitch;
+ terminal->attr_pitch = attr_pitch;
+ terminal->data = data;
+ terminal->data_attr = data_attr;
+ terminal->tab_ruler = tab_ruler;
+ terminal->start = 0;
}
- terminal->data_pitch = data_pitch;
- terminal->attr_pitch = attr_pitch;
terminal->margin_bottom =
height - (terminal->height - terminal->margin_bottom);
terminal->width = width;
terminal->height = height;
- terminal->data = data;
- terminal->data_attr = data_attr;
- terminal->tab_ruler = tab_ruler;
- terminal->start = 0;
terminal_init_tabs(terminal);
/* Update the window size */
@@ -812,7 +838,7 @@ resize_handler(struct widget *widget,
{
struct terminal *terminal = data;
int32_t columns, rows, m;
-
+ char *p;
m = 2 * terminal->margin;
columns = (width - m) / (int32_t) terminal->average_width;
rows = (height - m) / (int32_t) terminal->extents.height;
@@ -822,6 +848,9 @@ resize_handler(struct widget *widget,
width = columns * terminal->average_width + m;
height = rows * terminal->extents.height + m;
widget_set_size(terminal->widget, width, height);
+ asprintf(&p, "%s — [%dx%d]", terminal->title, columns, rows);
+ window_set_title(terminal->window, p);
+ free(p);
}
terminal_resize_cells(terminal, columns, rows);
@@ -840,7 +869,7 @@ terminal_resize(struct terminal *terminal, int columns, int rows)
width = columns * terminal->average_width + m;
height = rows * terminal->extents.height + m;
- frame_set_child_size(terminal->widget, width, height);
+ window_frame_set_child_size(terminal->widget, width, height);
}
struct color_scheme DEFAULT_COLORS = {
@@ -1232,6 +1261,8 @@ handle_osc(struct terminal *terminal)
case 0: /* Icon name and window title */
case 1: /* Icon label */
case 2: /* Window title*/
+ free(terminal->title);
+ terminal->title = strdup(p);
window_set_title(terminal->window, p);
break;
case 7: /* shell cwd as uri */
@@ -1383,12 +1414,9 @@ handle_escape(struct terminal *terminal)
terminal->curr_attr, terminal->width);
}
} else if (args[0] == 2) {
- for (i = 0; i < terminal->height; i++) {
- memset(terminal_get_row(terminal, i),
- 0, terminal->data_pitch);
- attr_init(terminal_get_attr_row(terminal, i),
- terminal->curr_attr, terminal->width);
- }
+ /* Clear screen by scrolling contents out */
+ terminal_scroll_buffer(terminal,
+ terminal->end - terminal->start);
}
break;
case 'K': /* EL */
@@ -1827,7 +1855,7 @@ handle_special_char(struct terminal *terminal, char c)
case '\v':
case '\f':
terminal->row++;
- if(terminal->row > terminal->margin_bottom) {
+ if (terminal->row > terminal->margin_bottom) {
terminal->row = terminal->margin_bottom;
terminal_scroll(terminal, +1);
}
@@ -1930,6 +1958,13 @@ handle_char(struct terminal *terminal, union utf8_char utf8)
row[terminal->column] = utf8;
attr_row[terminal->column++] = terminal->curr_attr;
+ if (terminal->row + terminal->start + 1 > terminal->end)
+ terminal->end = terminal->row + terminal->start + 1;
+ if (terminal->end == terminal->buffer_height)
+ terminal->log_size = terminal->buffer_height;
+ else if (terminal->log_size < terminal->buffer_height)
+ terminal->log_size = terminal->end;
+
/* cursor jump for wide character. */
if (is_wide(utf8))
row[terminal->column++].ch = 0x200B; /* space glyph */
@@ -2177,45 +2212,90 @@ fullscreen_handler(struct window *window, void *data)
}
static void
-close_handler(struct window *window, void *data)
+close_handler(void *data)
{
struct terminal *terminal = data;
terminal_destroy(terminal);
}
+static void
+terminal_copy(struct terminal *terminal, struct input *input)
+{
+ terminal->selection =
+ display_create_data_source(terminal->display);
+ wl_data_source_offer(terminal->selection,
+ "text/plain;charset=utf-8");
+ wl_data_source_add_listener(terminal->selection,
+ &data_source_listener, terminal);
+ input_set_selection(input, terminal->selection,
+ display_get_serial(terminal->display));
+}
+
+static void
+terminal_paste(struct terminal *terminal, struct input *input)
+{
+ input_receive_selection_data_to_fd(input,
+ "text/plain;charset=utf-8",
+ terminal->master);
+
+}
+
+static void
+terminal_new_instance(struct terminal *terminal)
+{
+ struct terminal *new_terminal;
+
+ new_terminal = terminal_create(terminal->display);
+ if (terminal_run(new_terminal, option_shell))
+ terminal_destroy(new_terminal);
+}
+
static int
handle_bound_key(struct terminal *terminal,
struct input *input, uint32_t sym, uint32_t time)
{
- struct terminal *new_terminal;
-
switch (sym) {
case XKB_KEY_X:
/* Cut selection; terminal doesn't do cut, fall
* through to copy. */
case XKB_KEY_C:
- terminal->selection =
- display_create_data_source(terminal->display);
- wl_data_source_offer(terminal->selection,
- "text/plain;charset=utf-8");
- wl_data_source_add_listener(terminal->selection,
- &data_source_listener, terminal);
- input_set_selection(input, terminal->selection,
- display_get_serial(terminal->display));
+ terminal_copy(terminal, input);
return 1;
case XKB_KEY_V:
- input_receive_selection_data_to_fd(input,
- "text/plain;charset=utf-8",
- terminal->master);
+ terminal_paste(terminal, input);
+ return 1;
+ case XKB_KEY_N:
+ terminal_new_instance(terminal);
+ return 1;
+
+ case XKB_KEY_Up:
+ if (!terminal->scrolling)
+ terminal->saved_start = terminal->start;
+ if (terminal->start == terminal->end - terminal->log_size)
+ return 1;
+ terminal->scrolling = 1;
+ terminal->start--;
+ terminal->row++;
+ terminal->selection_start_row++;
+ terminal->selection_end_row++;
+ widget_schedule_redraw(terminal->widget);
return 1;
- case XKB_KEY_N:
- new_terminal = terminal_create(terminal->display);
- if (terminal_run(new_terminal, option_shell))
- terminal_destroy(new_terminal);
+ case XKB_KEY_Down:
+ if (!terminal->scrolling)
+ terminal->saved_start = terminal->start;
+
+ if (terminal->start == terminal->saved_start)
+ return 1;
+ terminal->scrolling = 1;
+ terminal->start++;
+ terminal->row--;
+ terminal->selection_start_row--;
+ terminal->selection_end_row--;
+ widget_schedule_redraw(terminal->widget);
return 1;
default:
@@ -2231,7 +2311,7 @@ key_handler(struct window *window, struct input *input, uint32_t time,
struct terminal *terminal = data;
char ch[MAX_RESPONSE];
uint32_t modifiers, serial;
- int ret, len = 0;
+ int ret, len = 0, d;
bool convert_utf8 = true;
modifiers = input_get_modifiers(input);
@@ -2435,6 +2515,16 @@ key_handler(struct window *window, struct input *input, uint32_t time,
}
if (state == WL_KEYBOARD_KEY_STATE_PRESSED && len > 0) {
+ if (terminal->scrolling) {
+ d = terminal->saved_start - terminal->start;
+ terminal->row -= d;
+ terminal->selection_start_row -= d;
+ terminal->selection_end_row -= d;
+ terminal->start = terminal->saved_start;
+ terminal->scrolling = 0;
+ widget_schedule_redraw(terminal->widget);
+ }
+
terminal_write(terminal, ch, len);
/* Hide cursor, except if this was coming from a
@@ -2564,37 +2654,81 @@ recompute_selection(struct terminal *terminal)
}
static void
+menu_func(struct window *window, struct input *input, int index, void *data)
+{
+ struct terminal *terminal = data;
+
+ fprintf(stderr, "picked entry %d\n", index);
+
+ switch (index) {
+ case 0:
+ terminal_new_instance(terminal);
+ break;
+ case 1:
+ terminal_copy(terminal, input);
+ break;
+ case 2:
+ terminal_paste(terminal, input);
+ break;
+ }
+}
+
+static void
+show_menu(struct terminal *terminal, struct input *input, uint32_t time)
+{
+ int32_t x, y;
+ static const char *entries[] = {
+ "Open Terminal", "Copy", "Paste"
+ };
+
+ input_get_position(input, &x, &y);
+ window_show_menu(terminal->display, input, time, terminal->window,
+ x - 10, y - 10, menu_func,
+ entries, ARRAY_LENGTH(entries));
+}
+
+static void
+click_handler(struct widget *widget, struct terminal *terminal,
+ struct input *input, int32_t x, int32_t y,
+ uint32_t time)
+{
+ if (time - terminal->click_time < 500)
+ terminal->click_count++;
+ else
+ terminal->click_count = 1;
+
+ terminal->click_time = time;
+ terminal->dragging = (terminal->click_count - 1) % 3 + SELECT_CHAR;
+
+ terminal->selection_end_x = terminal->selection_start_x = x;
+ terminal->selection_end_y = terminal->selection_start_y = y;
+ if (recompute_selection(terminal))
+ widget_schedule_redraw(widget);
+}
+
+static void
button_handler(struct widget *widget,
struct input *input, uint32_t time,
uint32_t button,
enum wl_pointer_button_state state, void *data)
{
struct terminal *terminal = data;
+ int32_t x, y;
switch (button) {
- case 272:
+ case BTN_LEFT:
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
-
- if (time - terminal->button_time < 500)
- terminal->click_count++;
- else
- terminal->click_count = 1;
-
- terminal->button_time = time;
- terminal->dragging =
- (terminal->click_count - 1) % 3 + SELECT_CHAR;
-
- input_get_position(input,
- &terminal->selection_start_x,
- &terminal->selection_start_y);
- terminal->selection_end_x = terminal->selection_start_x;
- terminal->selection_end_y = terminal->selection_start_y;
- if (recompute_selection(terminal))
- widget_schedule_redraw(widget);
+ input_get_position(input, &x, &y);
+ click_handler(widget, terminal, input, x, y, time);
} else {
terminal->dragging = SELECT_NONE;
}
break;
+
+ case BTN_RIGHT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ show_menu(terminal, input, time);
+ break;
}
}
@@ -2602,6 +2736,11 @@ static int
enter_handler(struct widget *widget,
struct input *input, float x, float y, void *data)
{
+ struct terminal *terminal = data;
+
+ /* Reset title to get rid of resizing '[WxH]' in titlebar */
+ window_set_title(terminal->window, terminal->title);
+
return CURSOR_IBEAM;
}
@@ -2634,6 +2773,43 @@ output_handler(struct window *window, struct output *output, int enter,
window_schedule_redraw(window);
}
+static void
+touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct terminal *terminal = data;
+
+ if (id == 0)
+ click_handler(widget, terminal, input, x, y, time);
+}
+
+static void
+touch_up_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id, void *data)
+{
+ struct terminal *terminal = data;
+
+ if (id == 0)
+ terminal->dragging = SELECT_NONE;
+}
+
+static void
+touch_motion_handler(struct widget *widget, struct input *input,
+ uint32_t time, int32_t id, float x, float y, void *data)
+{
+ struct terminal *terminal = data;
+
+ if (terminal->dragging &&
+ id == 0) {
+ terminal->selection_end_x = (int)x;
+ terminal->selection_end_y = (int)y;
+
+ if (recompute_selection(terminal))
+ widget_schedule_redraw(widget);
+ }
+}
+
#ifndef howmany
#define howmany(x, y) (((x) + ((y) - 1)) / (y))
#endif
@@ -2652,8 +2828,9 @@ terminal_create(struct display *display)
terminal->margin_top = 0;
terminal->margin_bottom = -1;
terminal->window = window_create(display);
- terminal->widget = frame_create(terminal->window, terminal);
- window_set_title(terminal->window, "Wayland Terminal");
+ terminal->widget = window_frame_create(terminal->window, terminal);
+ terminal->title = strdup("Wayland Terminal");
+ window_set_title(terminal->window, terminal->title);
widget_set_transparent(terminal->widget, 0);
init_state_machine(&terminal->state_machine);
@@ -2661,6 +2838,8 @@ terminal_create(struct display *display)
terminal->display = display;
terminal->margin = 5;
+ terminal->buffer_height = 1024;
+ terminal->end = 1;
window_set_user_data(terminal->window, terminal);
window_set_key_handler(terminal->window, key_handler);
@@ -2678,6 +2857,9 @@ terminal_create(struct display *display)
widget_set_button_handler(terminal->widget, button_handler);
widget_set_enter_handler(terminal->widget, enter_handler);
widget_set_motion_handler(terminal->widget, motion_handler);
+ widget_set_touch_up_handler(terminal->widget, touch_up_handler);
+ widget_set_touch_down_handler(terminal->widget, touch_down_handler);
+ widget_set_touch_motion_handler(terminal->widget, touch_motion_handler);
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
cr = cairo_create(surface);
@@ -2726,6 +2908,7 @@ terminal_destroy(struct terminal *terminal)
if (wl_list_empty(&terminal_list))
display_exit(terminal->display);
+ free(terminal->title);
free(terminal);
}