Extend the SGR Pixel mouse reporting protocol to also report when the mouse leaves the window
This commit is contained in:
@@ -109,6 +109,8 @@ Detailed list of changes
|
|||||||
0.42.2 [future]
|
0.42.2 [future]
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- A new :ref:`protocol extension <mouse_leave_window>` to notify terminal programs that have turned on SGR Pixel mouse reporting when the mouse leaves the window (:disc:`8808`)
|
||||||
|
|
||||||
- Fix :opt:`remember_window_position` not working because of a stupid typo (:iss:`8646`)
|
- Fix :opt:`remember_window_position` not working because of a stupid typo (:iss:`8646`)
|
||||||
|
|
||||||
- A new :option:`kitty --grab-keyboard` that can be used to grab the keyboard so that global shortcuts are sent to kitty instead
|
- A new :option:`kitty --grab-keyboard` that can be used to grab the keyboard so that global shortcuts are sent to kitty instead
|
||||||
|
|||||||
@@ -29,6 +29,19 @@ there is only one number to reset these attributes, SGR 22, which resets both.
|
|||||||
There is no way to reset one and not the other. kitty uses 221 and 222 to reset
|
There is no way to reset one and not the other. kitty uses 221 and 222 to reset
|
||||||
bold and faint independently.
|
bold and faint independently.
|
||||||
|
|
||||||
|
.. _mouse_leave_window:
|
||||||
|
|
||||||
|
Reporting when the mouse leaves the window
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
kitty extends the SGR Pixel mouse reporting protocol created by xterm to
|
||||||
|
also report when the mouse leaves the window. This is event is delivered
|
||||||
|
encoded as a normal SGR pixel event except that the eight bit is set on the
|
||||||
|
first number. bits 1-7 are used to encode button and modifier information.
|
||||||
|
When bit 8 is set it means the event is a mouse has left the window event,
|
||||||
|
and all other bits should be ignored. The pixel position values must also
|
||||||
|
be ignored as they may not be accurate.
|
||||||
|
|
||||||
|
|
||||||
kitty specific private escape codes
|
kitty specific private escape codes
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|||||||
@@ -318,6 +318,7 @@ void colorprofile_report_stack(ColorProfile*, unsigned int*, unsigned int*);
|
|||||||
|
|
||||||
void set_mouse_cursor(MouseShape);
|
void set_mouse_cursor(MouseShape);
|
||||||
void enter_event(int modifiers);
|
void enter_event(int modifiers);
|
||||||
|
void leave_event(int modifiers);
|
||||||
void mouse_event(const int, int, int);
|
void mouse_event(const int, int, int);
|
||||||
void focus_in_event(void);
|
void focus_in_event(void);
|
||||||
void scroll_event(double, double, int, int);
|
void scroll_event(double, double, int, int);
|
||||||
|
|||||||
19
kitty/glfw.c
19
kitty/glfw.c
@@ -530,18 +530,21 @@ key_callback(GLFWwindow *w, GLFWkeyevent *ev) {
|
|||||||
static void
|
static void
|
||||||
cursor_enter_callback(GLFWwindow *w, int entered) {
|
cursor_enter_callback(GLFWwindow *w, int entered) {
|
||||||
if (!set_callback_window(w)) return;
|
if (!set_callback_window(w)) return;
|
||||||
|
double x, y;
|
||||||
|
glfwGetCursorPos(w, &x, &y);
|
||||||
|
monotonic_t now = monotonic();
|
||||||
|
global_state.callback_os_window->last_mouse_activity_at = now;
|
||||||
|
global_state.callback_os_window->mouse_x = x * global_state.callback_os_window->viewport_x_ratio;
|
||||||
|
global_state.callback_os_window->mouse_y = y * global_state.callback_os_window->viewport_y_ratio;
|
||||||
if (entered) {
|
if (entered) {
|
||||||
double x, y;
|
|
||||||
glfwGetCursorPos(w, &x, &y);
|
|
||||||
debug_input("Mouse cursor entered window: %llu at %fx%f\n", global_state.callback_os_window->id, x, y);
|
debug_input("Mouse cursor entered window: %llu at %fx%f\n", global_state.callback_os_window->id, x, y);
|
||||||
monotonic_t now = monotonic();
|
|
||||||
cursor_active_callback(w, now);
|
cursor_active_callback(w, now);
|
||||||
global_state.callback_os_window->last_mouse_activity_at = now;
|
|
||||||
global_state.callback_os_window->mouse_x = x * global_state.callback_os_window->viewport_x_ratio;
|
|
||||||
global_state.callback_os_window->mouse_y = y * global_state.callback_os_window->viewport_y_ratio;
|
|
||||||
if (is_window_ready_for_callbacks()) enter_event(mods_at_last_key_or_button_event);
|
if (is_window_ready_for_callbacks()) enter_event(mods_at_last_key_or_button_event);
|
||||||
request_tick_callback();
|
} else {
|
||||||
} else debug_input("Mouse cursor left window: %llu\n", global_state.callback_os_window->id);
|
debug_input("Mouse cursor left window: %llu\n", global_state.callback_os_window->id);
|
||||||
|
if (is_window_ready_for_callbacks()) leave_event(mods_at_last_key_or_button_event);
|
||||||
|
}
|
||||||
|
request_tick_callback();
|
||||||
global_state.callback_os_window = NULL;
|
global_state.callback_os_window = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
extern PyTypeObject Screen_Type;
|
extern PyTypeObject Screen_Type;
|
||||||
|
|
||||||
static MouseShape mouse_cursor_shape = TEXT_POINTER;
|
static MouseShape mouse_cursor_shape = TEXT_POINTER;
|
||||||
typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
|
typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE, LEAVE } MouseAction;
|
||||||
#define debug debug_input
|
#define debug debug_input
|
||||||
|
|
||||||
// Encoding of mouse events {{{
|
// Encoding of mouse events {{{
|
||||||
@@ -25,6 +25,7 @@ typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
|
|||||||
#define MOTION_INDICATOR (1 << 5)
|
#define MOTION_INDICATOR (1 << 5)
|
||||||
#define SCROLL_BUTTON_INDICATOR (1 << 6)
|
#define SCROLL_BUTTON_INDICATOR (1 << 6)
|
||||||
#define EXTRA_BUTTON_INDICATOR (1 << 7)
|
#define EXTRA_BUTTON_INDICATOR (1 << 7)
|
||||||
|
#define LEAVE_INDICATOR (1 << 8)
|
||||||
|
|
||||||
|
|
||||||
static unsigned int
|
static unsigned int
|
||||||
@@ -65,11 +66,18 @@ static char mouse_event_buf[64];
|
|||||||
static int
|
static int
|
||||||
encode_mouse_event_impl(const MousePosition *mpos, int mouse_tracking_protocol, int button, MouseAction action, int mods) {
|
encode_mouse_event_impl(const MousePosition *mpos, int mouse_tracking_protocol, int button, MouseAction action, int mods) {
|
||||||
unsigned int cb = encode_button(button);
|
unsigned int cb = encode_button(button);
|
||||||
if (action == MOVE) {
|
switch (action) {
|
||||||
if (cb == UINT_MAX) cb = 3;
|
case MOVE:
|
||||||
cb += 32;
|
if (cb == UINT_MAX) cb = 3;
|
||||||
} else {
|
cb += 32;
|
||||||
if (cb == UINT_MAX) return 0;
|
break;
|
||||||
|
case LEAVE:
|
||||||
|
if (mouse_tracking_protocol != SGR_PIXEL_PROTOCOL) return 0;
|
||||||
|
cb = LEAVE_INDICATOR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (cb == UINT_MAX) return 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (action == DRAG || action == MOVE) cb |= MOTION_INDICATOR;
|
if (action == DRAG || action == MOVE) cb |= MOTION_INDICATOR;
|
||||||
else if (action == RELEASE && mouse_tracking_protocol < SGR_PROTOCOL) cb = 3;
|
else if (action == RELEASE && mouse_tracking_protocol < SGR_PROTOCOL) cb = 3;
|
||||||
@@ -150,6 +158,21 @@ window_for_id(id_type window_id) {
|
|||||||
return window_for_window_id(window_id);
|
return window_for_window_id(window_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_mouse_leave_event_if_needed(id_type currently_over_window, int modifiers) {
|
||||||
|
if (global_state.mouse_hover_in_window != currently_over_window && global_state.mouse_hover_in_window) {
|
||||||
|
Window *left_window = window_for_id(global_state.mouse_hover_in_window);
|
||||||
|
global_state.mouse_hover_in_window = currently_over_window;
|
||||||
|
if (left_window) {
|
||||||
|
int sz = encode_mouse_event(left_window, 0, LEAVE, modifiers);
|
||||||
|
if (sz > 0) {
|
||||||
|
mouse_event_buf[sz] = 0;
|
||||||
|
write_escape_code_to_child(left_window->render_data.screen, ESC_CSI, mouse_event_buf);
|
||||||
|
debug("Sent mouse leave event to window: %llu\n", left_window->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
dispatch_mouse_event(Window *w, int button, int count, int modifiers, bool grabbed) {
|
dispatch_mouse_event(Window *w, int button, int count, int modifiers, bool grabbed) {
|
||||||
@@ -601,6 +624,8 @@ currently_pressed_button(void) {
|
|||||||
HANDLER(handle_event) {
|
HANDLER(handle_event) {
|
||||||
modifiers &= ~GLFW_LOCK_MASK;
|
modifiers &= ~GLFW_LOCK_MASK;
|
||||||
set_mouse_cursor_for_screen(w->render_data.screen);
|
set_mouse_cursor_for_screen(w->render_data.screen);
|
||||||
|
send_mouse_leave_event_if_needed(w->id, modifiers);
|
||||||
|
global_state.mouse_hover_in_window = w->id;
|
||||||
if (button == -1) {
|
if (button == -1) {
|
||||||
button = currently_pressed_button();
|
button = currently_pressed_button();
|
||||||
handle_move_event(w, button, modifiers, window_idx);
|
handle_move_event(w, button, modifiers, window_idx);
|
||||||
@@ -611,6 +636,7 @@ HANDLER(handle_event) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
handle_tab_bar_mouse(int button, int modifiers, int action) {
|
handle_tab_bar_mouse(int button, int modifiers, int action) {
|
||||||
|
send_mouse_leave_event_if_needed(0, modifiers);
|
||||||
if (button > -1) { // dont report motion events, as they are expensive and useless
|
if (button > -1) { // dont report motion events, as they are expensive and useless
|
||||||
call_boss(handle_click_on_tab, "Kdiii", global_state.callback_os_window->id, global_state.callback_os_window->mouse_x, button, modifiers, action);
|
call_boss(handle_click_on_tab, "Kdiii", global_state.callback_os_window->id, global_state.callback_os_window->mouse_x, button, modifiers, action);
|
||||||
}
|
}
|
||||||
@@ -694,6 +720,12 @@ update_mouse_pointer_shape(void) {
|
|||||||
set_mouse_cursor(mouse_cursor_shape);
|
set_mouse_cursor(mouse_cursor_shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
leave_event(int modifiers) {
|
||||||
|
if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window || !global_state.mouse_hover_in_window) return;
|
||||||
|
send_mouse_leave_event_if_needed(0, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
enter_event(int modifiers) {
|
enter_event(int modifiers) {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
@@ -713,9 +745,10 @@ enter_event(int modifiers) {
|
|||||||
if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window) return;
|
if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window) return;
|
||||||
unsigned window_idx; bool in_tab_bar;
|
unsigned window_idx; bool in_tab_bar;
|
||||||
Window *w = window_for_event(&window_idx, &in_tab_bar);
|
Window *w = window_for_event(&window_idx, &in_tab_bar);
|
||||||
|
send_mouse_leave_event_if_needed(w ? w->id : 0, modifiers);
|
||||||
if (!w || in_tab_bar) return;
|
if (!w || in_tab_bar) return;
|
||||||
bool mouse_cell_changed = false;
|
global_state.mouse_hover_in_window = w->id;
|
||||||
bool cell_half_changed = false;
|
bool mouse_cell_changed = false, cell_half_changed = false;
|
||||||
if (!set_mouse_position(w, &mouse_cell_changed, &cell_half_changed)) return;
|
if (!set_mouse_position(w, &mouse_cell_changed, &cell_half_changed)) return;
|
||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
int button = currently_pressed_button();
|
int button = currently_pressed_button();
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ typedef struct {
|
|||||||
bool has_pending_resizes, has_pending_closes;
|
bool has_pending_resizes, has_pending_closes;
|
||||||
bool check_for_active_animated_images;
|
bool check_for_active_animated_images;
|
||||||
struct { double x, y; } default_dpi;
|
struct { double x, y; } default_dpi;
|
||||||
id_type active_drag_in_window, tracked_drag_in_window;
|
id_type active_drag_in_window, tracked_drag_in_window, mouse_hover_in_window;
|
||||||
int active_drag_button, tracked_drag_button;
|
int active_drag_button, tracked_drag_button;
|
||||||
CloseRequest quit_request;
|
CloseRequest quit_request;
|
||||||
bool redirect_mouse_handling;
|
bool redirect_mouse_handling;
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ func Run(args []string) (rc int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
lp.ClearScreen()
|
lp.ClearScreen()
|
||||||
|
if current_mouse_event.Event_type == loop.MOUSE_LEAVE {
|
||||||
|
lp.Println("Mouse has left the window")
|
||||||
|
return
|
||||||
|
}
|
||||||
lp.Printf("Position: %d, %d (pixels)\r\n", current_mouse_event.Pixel.X, current_mouse_event.Pixel.Y)
|
lp.Printf("Position: %d, %d (pixels)\r\n", current_mouse_event.Pixel.X, current_mouse_event.Pixel.Y)
|
||||||
lp.Printf("Cell : %d, %d\r\n", current_mouse_event.Cell.X, current_mouse_event.Cell.Y)
|
lp.Printf("Cell : %d, %d\r\n", current_mouse_event.Cell.X, current_mouse_event.Cell.Y)
|
||||||
lp.Printf("Type : %s\r\n", current_mouse_event.Event_type)
|
lp.Printf("Type : %s\r\n", current_mouse_event.Event_type)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const (
|
|||||||
MOUSE_RELEASE
|
MOUSE_RELEASE
|
||||||
MOUSE_MOVE
|
MOUSE_MOVE
|
||||||
MOUSE_CLICK
|
MOUSE_CLICK
|
||||||
|
MOUSE_LEAVE
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e MouseEventType) String() string {
|
func (e MouseEventType) String() string {
|
||||||
@@ -141,10 +142,13 @@ func (e PointerShape) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SHIFT_INDICATOR int = 1 << 2
|
SHIFT_INDICATOR int = 1 << 2
|
||||||
ALT_INDICATOR = 1 << 3
|
ALT_INDICATOR = 1 << 3
|
||||||
CTRL_INDICATOR = 1 << 4
|
CTRL_INDICATOR = 1 << 4
|
||||||
MOTION_INDICATOR = 1 << 5
|
MOTION_INDICATOR = 1 << 5
|
||||||
|
SCROLL_BUTTON_INDICATOR = 1 << 6
|
||||||
|
EXTRA_BUTTON_INDICATOR = 1 << 7
|
||||||
|
LEAVE_INDICATOR = 1 << 8
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -245,11 +249,14 @@ func decode_sgr_mouse(text string, screen_size ScreenSize, last_letter byte) *Mo
|
|||||||
ans.Event_type = MOUSE_MOVE
|
ans.Event_type = MOUSE_MOVE
|
||||||
}
|
}
|
||||||
cb3 := cb & 3
|
cb3 := cb & 3
|
||||||
if cb >= 128 {
|
switch {
|
||||||
|
case cb&LEAVE_INDICATOR != 0:
|
||||||
|
ans.Event_type = MOUSE_LEAVE
|
||||||
|
case cb&EXTRA_BUTTON_INDICATOR != 0:
|
||||||
ans.Buttons |= ebmap[cb3]
|
ans.Buttons |= ebmap[cb3]
|
||||||
} else if cb >= 64 {
|
case cb&SCROLL_BUTTON_INDICATOR != 0:
|
||||||
ans.Buttons |= wbmap[cb3]
|
ans.Buttons |= wbmap[cb3]
|
||||||
} else if cb3 < 3 {
|
case cb3 < 3:
|
||||||
ans.Buttons |= bmap[cb3]
|
ans.Buttons |= bmap[cb3]
|
||||||
}
|
}
|
||||||
if cb&SHIFT_INDICATOR != 0 {
|
if cb&SHIFT_INDICATOR != 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user