stacking: Wire up mouse input

This commit is contained in:
Victoria Brekenfeld 2023-06-08 21:50:25 +02:00
parent 7e3a96eb53
commit f00753071e

View file

@ -10,6 +10,7 @@ use calloop::LoopHandle;
use cosmic::{ use cosmic::{
iced::widget as iced_widget, iced::widget as iced_widget,
iced_core::{alignment, Color, Length}, iced_core::{alignment, Color, Length},
iced_runtime::Command,
iced_widget::rule::FillMode, iced_widget::rule::FillMode,
theme, widget as cosmic_widget, Element as CosmicElement, theme, widget as cosmic_widget, Element as CosmicElement,
}; };
@ -67,8 +68,9 @@ pub struct CosmicStackInternal {
activated: Arc<AtomicBool>, activated: Arc<AtomicBool>,
group_focused: Arc<AtomicBool>, group_focused: Arc<AtomicBool>,
previous_keyboard: Arc<AtomicUsize>, previous_keyboard: Arc<AtomicUsize>,
pointer_entered: Option<Arc<AtomicU8>>, pointer_entered: Arc<AtomicU8>,
previous_pointer: Arc<AtomicUsize>, previous_pointer: Arc<AtomicUsize>,
last_seat: Arc<Mutex<Option<(Seat<State>, Serial)>>>,
last_location: Arc<Mutex<Option<(Point<f64, Logical>, Serial, u32)>>>, last_location: Arc<Mutex<Option<(Point<f64, Logical>, Serial, u32)>>>,
geometry: Arc<Mutex<Option<Rectangle<i32, Logical>>>>, geometry: Arc<Mutex<Option<Rectangle<i32, Logical>>>>,
mask: Arc<Mutex<Option<tiny_skia::Mask>>>, mask: Arc<Mutex<Option<tiny_skia::Mask>>>,
@ -76,23 +78,15 @@ pub struct CosmicStackInternal {
impl CosmicStackInternal { impl CosmicStackInternal {
pub fn swap_focus(&self, focus: Focus) -> Focus { pub fn swap_focus(&self, focus: Focus) -> Focus {
if let Some(pointer_entered) = self.pointer_entered.as_ref() { unsafe {
unsafe { std::mem::transmute::<u8, Focus>(
std::mem::transmute::<u8, Focus>( self.pointer_entered.swap(focus as u8, Ordering::SeqCst),
pointer_entered.swap(focus as u8, Ordering::SeqCst), )
)
}
} else {
Focus::Window
} }
} }
pub fn current_focus(&self) -> Focus { pub fn current_focus(&self) -> Focus {
if let Some(pointer_entered) = self.pointer_entered.as_ref() { unsafe { std::mem::transmute::<u8, Focus>(self.pointer_entered.load(Ordering::SeqCst)) }
unsafe { std::mem::transmute::<u8, Focus>(pointer_entered.load(Ordering::SeqCst)) }
} else {
Focus::Window
}
} }
} }
@ -127,8 +121,9 @@ impl CosmicStack {
activated: Arc::new(AtomicBool::new(false)), activated: Arc::new(AtomicBool::new(false)),
group_focused: Arc::new(AtomicBool::new(false)), group_focused: Arc::new(AtomicBool::new(false)),
previous_keyboard: Arc::new(AtomicUsize::new(0)), previous_keyboard: Arc::new(AtomicUsize::new(0)),
pointer_entered: None, pointer_entered: Arc::new(AtomicU8::new(Focus::None as u8)),
previous_pointer: Arc::new(AtomicUsize::new(0)), previous_pointer: Arc::new(AtomicUsize::new(0)),
last_seat: Arc::new(Mutex::new(None)),
last_location: Arc::new(Mutex::new(None)), last_location: Arc::new(Mutex::new(None)),
geometry: Arc::new(Mutex::new(None)), geometry: Arc::new(Mutex::new(None)),
mask: Arc::new(Mutex::new(None)), mask: Arc::new(Mutex::new(None)),
@ -190,9 +185,18 @@ impl CosmicStack {
let result = self.0.with_program(|p| match direction { let result = self.0.with_program(|p| match direction {
FocusDirection::Left => { FocusDirection::Left => {
if !p.group_focused.load(Ordering::SeqCst) { if !p.group_focused.load(Ordering::SeqCst) {
p.active if let Ok(old) =
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| val.checked_sub(1)) p.active
.is_ok() .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| {
val.checked_sub(1)
})
{
p.previous_keyboard.store(old, Ordering::SeqCst);
p.previous_pointer.store(old, Ordering::SeqCst);
true
} else {
false
}
} else { } else {
false false
} }
@ -200,15 +204,22 @@ impl CosmicStack {
FocusDirection::Right => { FocusDirection::Right => {
if !p.group_focused.load(Ordering::SeqCst) { if !p.group_focused.load(Ordering::SeqCst) {
let max = p.windows.lock().unwrap().len(); let max = p.windows.lock().unwrap().len();
p.active if let Ok(old) =
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| { p.active
if val < max - 1 { .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| {
Some(val + 1) if val < max - 1 {
} else { Some(val + 1)
None } else {
} None
}) }
.is_ok() })
{
p.previous_keyboard.store(old, Ordering::SeqCst);
p.previous_pointer.store(old, Ordering::SeqCst);
true
} else {
false
}
} else { } else {
false false
} }
@ -312,9 +323,13 @@ impl CosmicStack {
if let Some(previous) = windows.get(previous) { if let Some(previous) = windows.get(previous) {
KeyboardTarget::leave(previous, seat, data, serial); KeyboardTarget::leave(previous, seat, data, serial);
} }
seat.get_keyboard().unwrap().with_pressed_keysyms(|syms| { KeyboardTarget::enter(
KeyboardTarget::enter(&windows[active], seat, data, syms, serial) &windows[active],
}) seat,
data,
Vec::new(), /* TODO */
serial,
)
} }
active active
}) })
@ -367,8 +382,49 @@ impl CosmicStack {
} }
} }
#[derive(Debug, Clone, Copy)]
pub enum Message {
DragStart,
Activate(usize),
Close(usize),
}
impl Program for CosmicStackInternal { impl Program for CosmicStackInternal {
type Message = (); type Message = Message;
fn update(
&mut self,
message: Self::Message,
loop_handle: &LoopHandle<'static, crate::state::Data>,
) -> Command<Self::Message> {
match message {
Message::DragStart => {
if let Some((seat, serial)) = self.last_seat.lock().unwrap().clone() {
if let Some(surface) = self.windows.lock().unwrap()
[self.active.load(Ordering::SeqCst)]
.wl_surface()
{
loop_handle.insert_idle(move |data| {
Shell::move_request(&mut data.state, &surface, &seat, serial);
});
}
}
}
Message::Activate(idx) => {
if self.windows.lock().unwrap().get(idx).is_some() {
let old = self.active.swap(idx, Ordering::SeqCst);
self.previous_keyboard.store(old, Ordering::SeqCst);
self.previous_pointer.store(old, Ordering::SeqCst);
}
}
Message::Close(idx) => {
if let Some(val) = self.windows.lock().unwrap().get(idx) {
val.close()
}
}
}
Command::none()
}
fn view(&self) -> CosmicElement<'_, Self::Message> { fn view(&self) -> CosmicElement<'_, Self::Message> {
let windows = self.windows.lock().unwrap(); let windows = self.windows.lock().unwrap();
@ -402,6 +458,8 @@ impl Program for CosmicStackInternal {
.apply(iced_widget::container) .apply(iced_widget::container)
.padding([4, 24]) .padding([4, 24])
.center_y() .center_y()
.apply(iced_widget::mouse_area)
.on_press(Message::DragStart)
.into()]; .into()];
const ACTIVE_TAB_WIDTH: i32 = 140; const ACTIVE_TAB_WIDTH: i32 = 140;
@ -471,7 +529,7 @@ impl Program for CosmicStackInternal {
.into(), .into(),
); );
let text_width = tab_width - if tab_width > 125 { 64 } else { 40 }; let text_width = tab_width - if tab_width > 125 { 72 } else { 40 };
if text_width > 0 { if text_width > 0 {
tab_elements.push( tab_elements.push(
cosmic_widget::text(title) cosmic_widget::text(title)
@ -494,6 +552,9 @@ impl Program for CosmicStackInternal {
cosmic_widget::icon("window-close-symbolic", 16) cosmic_widget::icon("window-close-symbolic", 16)
.force_svg(true) .force_svg(true)
.style(theme::Svg::Symbolic) .style(theme::Svg::Symbolic)
.apply(iced_widget::button)
.style(theme::Button::Text)
.on_press(Message::Close(i))
.apply(iced_widget::container) .apply(iced_widget::container)
.height(Length::Fill) .height(Length::Fill)
.center_y() .center_y()
@ -534,6 +595,8 @@ impl Program for CosmicStackInternal {
} else { } else {
theme::Container::Transparent theme::Container::Transparent
}) })
.apply(iced_widget::mouse_area)
.on_press(Message::Activate(i))
.into(), .into(),
) )
} }
@ -596,7 +659,12 @@ impl Program for CosmicStackInternal {
elements.push(tabs.into()); elements.push(tabs.into());
} }
elements.push(iced_widget::horizontal_space(64).into()); elements.push(
iced_widget::horizontal_space(64)
.apply(iced_widget::mouse_area)
.on_press(Message::DragStart)
.into(),
);
iced_widget::row(elements) iced_widget::row(elements)
.height(TAB_HEIGHT as u16) .height(TAB_HEIGHT as u16)
@ -861,47 +929,52 @@ impl KeyboardTarget<State> for CosmicStack {
impl PointerTarget<State> for CosmicStack { impl PointerTarget<State> for CosmicStack {
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) { fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
let mut event = event.clone();
event.location.y -= TAB_HEIGHT as f64;
if self.0.with_program(|p| { if self.0.with_program(|p| {
if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] let active_window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)];
.user_data() if let Some(sessions) = active_window.user_data().get::<ScreencopySessions>() {
.get::<ScreencopySessions>()
{
for session in &*sessions.0.borrow() { for session in &*sessions.0.borrow() {
session.cursor_enter(seat, InputType::Pointer) session.cursor_enter(seat, InputType::Pointer)
} }
} }
if event.location.y < TAB_HEIGHT as f64 { if (event.location.y - active_window.geometry().loc.y as f64) < 0. {
let focus = p.swap_focus(Focus::Header); let previous = p.swap_focus(Focus::Header);
if previous == Focus::Window {
PointerTarget::leave(active_window, seat, data, event.serial, event.time);
}
true true
} else { } else {
let focus = p.swap_focus(Focus::Window); p.swap_focus(Focus::Window);
*p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time)); *p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time));
let active = p.active.load(Ordering::SeqCst); let active = p.active.load(Ordering::SeqCst);
p.previous_pointer.store(active, Ordering::SeqCst); p.previous_pointer.store(active, Ordering::SeqCst);
PointerTarget::enter( PointerTarget::enter(active_window, seat, data, &event);
&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)],
seat,
data,
event,
);
false false
} }
}) { }) {
PointerTarget::enter(&self.0, seat, data, event) event.location.y += TAB_HEIGHT as f64;
event.location -= self.0.with_program(|p| {
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]
.geometry()
.loc
.to_f64()
});
PointerTarget::enter(&self.0, seat, data, &event)
} }
} }
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) { fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
let mut event = event.clone();
event.location.y -= TAB_HEIGHT as f64;
let active = let active =
self.pointer_leave_if_previous(seat, data, event.serial, event.time, event.location); self.pointer_leave_if_previous(seat, data, event.serial, event.time, event.location);
if let Some((previous, next)) = self.0.with_program(|p| { if let Some((previous, next)) = self.0.with_program(|p| {
if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] let active_window = &p.windows.lock().unwrap()[active];
.user_data() if let Some(sessions) = active_window.user_data().get::<ScreencopySessions>() {
.get::<ScreencopySessions>()
{
for session in &*sessions.0.borrow() { for session in &*sessions.0.borrow() {
let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale
if let Some((geo, hotspot)) = if let Some((geo, hotspot)) =
@ -912,35 +985,34 @@ impl PointerTarget<State> for CosmicStack {
} }
} }
if event.location.y < TAB_HEIGHT as f64 { if (event.location.y - active_window.geometry().loc.y as f64) < 0. {
let previous = p.swap_focus(Focus::Header); let previous = p.swap_focus(Focus::Header);
if previous == Focus::Window { if previous == Focus::Window {
PointerTarget::leave( PointerTarget::leave(active_window, seat, data, event.serial, event.time);
&p.windows.lock().unwrap()[active],
seat,
data,
event.serial,
event.time,
);
} }
Some((previous, Focus::Header)) Some((previous, Focus::Header))
} else { } else {
let mut event = event.clone(); *p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time));
event.location.y -= TAB_HEIGHT as f64;
let previous = p.swap_focus(Focus::Window); let previous = p.swap_focus(Focus::Window);
if previous != Focus::Window { if previous != Focus::Window {
PointerTarget::enter(&p.windows.lock().unwrap()[active], seat, data, &event); PointerTarget::enter(active_window, seat, data, &event);
} else { } else {
PointerTarget::motion(&p.windows.lock().unwrap()[active], seat, data, &event); PointerTarget::motion(active_window, seat, data, &event);
} }
Some((previous, Focus::Window)) Some((previous, Focus::Window))
} }
}) { }) {
event.location.y += TAB_HEIGHT as f64;
event.location -= self
.0
.with_program(|p| p.windows.lock().unwrap()[active].geometry().loc.to_f64());
match (previous, next) { match (previous, next) {
(Focus::Header, Focus::Header) => PointerTarget::motion(&self.0, seat, data, event), (Focus::Header, Focus::Header) => {
(_, Focus::Header) => PointerTarget::enter(&self.0, seat, data, event), PointerTarget::motion(&self.0, seat, data, &event)
}
(_, Focus::Header) => PointerTarget::enter(&self.0, seat, data, &event),
(Focus::Header, _) => { (Focus::Header, _) => {
PointerTarget::leave(&self.0, seat, data, event.serial, event.time) PointerTarget::leave(&self.0, seat, data, event.serial, event.time)
} }
@ -967,7 +1039,12 @@ impl PointerTarget<State> for CosmicStack {
} }
match self.0.with_program(|p| p.current_focus()) { match self.0.with_program(|p| p.current_focus()) {
Focus::Header => PointerTarget::button(&self.0, seat, data, event), Focus::Header => {
self.0.with_program(|p| {
*p.last_seat.lock().unwrap() = Some((seat.clone(), event.serial));
});
PointerTarget::button(&self.0, seat, data, event)
}
Focus::Window => { Focus::Window => {
if self.0.with_program(|p| { if self.0.with_program(|p| {
PointerTarget::button( PointerTarget::button(
@ -1035,7 +1112,6 @@ impl PointerTarget<State> for CosmicStack {
p.swap_focus(Focus::None) p.swap_focus(Focus::None)
}); });
assert!(previous != Focus::None);
match previous { match previous {
Focus::Header => PointerTarget::leave(&self.0, seat, data, serial, time), Focus::Header => PointerTarget::leave(&self.0, seat, data, serial, time),