Skip to content

Commit 13d2229

Browse files
committed
implement context menu grab on inputs
1 parent 2553810 commit 13d2229

File tree

6 files changed

+153
-58
lines changed

6 files changed

+153
-58
lines changed

src/input/mod.rs

Lines changed: 143 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::{
3131
};
3232
use calloop::{
3333
timer::{TimeoutAction, Timer},
34-
RegistrationToken,
34+
LoopHandle, RegistrationToken,
3535
};
3636
use cosmic_comp_config::{workspace::WorkspaceLayout, NumlockState};
3737
use cosmic_settings_config::shortcuts;
@@ -58,7 +58,8 @@ use smithay::{
5858
},
5959
output::Output,
6060
reexports::{
61-
input::Device as InputDevice, wayland_server::protocol::wl_shm::Format as ShmFormat,
61+
input::Device as InputDevice,
62+
wayland_server::protocol::{wl_shm::Format as ShmFormat, wl_surface::WlSurface},
6263
},
6364
utils::{Point, Rectangle, Serial, SERIAL_COUNTER},
6465
wayland::{
@@ -693,6 +694,66 @@ impl State {
693694
}
694695
}
695696

697+
fn window_menu(
698+
seat: Seat<State>,
699+
evlh: &LoopHandle<State>,
700+
surface: WlSurface,
701+
serial: Serial,
702+
) {
703+
evlh.insert_idle(move |state| {
704+
let shell = state.common.shell.write().unwrap();
705+
706+
let grab = if let Some(mapped) =
707+
shell.element_for_surface(&surface).cloned()
708+
{
709+
let position = if let Some((output, set)) =
710+
shell.workspaces.sets.iter().find(|(_, set)| {
711+
set.sticky_layer
712+
.mapped()
713+
.any(|m| m == &mapped)
714+
}) {
715+
set.sticky_layer
716+
.element_geometry(&mapped)
717+
.unwrap()
718+
.loc
719+
.to_global(output)
720+
} else if let Some(workspace) =
721+
shell.space_for(&mapped)
722+
{
723+
let Some(elem_geo) =
724+
workspace.element_geometry(&mapped)
725+
else {
726+
return;
727+
};
728+
elem_geo.loc.to_global(&workspace.output)
729+
} else {
730+
return;
731+
};
732+
let cursor = seat
733+
.get_pointer()
734+
.unwrap()
735+
.current_location()
736+
.to_i32_round();
737+
738+
shell.menu_request(
739+
&surface,
740+
&seat,
741+
serial,
742+
cursor - position.as_logical(),
743+
false,
744+
&state.common.config,
745+
&state.common.event_loop_handle,
746+
false,
747+
)
748+
} else {
749+
None
750+
};
751+
drop(shell);
752+
753+
dispatch_grab(grab, seat, serial, state);
754+
});
755+
}
756+
696757
if let Some(mouse_button) = mouse_button {
697758
match mouse_button {
698759
smithay::backend::input::MouseButton::Left => {
@@ -719,61 +780,89 @@ impl State {
719780
},
720781
);
721782
}
722-
smithay::backend::input::MouseButton::Right => {
783+
smithay::backend::input::MouseButton::Middle => {
723784
supress_button();
724-
self.common.event_loop_handle.insert_idle(
725-
move |state| {
726-
let mut shell =
727-
state.common.shell.write().unwrap();
728-
let Some(target_elem) =
729-
shell.element_for_surface(&surface)
730-
else {
731-
return;
732-
};
733-
let Some(geom) =
734-
shell.space_for(target_elem).and_then(
735-
|f| f.element_geometry(target_elem),
736-
)
737-
else {
738-
return;
739-
};
740-
let geom = geom.to_f64();
741-
let center =
742-
geom.loc + geom.size.downscale(2.0);
743-
let offset = center.to_global(&output)
744-
- global_position;
745-
let edge = match (
746-
offset.x > 0.0,
747-
offset.y > 0.0,
748-
) {
749-
(true, true) => ResizeEdge::TOP_LEFT,
750-
(false, true) => ResizeEdge::TOP_RIGHT,
751-
(true, false) => {
752-
ResizeEdge::BOTTOM_LEFT
753-
}
754-
(false, false) => {
755-
ResizeEdge::BOTTOM_RIGHT
756-
}
757-
};
758-
let res = shell.resize_request(
759-
&surface,
760-
&seat_clone,
761-
serial,
762-
edge,
763-
state
764-
.common
765-
.config
766-
.cosmic_conf
767-
.edge_snap_threshold,
768-
false,
769-
);
770-
drop(shell);
771-
dispatch_grab(
772-
res, seat_clone, serial, state,
773-
);
774-
},
785+
window_menu(
786+
seat_clone,
787+
&self.common.event_loop_handle,
788+
surface,
789+
serial,
775790
);
776791
}
792+
smithay::backend::input::MouseButton::Right => {
793+
supress_button();
794+
if seat
795+
.get_keyboard()
796+
.unwrap()
797+
.modifier_state()
798+
.shift
799+
{
800+
window_menu(
801+
seat_clone,
802+
&self.common.event_loop_handle,
803+
surface,
804+
serial,
805+
);
806+
} else {
807+
self.common.event_loop_handle.insert_idle(
808+
move |state| {
809+
let mut shell =
810+
state.common.shell.write().unwrap();
811+
let Some(target_elem) =
812+
shell.element_for_surface(&surface)
813+
else {
814+
return;
815+
};
816+
let Some(geom) = shell
817+
.space_for(target_elem)
818+
.and_then(|f| {
819+
f.element_geometry(target_elem)
820+
})
821+
else {
822+
return;
823+
};
824+
let geom = geom.to_f64();
825+
let center =
826+
geom.loc + geom.size.downscale(2.0);
827+
let offset = center.to_global(&output)
828+
- global_position;
829+
let edge = match (
830+
offset.x > 0.0,
831+
offset.y > 0.0,
832+
) {
833+
(true, true) => {
834+
ResizeEdge::TOP_LEFT
835+
}
836+
(false, true) => {
837+
ResizeEdge::TOP_RIGHT
838+
}
839+
(true, false) => {
840+
ResizeEdge::BOTTOM_LEFT
841+
}
842+
(false, false) => {
843+
ResizeEdge::BOTTOM_RIGHT
844+
}
845+
};
846+
let res = shell.resize_request(
847+
&surface,
848+
&seat_clone,
849+
serial,
850+
edge,
851+
state
852+
.common
853+
.config
854+
.cosmic_conf
855+
.edge_snap_threshold,
856+
false,
857+
);
858+
drop(shell);
859+
dispatch_grab(
860+
res, seat_clone, serial, state,
861+
);
862+
},
863+
);
864+
}
865+
}
777866
_ => {}
778867
}
779868
}
@@ -807,8 +896,6 @@ impl State {
807896
},
808897
);
809898
ptr.frame(self);
810-
} else if event.state() == ButtonState::Released {
811-
ptr.unset_grab(self, serial, event.time_msec())
812899
}
813900
}
814901
InputEvent::PointerAxis { event, .. } => {

src/shell/element/stack.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,7 @@ impl Program for CosmicStackInternal {
872872
true,
873873
&state.common.config,
874874
&state.common.event_loop_handle,
875+
true,
875876
);
876877

877878
std::mem::drop(shell);
@@ -914,6 +915,7 @@ impl Program for CosmicStackInternal {
914915
false,
915916
&state.common.config,
916917
&state.common.event_loop_handle,
918+
true,
917919
);
918920

919921
std::mem::drop(shell);

src/shell/element/window.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ impl Program for CosmicWindowInternal {
515515
false,
516516
&state.common.config,
517517
&state.common.event_loop_handle,
518+
true,
518519
);
519520

520521
std::mem::drop(shell);

src/shell/grabs/menu/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@ impl Item {
161161
}
162162
}
163163

164-
/// Menu that comes up when right-clicking an application header bar
164+
/// Menu that comes up when:
165+
/// - Right-clicking an application header bar
166+
/// - Super + Middle-clicking a window
167+
/// - Super + Shift + Right-clicking a window
165168
pub struct ContextMenu {
166169
items: Vec<Item>,
167170
selected: AtomicBool,

src/shell/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2603,10 +2603,11 @@ impl Shell {
26032603
target_stack: bool,
26042604
config: &Config,
26052605
evlh: &LoopHandle<'static, State>,
2606+
client_initiated: bool,
26062607
) -> Option<(MenuGrab, Focus)> {
26072608
let serial = serial.into();
26082609
let Some(GrabStartData::Pointer(start_data)) =
2609-
check_grab_preconditions(&seat, surface, serial, true)
2610+
check_grab_preconditions(&seat, surface, serial, client_initiated)
26102611
else {
26112612
return None;
26122613
};

src/wayland/handlers/xdg_shell/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ impl XdgShellHandler for State {
467467
false,
468468
&self.common.config,
469469
&self.common.event_loop_handle,
470+
true,
470471
);
471472
if let Some((grab, focus)) = res {
472473
std::mem::drop(shell);

0 commit comments

Comments
 (0)