@@ -700,11 +700,22 @@ impl WindowManager { |
| 700 | | 700 | |
| 701 | // Warp pointer to center of focused window (mouse follows focus) | 701 | // Warp pointer to center of focused window (mouse follows focus) |
| 702 | if warp_pointer { | 702 | if warp_pointer { |
| 703 | - if let Err(e) = self.conn.warp_pointer_to_window(window) { | 703 | + // Use stored geometry instead of querying X11 (avoids race with ConfigureWindow) |
| 704 | - tracing::warn!("Failed to warp pointer: {}", e); | 704 | + if let Some(win) = self.windows.get(&window) { |
| | 705 | + let g = &win.current_geometry; |
| | 706 | + let center_x = g.x + (g.width as i16 / 2); |
| | 707 | + let center_y = g.y + (g.height as i16 / 2); |
| | 708 | + if let Err(e) = self.conn.warp_pointer(center_x, center_y) { |
| | 709 | + tracing::warn!("Failed to warp pointer: {}", e); |
| | 710 | + } |
| | 711 | + } else { |
| | 712 | + // Fallback to querying X11 if window not in our map |
| | 713 | + if let Err(e) = self.conn.warp_pointer_to_window(window) { |
| | 714 | + tracing::warn!("Failed to warp pointer: {}", e); |
| | 715 | + } |
| 705 | } | 716 | } |
| 706 | - // Record warp time to suppress EnterNotify feedback loop | | |
| 707 | self.last_warp = std::time::Instant::now(); | 717 | self.last_warp = std::time::Instant::now(); |
| | 718 | + self.conn.flush()?; |
| 708 | } | 719 | } |
| 709 | | 720 | |
| 710 | Ok(()) | 721 | Ok(()) |
@@ -981,6 +992,11 @@ impl WindowManager { |
| 981 | border_width, | 992 | border_width, |
| 982 | )?; | 993 | )?; |
| 983 | } | 994 | } |
| | 995 | + |
| | 996 | + // Store the actual geometry for pointer warping |
| | 997 | + if let Some(win) = self.windows.get_mut(window) { |
| | 998 | + win.current_geometry = Rect::new(gapped_x, gapped_y, final_width.max(1), final_height.max(1)); |
| | 999 | + } |
| 984 | } | 1000 | } |
| 985 | | 1001 | |
| 986 | // 2. Configure floating windows and stack them above tiled | 1002 | // 2. Configure floating windows and stack them above tiled |
@@ -988,59 +1004,65 @@ impl WindowManager { |
| 988 | | 1004 | |
| 989 | for window_id in floating_ids { | 1005 | for window_id in floating_ids { |
| 990 | // Get the window's floating geometry from our state | 1006 | // Get the window's floating geometry from our state |
| 991 | - if let Some(win) = self.windows.get(&window_id) { | 1007 | + let (geom, has_frame) = match self.windows.get(&window_id) { |
| 992 | - let geom = win.floating_geometry; | 1008 | + Some(win) => (win.floating_geometry, win.frame.is_some()), |
| 993 | - let adjusted_width = geom.width.saturating_sub(2 * border_width as u16); | 1009 | + None => { |
| 994 | - let adjusted_height = geom.height.saturating_sub(2 * border_width as u16); | 1010 | + tracing::warn!("apply_layout: floating window {} not in windows map!", window_id); |
| 995 | - | 1011 | + continue; |
| 996 | - let has_frame = win.frame.is_some(); | 1012 | + } |
| 997 | - | 1013 | + }; |
| 998 | - if has_frame && titlebar_enabled { | 1014 | + |
| 999 | - // Configure frame for floating window | 1015 | + let adjusted_width = geom.width.saturating_sub(2 * border_width as u16); |
| 1000 | - let client_height = adjusted_height.saturating_sub(titlebar_height); | 1016 | + let adjusted_height = geom.height.saturating_sub(2 * border_width as u16); |
| 1001 | - self.frames.configure_frame( | 1017 | + |
| 1002 | - &self.conn.conn, | 1018 | + if has_frame && titlebar_enabled { |
| 1003 | - window_id, | 1019 | + // Configure frame for floating window |
| 1004 | - geom.x, | 1020 | + let client_height = adjusted_height.saturating_sub(titlebar_height); |
| 1005 | - geom.y, | 1021 | + self.frames.configure_frame( |
| 1006 | - adjusted_width.max(1), | 1022 | + &self.conn.conn, |
| 1007 | - client_height.max(1), | 1023 | + window_id, |
| 1008 | - titlebar_height, | 1024 | + geom.x, |
| 1009 | - border_width as u16, | 1025 | + geom.y, |
| 1010 | - )?; | 1026 | + adjusted_width.max(1), |
| 1011 | - | 1027 | + client_height.max(1), |
| 1012 | - // Raise frame to top of stack | 1028 | + titlebar_height, |
| 1013 | - if let Some(frame) = self.frames.frame_for_client(window_id) { | 1029 | + border_width as u16, |
| 1014 | - let aux = ConfigureWindowAux::new().stack_mode(StackMode::ABOVE); | 1030 | + )?; |
| 1015 | - self.conn.conn.configure_window(frame, &aux)?; | 1031 | + |
| 1016 | - } | 1032 | + // Raise frame to top of stack |
| 1017 | - | 1033 | + if let Some(frame) = self.frames.frame_for_client(window_id) { |
| 1018 | - tracing::debug!( | | |
| 1019 | - "apply_layout: FLOATING+FRAME window={} at ({}, {}) size {}x{} (raising)", | | |
| 1020 | - window_id, geom.x, geom.y, adjusted_width.max(1), adjusted_height.max(1) | | |
| 1021 | - ); | | |
| 1022 | - } else { | | |
| 1023 | - tracing::debug!( | | |
| 1024 | - "apply_layout: FLOATING window={} at ({}, {}) size {}x{} (raising)", | | |
| 1025 | - window_id, geom.x, geom.y, adjusted_width.max(1), adjusted_height.max(1) | | |
| 1026 | - ); | | |
| 1027 | - | | |
| 1028 | - // Configure geometry | | |
| 1029 | - self.conn.configure_window( | | |
| 1030 | - window_id, | | |
| 1031 | - geom.x, | | |
| 1032 | - geom.y, | | |
| 1033 | - adjusted_width.max(1), | | |
| 1034 | - adjusted_height.max(1), | | |
| 1035 | - border_width, | | |
| 1036 | - )?; | | |
| 1037 | - | | |
| 1038 | - // Raise to top of stack (each subsequent window goes above the previous) | | |
| 1039 | let aux = ConfigureWindowAux::new().stack_mode(StackMode::ABOVE); | 1034 | let aux = ConfigureWindowAux::new().stack_mode(StackMode::ABOVE); |
| 1040 | - self.conn.conn.configure_window(window_id, &aux)?; | 1035 | + self.conn.conn.configure_window(frame, &aux)?; |
| 1041 | } | 1036 | } |
| | 1037 | + |
| | 1038 | + tracing::debug!( |
| | 1039 | + "apply_layout: FLOATING+FRAME window={} at ({}, {}) size {}x{} (raising)", |
| | 1040 | + window_id, geom.x, geom.y, adjusted_width.max(1), adjusted_height.max(1) |
| | 1041 | + ); |
| 1042 | } else { | 1042 | } else { |
| 1043 | - tracing::warn!("apply_layout: floating window {} not in windows map!", window_id); | 1043 | + tracing::debug!( |
| | 1044 | + "apply_layout: FLOATING window={} at ({}, {}) size {}x{} (raising)", |
| | 1045 | + window_id, geom.x, geom.y, adjusted_width.max(1), adjusted_height.max(1) |
| | 1046 | + ); |
| | 1047 | + |
| | 1048 | + // Configure geometry |
| | 1049 | + self.conn.configure_window( |
| | 1050 | + window_id, |
| | 1051 | + geom.x, |
| | 1052 | + geom.y, |
| | 1053 | + adjusted_width.max(1), |
| | 1054 | + adjusted_height.max(1), |
| | 1055 | + border_width, |
| | 1056 | + )?; |
| | 1057 | + |
| | 1058 | + // Raise to top of stack (each subsequent window goes above the previous) |
| | 1059 | + let aux = ConfigureWindowAux::new().stack_mode(StackMode::ABOVE); |
| | 1060 | + self.conn.conn.configure_window(window_id, &aux)?; |
| | 1061 | + } |
| | 1062 | + |
| | 1063 | + // Store the actual geometry for pointer warping |
| | 1064 | + if let Some(win) = self.windows.get_mut(&window_id) { |
| | 1065 | + win.current_geometry = Rect::new(geom.x, geom.y, adjusted_width.max(1), adjusted_height.max(1)); |
| 1044 | } | 1066 | } |
| 1045 | } | 1067 | } |
| 1046 | } | 1068 | } |