← KD100

Keep OSD visible while cursor hovers over it

76dbe5643aaf3186c3134a7c3b40ee521a82145f · Claude · Feb 7, 2026 05:08

The OSD now stays visible as long as the mouse cursor is over the window.
The auto-hide timer only starts when the cursor leaves the window.

This gives users time to examine the key layout without the OSD
disappearing too quickly.

Changes:
- Added cursor_inside field to osd_state_t
- Added EnterWindowMask and LeaveWindowMask to window events
- Handle EnterNotify/LeaveNotify to track cursor presence
- Auto-hide only triggers when cursor is outside the window

https://claude.ai/code/session_01H5sr8TkRyt6U1vdkVZRRvy

+17 −3 · 2 files changed

  • src/osd.c +16 −3
  • src/osd.h +1 −0
diff --git a/src/osd.c b/src/osd.c
index b7afe16..79643c6 100644
--- a/src/osd.c
+++ b/src/osd.c
@@ -91,6 +91,7 @@ osd_state_t* osd_create(config_t* config) {
     osd->recent_head = 0;
     osd->config = config;
     osd->dragging = 0;
+    osd->cursor_inside = 0;
     osd->font = NULL;
 
     // Initialize key descriptions to NULL
@@ -163,7 +164,8 @@ int osd_init_display(osd_state_t* osd) {
     attrs.background_pixel = 0;  // Transparent background
     attrs.border_pixel = 0;
     attrs.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask |
-                       PointerMotionMask | ButtonMotionMask | StructureNotifyMask;
+                       PointerMotionMask | ButtonMotionMask | StructureNotifyMask |
+                       EnterWindowMask | LeaveWindowMask;
 
     // Create the window
     Window win = XCreateWindow(dpy, RootWindow(dpy, osd->screen),
@@ -533,8 +535,8 @@ void osd_update(osd_state_t* osd) {
     Window win = (Window)osd->window;
     long now = osd_get_time_ms();
 
-    // Auto-hide check: if auto_show is enabled, OSD is visible, and enough time passed
-    if (osd->auto_show && osd->mode != OSD_MODE_HIDDEN && osd->last_action_time_ms > 0) {
+    // Auto-hide check: if auto_show is enabled, OSD is visible, cursor is outside, and enough time passed
+    if (osd->auto_show && osd->mode != OSD_MODE_HIDDEN && !osd->cursor_inside && osd->last_action_time_ms > 0) {
         long time_since_action = now - osd->last_action_time_ms;
         if (time_since_action > osd->display_duration_ms) {
             osd_hide(osd);
@@ -595,6 +597,17 @@ void osd_update(osd_state_t* osd) {
             case ConfigureNotify:
                 // Window was moved or resized
                 break;
+
+            case EnterNotify:
+                // Cursor entered the window - don't auto-hide while hovering
+                osd->cursor_inside = 1;
+                break;
+
+            case LeaveNotify:
+                // Cursor left the window - restart auto-hide timer
+                osd->cursor_inside = 0;
+                osd->last_action_time_ms = osd_get_time_ms();
+                break;
         }
     }
 
diff --git a/src/osd.h b/src/osd.h
index 3431502..2eabcbe 100644
--- a/src/osd.h
+++ b/src/osd.h
@@ -53,6 +53,7 @@ typedef struct {
     int dragging;
     int drag_start_x;
     int drag_start_y;
+    int cursor_inside;            // Is cursor hovering over window
 
     // Configuration reference for key names
     config_t* config;