Port multi-click handler to C

This commit is contained in:
Kovid Goyal
2017-09-14 10:44:20 +05:30
parent a921180224
commit cf6894d74f
6 changed files with 108 additions and 50 deletions

View File

@@ -21,7 +21,7 @@ from .keys import (
)
from .session import create_session
from .tabs import SpecialWindow, TabManager
from .utils import safe_print, get_primary_selection
from .utils import safe_print, get_primary_selection, set_primary_selection
from .window import load_shader_programs
@@ -288,6 +288,13 @@ class Boss:
text = get_primary_selection()
self.paste_to_active_window(text)
def set_primary_selection(self):
w = self.active_window
if w is not None and not w.destroyed:
text = w.text_for_selection()
if text:
set_primary_selection(text)
def next_tab(self):
self.tab_manager.next_tab()

View File

@@ -38,8 +38,9 @@ cell_for_pos(Window *w, unsigned int *x, unsigned int *y) {
return ret;
}
void
handle_move_event(Window *w, int UNUSED button, int UNUSED modifiers, unsigned int UNUSED window_idx) {
#define HANDLER(name) static inline void name(Window UNUSED *w, int UNUSED button, int UNUSED modifiers, unsigned int UNUSED window_idx)
HANDLER(handle_move_event) {
unsigned int x, y;
if (cell_for_pos(w, &x, &y)) {
Line *line = screen_visual_line(w->render_data.screen, y);
@@ -50,8 +51,46 @@ handle_move_event(Window *w, int UNUSED button, int UNUSED modifiers, unsigned i
}
}
void
handle_button_event(Window *w, int button, int modifiers, unsigned int window_idx) {
static inline void
multi_click(Window *w, unsigned int count) {
Screen *screen = w->render_data.screen;
index_type start, end;
bool found_selection = false;
switch(count) {
case 2:
found_selection = screen_selection_range_for_word(screen, w->mouse_cell_x, w->mouse_cell_y, &start, &end);
break;
case 3:
found_selection = screen_selection_range_for_line(screen, w->mouse_cell_y, &start, &end);
break;
default:
break;
}
if (found_selection) {
screen_start_selection(screen, start, w->mouse_cell_y);
screen_update_selection(screen, end, w->mouse_cell_y, true);
call_boss(set_primary_selection, NULL);
}
}
HANDLER(add_click) {
ClickQueue *q = &w->click_queue;
if (q->length == CLICK_QUEUE_SZ) { memmove(q->clicks, q->clicks + 1, sizeof(Click) * (CLICK_QUEUE_SZ - 1)); q->length--; }
double now = monotonic();
#define N(n) (q->clicks[q->length - n])
N(0).at = now; N(0).button = button; N(0).modifiers = modifiers;
q->length++;
// Now dispatch the multi-click if any
if (q->length > 2 && N(1).at - N(3).at <= 2 * OPT(click_interval)) {
multi_click(w, 3);
q->length = 0;
} else if (q->length > 1 && N(1).at - N(2).at <= OPT(click_interval)) {
multi_click(w, 2);
}
#undef N
}
HANDLER(handle_button_event) {
Tab *t = global_state.tabs + global_state.active_tab;
bool is_release = !global_state.mouse_button_pressed[button];
if (window_idx != t->active_window) {
@@ -72,8 +111,8 @@ handle_button_event(Window *w, int button, int modifiers, unsigned int window_id
if (is_release) {
if (modifiers == (int)OPT(open_url_modifiers)) {
// TODO: click_url
} else if (!modifiers) {
// TODO: handle multi click
} else {
if (is_release) add_click(w, button, modifiers, window_idx);
}
}
break;
@@ -85,8 +124,7 @@ handle_button_event(Window *w, int button, int modifiers, unsigned int window_id
}
}
void
handle_event(Window *w, int button, int modifiers, unsigned int window_idx) {
HANDLER(handle_event) {
switch(button) {
case -1:
handle_move_event(w, button, modifiers, window_idx);
@@ -103,7 +141,8 @@ handle_event(Window *w, int button, int modifiers, unsigned int window_idx) {
}
}
void handle_tab_bar_mouse(int button, int UNUSED modifiers) {
static inline void
handle_tab_bar_mouse(int button, int UNUSED modifiers) {
if (button != GLFW_MOUSE_BUTTON_LEFT || !global_state.mouse_button_pressed[button]) return;
call_boss(activate_tab_at, "d", global_state.mouse_x);
}

View File

@@ -1344,41 +1344,40 @@ text_for_selection(Screen *self) {
return ans;
}
static PyObject*
selection_range_for_line(Screen *self, PyObject *args) {
unsigned int y;
if (!PyArg_ParseTuple(args, "I", &y)) return NULL;
if (y >= self->lines) { PyErr_SetString(PyExc_ValueError, "y larger than lines"); return NULL; }
bool
screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end) {
if (y >= self->lines) { return false; }
Line *line = visual_line_(self, y);
index_type xlimit = line->xnum, xstart = 0;
while (xlimit > 0 && CHAR_IS_BLANK(line->cells[xlimit - 1].ch)) xlimit--;
while (xstart < xlimit && CHAR_IS_BLANK(line->cells[xstart].ch)) xstart++;
return Py_BuildValue("II", (unsigned int)xstart, (unsigned int)xlimit);
*start = xstart; *end = xlimit;
return true;
}
static inline bool
has_char(int kind, void *data, Py_ssize_t sz, char_type ch) {
for (Py_ssize_t i = 0; i < sz; i++) {
if (PyUnicode_READ(kind, data, i) == ch) return true;
is_opt_word_char(char_type ch) {
for (size_t i = 0; i < OPT(select_by_word_characters_count); i++) {
if (OPT(select_by_word_characters[i]) == ch) return true;
}
return false;
}
static PyObject*
selection_range_for_word(Screen *self, PyObject *args) {
unsigned int x, y;
PyObject *extra_chars;
if (!PyArg_ParseTuple(args, "IIU", &x, &y, &extra_chars)) return NULL;
if (PyUnicode_READY(extra_chars) != 0) return NULL;
if (y >= self->lines) { PyErr_SetString(PyExc_ValueError, "y larger than lines"); return NULL; }
if (x >= self->columns) { PyErr_SetString(PyExc_ValueError, "x larger than columns"); return NULL; }
bool
screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_type *s, index_type *e) {
if (y >= self->lines || x >= self->columns) return false;
index_type start, end;
Line *line = visual_line_(self, y);
#define is_ok(x) (is_word_char((line->cells[x].ch) & CHAR_MASK) || has_char(PyUnicode_KIND(extra_chars), PyUnicode_DATA(extra_chars), PyUnicode_GET_LENGTH(extra_chars), (line->cells[x].ch) & CHAR_MASK))
if (!is_ok(x)) Py_BuildValue("II", x, x + 1);
unsigned int start = x, end = x;
while(start > 0 && is_ok(start - 1)) start--;
while(end < self->columns - 1 && is_ok(end + 1)) end++;
return Py_BuildValue("II", start, end + 1);
#define is_ok(x) (is_word_char((line->cells[x].ch) & CHAR_MASK) || is_opt_word_char(line->cells[x].ch & CHAR_MASK))
if (!is_ok(x)) {
start = x; end = x + 1;
} else {
start = x, end = x;
while(start > 0 && is_ok(start - 1)) start--;
while(end < self->columns - 1 && is_ok(end + 1)) end++;
}
*s = start; *e = end;
return true;
#undef is_ok
}
@@ -1433,24 +1432,17 @@ screen_is_selection_dirty(Screen *self) {
return false;
}
static PyObject*
start_selection(Screen *self, PyObject *args) {
unsigned int x, y;
if (!PyArg_ParseTuple(args, "II", &x, &y)) return NULL;
void
screen_start_selection(Screen *self, index_type x, index_type y) {
#define A(attr, val) self->selection.attr = val;
A(start_x, x); A(end_x, x); A(start_y, y); A(end_y, y); A(start_scrolled_by, self->scrolled_by); A(end_scrolled_by, self->scrolled_by); A(in_progress, true);
#undef A
Py_RETURN_NONE;
}
static PyObject*
update_selection(Screen *self, PyObject *args) {
unsigned int x, y;
int ended;
if (!PyArg_ParseTuple(args, "IIp", &x, &y, &ended)) return NULL;
void
screen_update_selection(Screen *self, index_type x, index_type y, bool ended) {
self->selection.end_x = x; self->selection.end_y = y; self->selection.end_scrolled_by = self->scrolled_by;
if (ended) self->selection.in_progress = false;
Py_RETURN_NONE;
}
static PyObject*
@@ -1533,12 +1525,8 @@ static PyMethodDef methods[] = {
MND(mark_as_dirty, METH_NOARGS)
MND(resize, METH_VARARGS)
MND(set_margins, METH_VARARGS)
MND(selection_range_for_line, METH_VARARGS)
MND(selection_range_for_word, METH_VARARGS)
MND(text_for_selection, METH_NOARGS)
MND(is_selection_in_progress, METH_NOARGS)
MND(start_selection, METH_VARARGS)
MND(update_selection, METH_VARARGS)
MND(scroll, METH_VARARGS)
MND(toggle_alt_screen, METH_NOARGS)
MND(reset_callbacks, METH_NOARGS)

View File

@@ -64,6 +64,10 @@ bool screen_is_selection_dirty(Screen *self);
bool screen_invert_colors(Screen *self);
void screen_update_cell_data(Screen *self, void *address, size_t sz);
bool screen_is_cursor_visible(Screen *self);
bool screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end);
bool screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_type *start, index_type *end);
void screen_start_selection(Screen *self, index_type x, index_type y);
void screen_update_selection(Screen *self, index_type x, index_type y, bool ended);
Line* screen_visual_line(Screen *self, index_type y);
unsigned long screen_current_char_width(Screen *self);
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);

View File

@@ -128,6 +128,14 @@ PYWRAP1(set_options) {
S(cursor_opacity, PyFloat_AsDouble);
S(mouse_hide_wait, PyFloat_AsDouble);
S(open_url_modifiers, PyLong_AsUnsignedLong);
S(click_interval, PyFloat_AsDouble);
PyObject *chars = PyObject_GetAttrString(args, "select_by_word_characters");
if (chars == NULL) return NULL;
for (size_t i = 0; i < MIN((size_t)PyUnicode_GET_LENGTH(chars), sizeof(OPT(select_by_word_characters))/sizeof(OPT(select_by_word_characters[0]))); i++) {
OPT(select_by_word_characters)[i] = PyUnicode_READ(PyUnicode_KIND(chars), PyUnicode_DATA(chars), i);
}
OPT(select_by_word_characters_count) = PyUnicode_GET_LENGTH(chars);
Py_DECREF(chars);
#undef S
Py_RETURN_NONE;
}

View File

@@ -10,11 +10,11 @@
#define OPT(name) global_state.opts.name
typedef struct {
double visual_bell_duration, cursor_blink_interval, cursor_stop_blinking_after, mouse_hide_wait;
double visual_bell_duration, cursor_blink_interval, cursor_stop_blinking_after, mouse_hide_wait, click_interval, cursor_opacity;
bool enable_audio_bell;
CursorShape cursor_shape;
double cursor_opacity;
unsigned int open_url_modifiers;
char_type select_by_word_characters[256]; size_t select_by_word_characters_count;
} Options;
typedef struct {
@@ -27,6 +27,17 @@ typedef struct {
unsigned int left, top, right, bottom;
} WindowGeometry;
typedef struct {
double at;
int button, modifiers;
} Click;
#define CLICK_QUEUE_SZ 3
typedef struct {
Click clicks[CLICK_QUEUE_SZ];
unsigned int length;
} ClickQueue;
typedef struct {
unsigned int id;
bool visible;
@@ -34,6 +45,7 @@ typedef struct {
ScreenRenderData render_data;
unsigned int mouse_cell_x, mouse_cell_y;
WindowGeometry geometry;
ClickQueue click_queue;
} Window;
typedef struct {