Implement the mouse tracking protocol
This commit is contained in:
parent
991d01bb68
commit
d3fd0646fb
@ -263,11 +263,14 @@ class CharGrid:
|
|||||||
self.current_cursor = Cursor(c.x, c.y, c.hidden, c.shape, c.color, c.blink)
|
self.current_cursor = Cursor(c.x, c.y, c.hidden, c.shape, c.color, c.blink)
|
||||||
|
|
||||||
def cell_for_pos(self, x, y):
|
def cell_for_pos(self, x, y):
|
||||||
return int(x // cell_size.width), int(y // cell_size.height)
|
x, y = int(x // cell_size.width), int(y // cell_size.height)
|
||||||
|
if 0 <= x < self.screen.columns and 0 <= y < self.screen.lines:
|
||||||
|
return x, y
|
||||||
|
return None, None
|
||||||
|
|
||||||
def update_drag(self, is_press, x, y):
|
def update_drag(self, is_press, x, y):
|
||||||
x, y = self.cell_for_pos(x, y)
|
x, y = self.cell_for_pos(x, y)
|
||||||
if 0 <= x < self.screen.columns and 0 <= y < self.screen.lines:
|
if x is not None:
|
||||||
ps = None
|
ps = None
|
||||||
with self.buffer_lock:
|
with self.buffer_lock:
|
||||||
if is_press:
|
if is_press:
|
||||||
@ -287,24 +290,26 @@ class CharGrid:
|
|||||||
|
|
||||||
def has_url_at(self, x, y):
|
def has_url_at(self, x, y):
|
||||||
x, y = self.cell_for_pos(x, y)
|
x, y = self.cell_for_pos(x, y)
|
||||||
l = self.screen_line(y)
|
if x is not None:
|
||||||
if l is not None:
|
l = self.screen_line(y)
|
||||||
text = l.as_base_text()
|
if l is not None:
|
||||||
for m in self.url_pat.finditer(text):
|
text = l.as_base_text()
|
||||||
if m.start() <= x < m.end():
|
for m in self.url_pat.finditer(text):
|
||||||
return True
|
if m.start() <= x < m.end():
|
||||||
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def click_url(self, x, y):
|
def click_url(self, x, y):
|
||||||
x, y = self.cell_for_pos(x, y)
|
x, y = self.cell_for_pos(x, y)
|
||||||
l = self.screen_line(y)
|
if x is not None:
|
||||||
if l is not None:
|
l = self.screen_line(y)
|
||||||
text = l.as_base_text()
|
if l is not None:
|
||||||
for m in self.url_pat.finditer(text):
|
text = l.as_base_text()
|
||||||
if m.start() <= x < m.end():
|
for m in self.url_pat.finditer(text):
|
||||||
url = ''.join(l[i] for i in range(*m.span())).rstrip('.')
|
if m.start() <= x < m.end():
|
||||||
if url:
|
url = ''.join(l[i] for i in range(*m.span())).rstrip('.')
|
||||||
open_url(url, self.opts.open_url_with)
|
if url:
|
||||||
|
open_url(url, self.opts.open_url_with)
|
||||||
|
|
||||||
def screen_line(self, y):
|
def screen_line(self, y):
|
||||||
' Return the Line object corresponding to the yth line on the rendered screen '
|
' Return the Line object corresponding to the yth line on the rendered screen '
|
||||||
@ -318,36 +323,37 @@ class CharGrid:
|
|||||||
|
|
||||||
def multi_click(self, count, x, y):
|
def multi_click(self, count, x, y):
|
||||||
x, y = self.cell_for_pos(x, y)
|
x, y = self.cell_for_pos(x, y)
|
||||||
line = self.screen_line(y)
|
if x is not None:
|
||||||
if line is not None and 0 <= x < self.screen.columns and count in (2, 3):
|
line = self.screen_line(y)
|
||||||
s = self.current_selection
|
if line is not None and count in (2, 3):
|
||||||
s.start_scrolled_by = s.end_scrolled_by = self.scrolled_by
|
s = self.current_selection
|
||||||
s.start_y = s.end_y = y
|
s.start_scrolled_by = s.end_scrolled_by = self.scrolled_by
|
||||||
s.in_progress = False
|
s.start_y = s.end_y = y
|
||||||
if count == 3:
|
s.in_progress = False
|
||||||
for i in range(self.screen.columns):
|
if count == 3:
|
||||||
if line[i] != ' ':
|
for i in range(self.screen.columns):
|
||||||
s.start_x = i
|
if line[i] != ' ':
|
||||||
break
|
s.start_x = i
|
||||||
else:
|
break
|
||||||
s.start_x = 0
|
else:
|
||||||
for i in range(self.screen.columns):
|
s.start_x = 0
|
||||||
c = self.screen.columns - 1 - i
|
for i in range(self.screen.columns):
|
||||||
if line[c] != ' ':
|
c = self.screen.columns - 1 - i
|
||||||
s.end_x = c
|
if line[c] != ' ':
|
||||||
break
|
s.end_x = c
|
||||||
else:
|
break
|
||||||
s.end_x = self.screen.columns - 1
|
else:
|
||||||
elif count == 2:
|
s.end_x = self.screen.columns - 1
|
||||||
i = x
|
elif count == 2:
|
||||||
pat = re.compile(r'\w')
|
i = x
|
||||||
while i >= 0 and pat.match(line[i]) is not None:
|
pat = re.compile(r'\w')
|
||||||
i -= 1
|
while i >= 0 and pat.match(line[i]) is not None:
|
||||||
s.start_x = i if i == x else i + 1
|
i -= 1
|
||||||
i = x
|
s.start_x = i if i == x else i + 1
|
||||||
while i < self.screen.columns and pat.match(line[i]) is not None:
|
i = x
|
||||||
i += 1
|
while i < self.screen.columns and pat.match(line[i]) is not None:
|
||||||
s.end_x = i if i == x else i - 1
|
i += 1
|
||||||
|
s.end_x = i if i == x else i - 1
|
||||||
|
|
||||||
def text_for_selection(self, sel=None):
|
def text_for_selection(self, sel=None):
|
||||||
start, end = (sel or self.current_selection).limits(self.scrolled_by)
|
start, end = (sel or self.current_selection).limits(self.scrolled_by)
|
||||||
|
|||||||
@ -73,7 +73,13 @@ PyInit_fast_data_types(void) {
|
|||||||
PyModule_AddIntMacro(m, DECOM);
|
PyModule_AddIntMacro(m, DECOM);
|
||||||
PyModule_AddIntMacro(m, IRM);
|
PyModule_AddIntMacro(m, IRM);
|
||||||
PyModule_AddIntMacro(m, DATA_CELL_SIZE);
|
PyModule_AddIntMacro(m, DATA_CELL_SIZE);
|
||||||
|
PyModule_AddIntMacro(m, ANY_MODE);
|
||||||
|
PyModule_AddIntMacro(m, MOTION_MODE);
|
||||||
|
PyModule_AddIntMacro(m, BUTTON_MODE);
|
||||||
|
PyModule_AddIntMacro(m, SGR_PROTOCOL);
|
||||||
|
PyModule_AddIntMacro(m, NORMAL_PROTOCOL);
|
||||||
|
PyModule_AddIntMacro(m, URXVT_PROTOCOL);
|
||||||
|
PyModule_AddIntMacro(m, UTF8_PROTOCOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
|
|||||||
@ -23,6 +23,13 @@ typedef uint32_t combining_type;
|
|||||||
typedef unsigned int index_type;
|
typedef unsigned int index_type;
|
||||||
|
|
||||||
#define ERROR_PREFIX "[PARSE ERROR]"
|
#define ERROR_PREFIX "[PARSE ERROR]"
|
||||||
|
#define ANY_MODE 3
|
||||||
|
#define MOTION_MODE 2
|
||||||
|
#define BUTTON_MODE 1
|
||||||
|
#define NORMAL_PROTOCOL 0
|
||||||
|
#define UTF8_PROTOCOL 1
|
||||||
|
#define SGR_PROTOCOL 2
|
||||||
|
#define URXVT_PROTOCOL 3
|
||||||
|
|
||||||
#define CELL_SIZE (sizeof(char_type) + sizeof(color_type) + sizeof(decoration_type) + sizeof(combining_type))
|
#define CELL_SIZE (sizeof(char_type) + sizeof(color_type) + sizeof(decoration_type) + sizeof(combining_type))
|
||||||
// The data cell size must be a multiple of 3
|
// The data cell size must be a multiple of 3
|
||||||
@ -227,8 +234,8 @@ PyTypeObject ChangeTracker_Type;
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mDECARM,
|
bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mDECARM,
|
||||||
mBRACKETED_PASTE, mFOCUS_TRACKING, mMOUSE_BUTTON_TRACKING,
|
mBRACKETED_PASTE, mFOCUS_TRACKING;
|
||||||
mMOUSE_MOTION_TRACKING, mMOUSE_SGR_MODE, mMOUSE_MOVE_TRACKING;
|
unsigned long mouse_tracking_mode, mouse_tracking_protocol;
|
||||||
} ScreenModes;
|
} ScreenModes;
|
||||||
PyTypeObject ScreenModes_Type;
|
PyTypeObject ScreenModes_Type;
|
||||||
|
|
||||||
|
|||||||
@ -63,7 +63,9 @@
|
|||||||
#define MOUSE_MOTION_TRACKING (1002 << 5)
|
#define MOUSE_MOTION_TRACKING (1002 << 5)
|
||||||
#define MOUSE_MOVE_TRACKING (1003 << 5)
|
#define MOUSE_MOVE_TRACKING (1003 << 5)
|
||||||
#define FOCUS_TRACKING (1004 << 5)
|
#define FOCUS_TRACKING (1004 << 5)
|
||||||
|
#define MOUSE_UTF8_MODE (1005 << 5)
|
||||||
#define MOUSE_SGR_MODE (1006 << 5)
|
#define MOUSE_SGR_MODE (1006 << 5)
|
||||||
|
#define MOUSE_URXVT_MODE (1015 << 5)
|
||||||
|
|
||||||
// Alternate screen buffer
|
// Alternate screen buffer
|
||||||
#define ALTERNATE_SCREEN (1049 << 5)
|
#define ALTERNATE_SCREEN (1049 << 5)
|
||||||
|
|||||||
62
kitty/mouse.py
Normal file
62
kitty/mouse.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
from .fast_data_types import (
|
||||||
|
GLFW_MOUSE_BUTTON_2, GLFW_MOUSE_BUTTON_3, GLFW_MOD_ALT, GLFW_MOD_CONTROL,
|
||||||
|
GLFW_MOD_SHIFT, GLFW_MOUSE_BUTTON_4, GLFW_MOUSE_BUTTON_5, SGR_PROTOCOL,
|
||||||
|
GLFW_MOUSE_BUTTON_1, URXVT_PROTOCOL, UTF8_PROTOCOL
|
||||||
|
)
|
||||||
|
|
||||||
|
PRESS, RELEASE, DRAG, MOVE = range(4)
|
||||||
|
SHIFT_INDICATOR = 1 << 2
|
||||||
|
ALT_INDICATOR = 1 << 3
|
||||||
|
CONTROL_INDICATOR = 1 << 4
|
||||||
|
MOTION_INDICATOR = 1 << 5
|
||||||
|
EXTRA_BUTTON_INDICATOR = 1 << 6
|
||||||
|
|
||||||
|
cb_map = {
|
||||||
|
GLFW_MOUSE_BUTTON_1: 0,
|
||||||
|
GLFW_MOUSE_BUTTON_2: 0b1,
|
||||||
|
GLFW_MOUSE_BUTTON_3: 0b10,
|
||||||
|
GLFW_MOUSE_BUTTON_4: EXTRA_BUTTON_INDICATOR,
|
||||||
|
GLFW_MOUSE_BUTTON_5: EXTRA_BUTTON_INDICATOR | 0b1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def encode_mouse_event(tracking_mode, tracking_protocol, button, action, mods, x, y):
|
||||||
|
x, y = x + 1, y + 1 # One based indexing
|
||||||
|
cb = 0
|
||||||
|
if action is MOVE:
|
||||||
|
if tracking_protocol != SGR_PROTOCOL:
|
||||||
|
cb = 0b11
|
||||||
|
else:
|
||||||
|
cb = cb_map.get(button)
|
||||||
|
if cb is None:
|
||||||
|
return
|
||||||
|
if action in (DRAG, MOVE):
|
||||||
|
cb |= MOTION_INDICATOR
|
||||||
|
elif action is RELEASE:
|
||||||
|
if tracking_protocol != SGR_PROTOCOL:
|
||||||
|
cb = 0b11
|
||||||
|
if mods & GLFW_MOD_SHIFT:
|
||||||
|
cb |= SHIFT_INDICATOR
|
||||||
|
if mods & GLFW_MOD_ALT:
|
||||||
|
cb |= ALT_INDICATOR
|
||||||
|
if mods & GLFW_MOD_CONTROL:
|
||||||
|
cb |= CONTROL_INDICATOR
|
||||||
|
ans = None
|
||||||
|
if tracking_protocol == SGR_PROTOCOL:
|
||||||
|
ans = '\033[<%d;%d;%d%s' % (cb, x, y, 'm' if action is RELEASE else 'M')
|
||||||
|
ans = ans.encode('ascii')
|
||||||
|
elif tracking_protocol == URXVT_PROTOCOL:
|
||||||
|
ans = '\033[%d;%d;%dM' % (cb + 32, x, y)
|
||||||
|
ans = ans.encode('ascii')
|
||||||
|
elif tracking_protocol == UTF8_PROTOCOL:
|
||||||
|
ans = bytearray([0o33, ord('['), cb + 32])
|
||||||
|
ans.extend(chr(x + 32).encode('utf-8') + chr(y + 32).encode('utf-8'))
|
||||||
|
ans = bytes(ans)
|
||||||
|
else:
|
||||||
|
if x <= 223 and y <= 223:
|
||||||
|
ans = bytearray([0o33, ord('['), cb + 32, x + 32, y + 32])
|
||||||
|
return ans
|
||||||
@ -344,17 +344,23 @@ set_mode_from_const(Screen *self, unsigned int mode, bool val) {
|
|||||||
case name: \
|
case name: \
|
||||||
self->modes.m##name = val; break;
|
self->modes.m##name = val; break;
|
||||||
|
|
||||||
|
#define MOUSE_MODE(name, attr, value) \
|
||||||
|
case name: \
|
||||||
|
self->modes.attr = val ? value : 0; break;
|
||||||
|
|
||||||
bool private;
|
bool private;
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
SIMPLE_MODE(LNM)
|
SIMPLE_MODE(LNM)
|
||||||
SIMPLE_MODE(IRM)
|
SIMPLE_MODE(IRM)
|
||||||
SIMPLE_MODE(DECARM)
|
SIMPLE_MODE(DECARM)
|
||||||
SIMPLE_MODE(BRACKETED_PASTE)
|
SIMPLE_MODE(BRACKETED_PASTE)
|
||||||
SIMPLE_MODE(MOUSE_BUTTON_TRACKING)
|
|
||||||
SIMPLE_MODE(MOUSE_MOVE_TRACKING)
|
|
||||||
SIMPLE_MODE(MOUSE_MOTION_TRACKING)
|
|
||||||
SIMPLE_MODE(MOUSE_SGR_MODE)
|
|
||||||
SIMPLE_MODE(FOCUS_TRACKING)
|
SIMPLE_MODE(FOCUS_TRACKING)
|
||||||
|
MOUSE_MODE(MOUSE_BUTTON_TRACKING, mouse_tracking_mode, BUTTON_MODE)
|
||||||
|
MOUSE_MODE(MOUSE_MOTION_TRACKING, mouse_tracking_mode, MOTION_MODE)
|
||||||
|
MOUSE_MODE(MOUSE_MOVE_TRACKING, mouse_tracking_mode, ANY_MODE)
|
||||||
|
MOUSE_MODE(MOUSE_UTF8_MODE, mouse_tracking_protocol, UTF8_PROTOCOL)
|
||||||
|
MOUSE_MODE(MOUSE_SGR_MODE, mouse_tracking_protocol, SGR_PROTOCOL)
|
||||||
|
MOUSE_MODE(MOUSE_URXVT_MODE, mouse_tracking_protocol, URXVT_PROTOCOL)
|
||||||
|
|
||||||
case DECCKM:
|
case DECCKM:
|
||||||
case DECSCLM:
|
case DECSCLM:
|
||||||
@ -403,6 +409,7 @@ set_mode_from_const(Screen *self, unsigned int mode, bool val) {
|
|||||||
fprintf(stderr, "%s %s %u %s\n", ERROR_PREFIX, "Unsupported screen mode: ", mode, private ? "(private)" : "");
|
fprintf(stderr, "%s %s %u %s\n", ERROR_PREFIX, "Unsupported screen mode: ", mode, private ? "(private)" : "");
|
||||||
}
|
}
|
||||||
#undef SIMPLE_MODE
|
#undef SIMPLE_MODE
|
||||||
|
#undef MOUSE_MODE
|
||||||
}
|
}
|
||||||
|
|
||||||
void screen_set_mode(Screen *self, unsigned int mode) {
|
void screen_set_mode(Screen *self, unsigned int mode) {
|
||||||
@ -993,12 +1000,17 @@ WRAP1B(erase_in_display, 0)
|
|||||||
|
|
||||||
MODE_GETTER(in_bracketed_paste_mode, BRACKETED_PASTE)
|
MODE_GETTER(in_bracketed_paste_mode, BRACKETED_PASTE)
|
||||||
MODE_GETTER(focus_tracking_enabled, FOCUS_TRACKING)
|
MODE_GETTER(focus_tracking_enabled, FOCUS_TRACKING)
|
||||||
MODE_GETTER(mouse_button_tracking_enabled, MOUSE_BUTTON_TRACKING)
|
|
||||||
MODE_GETTER(mouse_motion_tracking_enabled, MOUSE_MOTION_TRACKING)
|
|
||||||
MODE_GETTER(mouse_move_tracking_enabled, MOUSE_MOVE_TRACKING)
|
|
||||||
MODE_GETTER(mouse_in_sgr_mode, MOUSE_SGR_MODE)
|
|
||||||
MODE_GETTER(auto_repeat_enabled, DECARM)
|
MODE_GETTER(auto_repeat_enabled, DECARM)
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
mouse_tracking_mode(Screen *self) {
|
||||||
|
return PyLong_FromUnsignedLong(self->modes.mouse_tracking_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
mouse_tracking_protocol(Screen *self) {
|
||||||
|
return PyLong_FromUnsignedLong(self->modes.mouse_tracking_protocol);
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
cursor_up(Screen *self, PyObject *args) {
|
cursor_up(Screen *self, PyObject *args) {
|
||||||
@ -1100,7 +1112,8 @@ static PyObject* mark_as_dirty(Screen *self) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject* current_char_width(Screen *self) {
|
static PyObject*
|
||||||
|
current_char_width(Screen *self) {
|
||||||
#define current_char_width_doc "The width of the character under the cursor"
|
#define current_char_width_doc "The width of the character under the cursor"
|
||||||
unsigned long ans = 1;
|
unsigned long ans = 1;
|
||||||
if (self->cursor->x < self->columns - 1 && self->cursor->y < self->lines) {
|
if (self->cursor->x < self->columns - 1 && self->cursor->y < self->lines) {
|
||||||
@ -1109,6 +1122,13 @@ static PyObject* current_char_width(Screen *self) {
|
|||||||
return PyLong_FromUnsignedLong(ans);
|
return PyLong_FromUnsignedLong(ans);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
is_main_linebuf(Screen *self) {
|
||||||
|
PyObject *ans = (self->linebuf == self->main_linebuf) ? Py_True : Py_False;
|
||||||
|
Py_INCREF(ans);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
WRAP2(cursor_position, 1, 1)
|
WRAP2(cursor_position, 1, 1)
|
||||||
|
|
||||||
#define COUNT_WRAP(name) WRAP1(name, 1)
|
#define COUNT_WRAP(name) WRAP1(name, 1)
|
||||||
@ -1132,6 +1152,7 @@ static PyMethodDef methods[] = {
|
|||||||
MND(reset_mode, METH_VARARGS)
|
MND(reset_mode, METH_VARARGS)
|
||||||
MND(reset, METH_NOARGS)
|
MND(reset, METH_NOARGS)
|
||||||
MND(reset_dirty, METH_NOARGS)
|
MND(reset_dirty, METH_NOARGS)
|
||||||
|
MND(is_main_linebuf, METH_NOARGS)
|
||||||
MND(consolidate_changes, METH_NOARGS)
|
MND(consolidate_changes, METH_NOARGS)
|
||||||
MND(cursor_back, METH_VARARGS)
|
MND(cursor_back, METH_VARARGS)
|
||||||
MND(erase_in_line, METH_VARARGS)
|
MND(erase_in_line, METH_VARARGS)
|
||||||
@ -1144,6 +1165,8 @@ static PyMethodDef methods[] = {
|
|||||||
MND(change_scrollback_size, METH_VARARGS)
|
MND(change_scrollback_size, METH_VARARGS)
|
||||||
MND(erase_characters, METH_VARARGS)
|
MND(erase_characters, METH_VARARGS)
|
||||||
MND(cursor_up, METH_VARARGS)
|
MND(cursor_up, METH_VARARGS)
|
||||||
|
MND(mouse_tracking_mode, METH_NOARGS)
|
||||||
|
MND(mouse_tracking_protocol, METH_NOARGS)
|
||||||
MND(cursor_up1, METH_VARARGS)
|
MND(cursor_up1, METH_VARARGS)
|
||||||
MND(cursor_down, METH_VARARGS)
|
MND(cursor_down, METH_VARARGS)
|
||||||
MND(cursor_down1, METH_VARARGS)
|
MND(cursor_down1, METH_VARARGS)
|
||||||
@ -1165,10 +1188,6 @@ static PyMethodDef methods[] = {
|
|||||||
MND(in_bracketed_paste_mode, METH_NOARGS)
|
MND(in_bracketed_paste_mode, METH_NOARGS)
|
||||||
MND(auto_repeat_enabled, METH_NOARGS)
|
MND(auto_repeat_enabled, METH_NOARGS)
|
||||||
MND(focus_tracking_enabled, METH_NOARGS)
|
MND(focus_tracking_enabled, METH_NOARGS)
|
||||||
MND(mouse_button_tracking_enabled, METH_NOARGS)
|
|
||||||
MND(mouse_motion_tracking_enabled, METH_NOARGS)
|
|
||||||
MND(mouse_move_tracking_enabled, METH_NOARGS)
|
|
||||||
MND(mouse_in_sgr_mode, METH_NOARGS)
|
|
||||||
{"update_cell_data", (PyCFunction)screen_update_cell_data, METH_VARARGS, ""},
|
{"update_cell_data", (PyCFunction)screen_update_cell_data, METH_VARARGS, ""},
|
||||||
{"select_graphic_rendition", (PyCFunction)_select_graphic_rendition, METH_VARARGS, ""},
|
{"select_graphic_rendition", (PyCFunction)_select_graphic_rendition, METH_VARARGS, ""},
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import weakref
|
import weakref
|
||||||
from collections import deque
|
from collections import deque, defaultdict
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
|
|
||||||
@ -13,9 +13,12 @@ from .constants import wakeup, tab_manager, appname, WindowGeometry
|
|||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
BRACKETED_PASTE_START, BRACKETED_PASTE_END, Screen, read_bytes_dump,
|
BRACKETED_PASTE_START, BRACKETED_PASTE_END, Screen, read_bytes_dump,
|
||||||
read_bytes, GLFW_MOD_SHIFT, GLFW_MOUSE_BUTTON_1, GLFW_PRESS,
|
read_bytes, GLFW_MOD_SHIFT, GLFW_MOUSE_BUTTON_1, GLFW_PRESS,
|
||||||
GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE, GLFW_KEY_LEFT_SHIFT,
|
GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE, glfw_post_empty_event,
|
||||||
GLFW_KEY_RIGHT_SHIFT, glfw_post_empty_event
|
GLFW_MOUSE_BUTTON_5, ANY_MODE, MOTION_MODE, GLFW_KEY_LEFT_SHIFT,
|
||||||
|
GLFW_KEY_RIGHT_SHIFT, GLFW_KEY_UP, GLFW_KEY_DOWN, GLFW_MOUSE_BUTTON_4
|
||||||
)
|
)
|
||||||
|
from .keys import key_map
|
||||||
|
from .mouse import encode_mouse_event, PRESS, RELEASE, MOVE, DRAG
|
||||||
from .terminfo import get_capabilities
|
from .terminfo import get_capabilities
|
||||||
from .utils import sanitize_title, get_primary_selection, parse_color_set
|
from .utils import sanitize_title, get_primary_selection, parse_color_set
|
||||||
|
|
||||||
@ -24,6 +27,7 @@ class Window:
|
|||||||
|
|
||||||
def __init__(self, tab, child, opts, args):
|
def __init__(self, tab, child, opts, args):
|
||||||
self.tabref = weakref.ref(tab)
|
self.tabref = weakref.ref(tab)
|
||||||
|
self.mouse_button_pressed = defaultdict(lambda: False)
|
||||||
self.destroyed = False
|
self.destroyed = False
|
||||||
self.click_queue = deque(maxlen=3)
|
self.click_queue = deque(maxlen=3)
|
||||||
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)
|
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)
|
||||||
@ -149,11 +153,13 @@ class Window:
|
|||||||
glfw_post_empty_event()
|
glfw_post_empty_event()
|
||||||
|
|
||||||
def on_mouse_button(self, window, button, action, mods):
|
def on_mouse_button(self, window, button, action, mods):
|
||||||
handle_event = mods == GLFW_MOD_SHIFT or not self.screen.mouse_button_tracking_enabled()
|
self.mouse_button_pressed[button] = action == GLFW_PRESS
|
||||||
if handle_event:
|
mode = self.screen.mouse_tracking_mode()
|
||||||
|
send_event = mods != GLFW_MOD_SHIFT and mode > 0
|
||||||
|
x, y = window.get_cursor_pos()
|
||||||
|
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
|
||||||
|
if not send_event:
|
||||||
if button == GLFW_MOUSE_BUTTON_1:
|
if button == GLFW_MOUSE_BUTTON_1:
|
||||||
x, y = window.get_cursor_pos()
|
|
||||||
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
|
|
||||||
self.char_grid.update_drag(action == GLFW_PRESS, x, y)
|
self.char_grid.update_drag(action == GLFW_PRESS, x, y)
|
||||||
if action == GLFW_RELEASE:
|
if action == GLFW_RELEASE:
|
||||||
if mods == self.char_grid.opts.open_url_modifiers:
|
if mods == self.char_grid.opts.open_url_modifiers:
|
||||||
@ -164,27 +170,62 @@ class Window:
|
|||||||
if action == GLFW_RELEASE:
|
if action == GLFW_RELEASE:
|
||||||
self.paste_from_selection()
|
self.paste_from_selection()
|
||||||
else:
|
else:
|
||||||
x, y = window.get_cursor_pos()
|
if action == GLFW_RELEASE and button == GLFW_MOUSE_BUTTON_1 and mods == self.char_grid.opts.open_url_modifiers:
|
||||||
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
|
self.char_grid.click_url(x, y)
|
||||||
x, y = self.char_grid.cell_for_pos(x, y)
|
x, y = self.char_grid.cell_for_pos(x, y)
|
||||||
|
if x is not None:
|
||||||
|
ev = encode_mouse_event(mode, self.screen.mouse_tracking_protocol(),
|
||||||
|
button, PRESS if action == GLFW_PRESS else RELEASE, mods, x, y)
|
||||||
|
if ev:
|
||||||
|
self.write_to_child(ev)
|
||||||
|
|
||||||
def on_mouse_move(self, window, x, y):
|
def on_mouse_move(self, window, x, y):
|
||||||
|
button = None
|
||||||
|
for b in range(0, GLFW_MOUSE_BUTTON_5 + 1):
|
||||||
|
if self.mouse_button_pressed[b]:
|
||||||
|
button = b
|
||||||
|
break
|
||||||
|
action = MOVE if button is None else DRAG
|
||||||
|
mode = self.screen.mouse_tracking_mode()
|
||||||
|
send_event = (mode == ANY_MODE or (mode == MOTION_MODE and button is not None)) and not (
|
||||||
|
window.is_key_pressed(GLFW_KEY_LEFT_SHIFT) or window.is_key_pressed(GLFW_KEY_RIGHT_SHIFT))
|
||||||
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
|
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
|
||||||
if self.char_grid.current_selection.in_progress:
|
|
||||||
self.char_grid.update_drag(None, x, y)
|
|
||||||
tm = tab_manager()
|
tm = tab_manager()
|
||||||
tm.queue_ui_action(tab_manager().change_mouse_cursor, self.char_grid.has_url_at(x, y))
|
tm.queue_ui_action(tab_manager().change_mouse_cursor, self.char_grid.has_url_at(x, y))
|
||||||
|
if send_event:
|
||||||
|
x, y = self.char_grid.cell_for_pos(x, y)
|
||||||
|
if x is not None:
|
||||||
|
ev = encode_mouse_event(mode, self.screen.mouse_tracking_protocol(),
|
||||||
|
button, action, 0, x, y)
|
||||||
|
if ev:
|
||||||
|
self.write_to_child(ev)
|
||||||
|
else:
|
||||||
|
if self.char_grid.current_selection.in_progress:
|
||||||
|
self.char_grid.update_drag(None, x, y)
|
||||||
|
|
||||||
def on_mouse_scroll(self, window, x, y):
|
def on_mouse_scroll(self, window, x, y):
|
||||||
handle_event = (
|
s = int(round(y * self.opts.wheel_scroll_multiplier))
|
||||||
window.is_key_pressed(GLFW_KEY_LEFT_SHIFT) or
|
if abs(s) < 0:
|
||||||
window.is_key_pressed(GLFW_KEY_RIGHT_SHIFT) or
|
return
|
||||||
not self.screen.mouse_button_tracking_enabled())
|
upwards = s > 0
|
||||||
if handle_event:
|
if self.screen.is_main_linebuf():
|
||||||
s = int(round(y * self.opts.wheel_scroll_multiplier))
|
self.char_grid.scroll(abs(s), upwards)
|
||||||
if abs(s) > 0:
|
glfw_post_empty_event()
|
||||||
self.char_grid.scroll(abs(s), s > 0)
|
else:
|
||||||
glfw_post_empty_event()
|
mode = self.screen.mouse_tracking_mode()
|
||||||
|
send_event = mode > 0
|
||||||
|
if send_event:
|
||||||
|
x, y = window.get_cursor_pos()
|
||||||
|
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
|
||||||
|
x, y = self.char_grid.cell_for_pos(x, y)
|
||||||
|
if x is not None:
|
||||||
|
ev = encode_mouse_event(mode, self.screen.mouse_tracking_protocol(),
|
||||||
|
GLFW_MOUSE_BUTTON_4 if upwards else GLFW_MOUSE_BUTTON_5, PRESS, 0, x, y)
|
||||||
|
if ev:
|
||||||
|
self.write_to_child(ev)
|
||||||
|
else:
|
||||||
|
k = key_map[GLFW_KEY_UP if upwards else GLFW_KEY_DOWN]
|
||||||
|
self.write_to_child(k * abs(s))
|
||||||
|
|
||||||
# actions {{{
|
# actions {{{
|
||||||
|
|
||||||
@ -239,7 +280,7 @@ class Window:
|
|||||||
glfw_post_empty_event()
|
glfw_post_empty_event()
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def dump_commands(self, *a):
|
def dump_commands(self, *a): # {{{
|
||||||
if a:
|
if a:
|
||||||
if a[0] == 'draw':
|
if a[0] == 'draw':
|
||||||
if a[1] is None:
|
if a[1] is None:
|
||||||
@ -253,3 +294,4 @@ class Window:
|
|||||||
print('draw', ''.join(self.draw_dump_buf))
|
print('draw', ''.join(self.draw_dump_buf))
|
||||||
self.draw_dump_buf = []
|
self.draw_dump_buf = []
|
||||||
print(*a)
|
print(*a)
|
||||||
|
# }}}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user