use std::time::Instant;
use smithay::input::pointer::{AxisFrame, MotionEvent};
use smithay::utils::SERIAL_COUNTER;
use crate::backend::interface::BackendView;
use crate::compositor::exit_confirm::exit_confirm_controller;
use crate::compositor::monitor::camera::camera_controller;
use crate::compositor::root::Halley;
use crate::compositor::screenshot::screenshot_controller;
use crate::input::ctx::InputCtx;
use super::context::pointer_screen_context_for_monitor;
use super::focus::pointer_focus_for_screen;
use crate::input::keyboard::bindings::{
apply_bound_pointer_input, apply_compositor_action_press, compositor_binding_action_active,
};
use halley_config::{CompositorBindingAction, WHEEL_DOWN_CODE, WHEEL_UP_CODE};
use smithay::backend::input::{Axis, AxisRelativeDirection, AxisSource};
#[inline]
fn now_millis_u32() -> u32 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| (d.as_millis() & 0xffff_ffff) as u32)
.unwrap_or(0)
}
fn axis_scroll_delta(amount_v120: Option<f64>, amount_px: Option<f64>) -> i32 {
let raw = if let Some(v120) = amount_v120 {
-(v120 / 120.0) * 48.0
} else {
-amount_px.unwrap_or(0.0)
};
if raw.abs() < 1.0 {
0
} else {
raw.round() as i32
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn handle_pointer_axis_input<B: BackendView>(
st: &mut Halley,
ctx: &InputCtx<'_, B>,
source: AxisSource,
amount_v120_horizontal: Option<f64>,
amount_v120_vertical: Option<f64>,
amount_horizontal: Option<f64>,
amount_vertical: Option<f64>,
relative_direction_horizontal: AxisRelativeDirection,
relative_direction_vertical: AxisRelativeDirection,
) {
if exit_confirm_controller(&*st).active() {
return;
}
if screenshot_controller(&mut *st).screenshot_session_active() {
return;
}
if crate::compositor::interaction::state::note_cursor_activity(st, st.now_ms(Instant::now())) {
ctx.backend.request_redraw();
}
if crate::protocol::wayland::session_lock::session_lock_active(st) {
let (sx, sy) = {
let ps = ctx.pointer_state.borrow();
(ps.screen.0, ps.screen.1)
};
let target_monitor = st.monitor_for_screen_or_interaction(sx, sy);
st.activate_monitor(target_monitor.as_str());
let context = pointer_screen_context_for_monitor(st, target_monitor, (sx, sy), true, true);
{
let mut ps = ctx.pointer_state.borrow_mut();
ps.workspace_size = (context.ws_w, context.ws_h);
ps.world = context.world;
}
if let Some(pointer) = st.platform.seat.get_pointer() {
if pointer.current_focus().is_none()
&& let Some(focus) = pointer_focus_for_screen(
st,
context.ws_w,
context.ws_h,
context.local_sx,
context.local_sy,
Instant::now(),
None,
)
{
pointer.motion(
st,
Some(focus),
&MotionEvent {
location: (context.local_sx as f64, context.local_sy as f64).into(),
serial: SERIAL_COUNTER.next_serial(),
time: now_millis_u32(),
},
);
}
if pointer.current_focus().is_some() {
let mut frame = AxisFrame::new(now_millis_u32())
.source(source)
.relative_direction(Axis::Horizontal, relative_direction_horizontal)
.relative_direction(Axis::Vertical, relative_direction_vertical);
if let Some(v120) = amount_v120_horizontal {
frame = frame.v120(Axis::Horizontal, v120.round() as i32);
}
if let Some(v120) = amount_v120_vertical {
frame = frame.v120(Axis::Vertical, v120.round() as i32);
}
let horizontal_value =
amount_horizontal.or_else(|| amount_v120_horizontal.map(|v| v / 8.0));
let vertical_value =
amount_vertical.or_else(|| amount_v120_vertical.map(|v| v / 8.0));
if let Some(v) = horizontal_value {
frame = frame.value(Axis::Horizontal, v);
}
if let Some(v) = vertical_value {
frame = frame.value(Axis::Vertical, v);
}
pointer.axis(st, frame);
pointer.frame(st);
}
}
return;
}
let (sx, sy) = {
let ps = ctx.pointer_state.borrow();
(ps.screen.0, ps.screen.1)
};
let target_monitor = st.monitor_for_screen_or_interaction(sx, sy);
st.activate_monitor(target_monitor.as_str());
let context = pointer_screen_context_for_monitor(st, target_monitor, (sx, sy), true, true);
{
let mut ps = ctx.pointer_state.borrow_mut();
ps.workspace_size = (context.ws_w, context.ws_h);
ps.world = context.world;
}
let now = Instant::now();
let now_ms = st.now_ms(now);
let mut steps = (amount_v120_vertical.unwrap_or(0.0) as f32) / 120.0;
if steps.abs() < f32::EPSILON {
let px = amount_vertical.unwrap_or(0.0) as f32;
if px.abs() > f32::EPSILON {
steps = px / 40.0;
}
}
let mut scroll_dx = axis_scroll_delta(amount_v120_horizontal, amount_horizontal);
let mut scroll_dy = -axis_scroll_delta(amount_v120_vertical, amount_vertical);
let mods = ctx.mod_state.borrow().clone();
if mods.shift_down && scroll_dx == 0 && scroll_dy != 0 {
scroll_dx = scroll_dy;
scroll_dy = 0;
}
if (scroll_dx != 0 || scroll_dy != 0)
&& crate::overlay::error_toast_hit_test(
st,
context.monitor.as_str(),
context.ws_w,
context.ws_h,
context.local_sx as f64,
context.local_sy as f64,
)
{
st.ui
.render_state
.set_overlay_error_toast_hovered(context.monitor.as_str(), true, now_ms);
let changed = crate::overlay::scroll_error_toast(
st,
context.monitor.as_str(),
context.ws_w,
context.ws_h,
scroll_dx,
scroll_dy,
);
if changed {
ctx.backend.request_redraw();
}
return;
}
if steps.abs() >= f32::EPSILON {
let steps = steps.clamp(-4.0, 4.0);
let wheel_code = if steps > 0.0 {
WHEEL_UP_CODE
} else {
WHEEL_DOWN_CODE
};
if let Some(action) = compositor_binding_action_active(st, wheel_code, &mods) {
ctx.pointer_state.borrow_mut().panning = false;
let zoom_action = matches!(
action,
CompositorBindingAction::ZoomIn | CompositorBindingAction::ZoomOut
);
if matches!(
action,
CompositorBindingAction::ZoomIn | CompositorBindingAction::ZoomOut
) {
crate::compositor::interaction::pointer::set_temporary_cursor_override_icon(
st,
if matches!(action, CompositorBindingAction::ZoomIn) {
smithay::input::pointer::CursorIcon::ZoomIn
} else {
smithay::input::pointer::CursorIcon::ZoomOut
},
Instant::now(),
220,
);
}
if apply_compositor_action_press(st, action, ctx.config_path, ctx.wayland_display) {
if zoom_action {
ctx.backend.request_output_redraw(context.monitor.as_str());
} else {
ctx.backend.request_redraw();
}
}
return;
}
if apply_bound_pointer_input(st, wheel_code, &mods, ctx.config_path, ctx.wayland_display) {
ctx.pointer_state.borrow_mut().panning = false;
ctx.backend.request_redraw();
return;
}
}
if steps.abs() >= f32::EPSILON {
let overlay = crate::overlay::OverlayView::from_halley(st);
let overflow_scrollable = overlay
.cluster_overflow_member_ids_for_monitor(context.monitor.as_str())
.len()
> 15;
let over_overflow_strip = overflow_scrollable
&& crate::overlay::cluster_overflow_strip_slot_at(
&overlay,
context.monitor.as_str(),
context.local_sx,
context.local_sy,
now_ms,
)
.is_some();
if over_overflow_strip {
let delta = if steps > 0.0 { 1 } else { -1 };
let changed =
st.adjust_cluster_overflow_scroll_for_monitor(context.monitor.as_str(), delta);
st.reveal_cluster_overflow_for_monitor(context.monitor.as_str(), now_ms);
if changed {
ctx.backend.request_redraw();
}
return;
}
}
let resize_preview = ctx.pointer_state.borrow().resize;
if let Some(pointer) = st.platform.seat.get_pointer() {
let constrained_surface_info =
crate::compositor::interaction::pointer::active_constrained_pointer_surface(st);
let locked_surface = constrained_surface_info
.as_ref()
.and_then(|(s, locked)| if *locked { Some(s.clone()) } else { None });
if pointer.current_focus().is_none() {
if let Some(mut focus) = pointer_focus_for_screen(
st,
context.ws_w,
context.ws_h,
context.local_sx,
context.local_sy,
now,
resize_preview,
) {
if let Some(constrained) =
crate::compositor::interaction::pointer::find_constrained_surface_in_hierarchy(
st, &focus.0,
)
&& constrained != focus.0
{
focus.0 = constrained;
focus.1 = pointer.current_location();
}
if locked_surface.is_none() {
let location = if crate::compositor::monitor::layer_shell::is_layer_surface_tree(
st, &focus.0,
)
|| crate::protocol::wayland::session_lock::is_session_lock_surface(
st, &focus.0,
) {
(context.local_sx as f64, context.local_sy as f64).into()
} else {
let cam_scale = st.camera_render_scale() as f64;
(
context.local_sx as f64 / cam_scale,
context.local_sy as f64 / cam_scale,
)
.into()
};
pointer.motion(
st,
Some(focus),
&MotionEvent {
location,
serial: SERIAL_COUNTER.next_serial(),
time: now_millis_u32(),
},
);
} else {
pointer.motion(
st,
Some((locked_surface.unwrap(), pointer.current_location())),
&MotionEvent {
location: pointer.current_location(),
serial: SERIAL_COUNTER.next_serial(),
time: now_millis_u32(),
},
);
}
}
}
if pointer.current_focus().is_some() {
let mut frame = AxisFrame::new(now_millis_u32())
.source(source)
.relative_direction(Axis::Horizontal, relative_direction_horizontal)
.relative_direction(Axis::Vertical, relative_direction_vertical);
if let Some(v120) = amount_v120_horizontal {
frame = frame.v120(Axis::Horizontal, v120.round() as i32);
}
if let Some(v120) = amount_v120_vertical {
frame = frame.v120(Axis::Vertical, v120.round() as i32);
}
let horizontal_value =
amount_horizontal.or_else(|| amount_v120_horizontal.map(|v| v / 8.0));
let vertical_value = amount_vertical.or_else(|| amount_v120_vertical.map(|v| v / 8.0));
if let Some(v) = horizontal_value {
frame = frame.value(Axis::Horizontal, v);
}
if let Some(v) = vertical_value {
frame = frame.value(Axis::Vertical, v);
}
if source == AxisSource::Finger {
let horizontal_stopped = amount_horizontal.unwrap_or(0.0).abs() < f64::EPSILON
&& amount_v120_horizontal.unwrap_or(0.0).abs() < f64::EPSILON;
let vertical_stopped = amount_vertical.unwrap_or(0.0).abs() < f64::EPSILON
&& amount_v120_vertical.unwrap_or(0.0).abs() < f64::EPSILON;
if horizontal_stopped {
frame = frame.stop(Axis::Horizontal);
}
if vertical_stopped {
frame = frame.stop(Axis::Vertical);
}
}
if horizontal_value.is_some()
|| vertical_value.is_some()
|| amount_v120_horizontal.is_some()
|| amount_v120_vertical.is_some()
|| frame.stop.0
|| frame.stop.1
{
pointer.axis(st, frame);
pointer.frame(st);
}
}
return;
}
if steps.abs() < f32::EPSILON {
return;
}
if camera_controller(&*st)
.pan_blocked_on_monitor(st.model.monitor_state.current_monitor.as_str())
{
return;
}
let steps = steps.clamp(-4.0, 4.0);
let camera = camera_controller(&*st).view_size();
let pan_y = -camera.y * (steps / 18.0);
{
let mut ps = ctx.pointer_state.borrow_mut();
ps.panning = false;
}
st.note_pan_activity(now);
camera_controller(&mut *st).pan_target(halley_core::field::Vec2 { x: 0.0, y: pan_y });
st.note_pan_viewport_change(now);
ctx.backend.request_redraw();
}