parent
41e865c8d3
commit
0b94ae1eb8
8 changed files with 239 additions and 65 deletions
82
Cargo.lock
generated
82
Cargo.lock
generated
|
|
@ -205,6 +205,12 @@ dependencies = [
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.12"
|
version = "0.3.12"
|
||||||
|
|
@ -236,13 +242,19 @@ dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hecs"
|
name = "hecs"
|
||||||
version = "0.10.5"
|
version = "0.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e1cbc675ee8d97b4d206a985137f8ad59666538f56f906474f554467a63c776d"
|
checksum = "e1cbc675ee8d97b4d206a985137f8ad59666538f56f906474f554467a63c776d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown",
|
"hashbrown 0.14.5",
|
||||||
"hecs-macros",
|
"hecs-macros",
|
||||||
"spin",
|
"spin",
|
||||||
]
|
]
|
||||||
|
|
@ -285,6 +297,16 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.16"
|
version = "0.4.16"
|
||||||
|
|
@ -397,6 +419,28 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a"
|
||||||
|
dependencies = [
|
||||||
|
"num_enum_derive",
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum_derive"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-crate",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_threads"
|
name = "num_threads"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
|
@ -444,6 +488,15 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-crate"
|
||||||
|
version = "3.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
|
||||||
|
dependencies = [
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.95"
|
version = "1.0.95"
|
||||||
|
|
@ -694,6 +747,23 @@ dependencies = [
|
||||||
"time-core",
|
"time-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.22.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
|
|
@ -1007,6 +1077,15 @@ version = "0.53.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wl_drm"
|
name = "wl_drm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -1069,6 +1148,7 @@ dependencies = [
|
||||||
"hecs",
|
"hecs",
|
||||||
"log",
|
"log",
|
||||||
"macros",
|
"macros",
|
||||||
|
"num_enum",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"rustix",
|
"rustix",
|
||||||
"sd-notify",
|
"sd-notify",
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ smithay-client-toolkit = { version = "0.19.1", default-features = false }
|
||||||
sd-notify = { version = "0.4.2", optional = true }
|
sd-notify = { version = "0.4.2", optional = true }
|
||||||
macros = { version = "0.1.0", path = "macros" }
|
macros = { version = "0.1.0", path = "macros" }
|
||||||
hecs = { version = "0.10.5", features = ["macros"] }
|
hecs = { version = "0.10.5", features = ["macros"] }
|
||||||
|
num_enum = "0.7.4"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|
|
||||||
|
|
@ -350,11 +350,6 @@ impl<S: X11Selection> Dispatch<WlPointer, Entity> for InnerServerState<S> {
|
||||||
_: &DisplayHandle,
|
_: &DisplayHandle,
|
||||||
_: &mut wayland_server::DataInit<'_, Self>,
|
_: &mut wayland_server::DataInit<'_, Self>,
|
||||||
) {
|
) {
|
||||||
let c_pointer = state
|
|
||||||
.world
|
|
||||||
.get::<&client::wl_pointer::WlPointer>(*entity)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match request {
|
match request {
|
||||||
Request::<WlPointer>::SetCursor {
|
Request::<WlPointer>::SetCursor {
|
||||||
serial,
|
serial,
|
||||||
|
|
@ -362,6 +357,11 @@ impl<S: X11Selection> Dispatch<WlPointer, Entity> for InnerServerState<S> {
|
||||||
hotspot_y,
|
hotspot_y,
|
||||||
surface,
|
surface,
|
||||||
} => {
|
} => {
|
||||||
|
let c_pointer = state
|
||||||
|
.world
|
||||||
|
.get::<&client::wl_pointer::WlPointer>(*entity)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let c_surface = surface.and_then(|s| {
|
let c_surface = surface.and_then(|s| {
|
||||||
let e = s.data().copied()?;
|
let e = s.data().copied()?;
|
||||||
Some(
|
Some(
|
||||||
|
|
@ -374,9 +374,11 @@ impl<S: X11Selection> Dispatch<WlPointer, Entity> for InnerServerState<S> {
|
||||||
c_pointer.set_cursor(serial, c_surface.as_deref(), hotspot_x, hotspot_y);
|
c_pointer.set_cursor(serial, c_surface.as_deref(), hotspot_x, hotspot_y);
|
||||||
}
|
}
|
||||||
Request::<WlPointer>::Release => {
|
Request::<WlPointer>::Release => {
|
||||||
c_pointer.release();
|
let (client, _) = state
|
||||||
drop(c_pointer);
|
.world
|
||||||
let _ = state.world.despawn(*entity);
|
.remove::<(client::wl_pointer::WlPointer, WlPointer)>(*entity)
|
||||||
|
.unwrap();
|
||||||
|
client.release();
|
||||||
}
|
}
|
||||||
_ => warn!("unhandled cursor request: {request:?}"),
|
_ => warn!("unhandled cursor request: {request:?}"),
|
||||||
}
|
}
|
||||||
|
|
@ -395,12 +397,11 @@ impl<S: X11Selection> Dispatch<WlKeyboard, Entity> for InnerServerState<S> {
|
||||||
) {
|
) {
|
||||||
match request {
|
match request {
|
||||||
Request::<WlKeyboard>::Release => {
|
Request::<WlKeyboard>::Release => {
|
||||||
state
|
let (client, _) = state
|
||||||
.world
|
.world
|
||||||
.get::<&client::wl_keyboard::WlKeyboard>(*entity)
|
.remove::<(client::wl_keyboard::WlKeyboard, WlKeyboard)>(*entity)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.release();
|
client.release();
|
||||||
state.world.despawn(*entity).unwrap();
|
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
@ -443,16 +444,15 @@ impl<S: X11Selection> Dispatch<WlSeat, Entity> for InnerServerState<S> {
|
||||||
) {
|
) {
|
||||||
match request {
|
match request {
|
||||||
Request::<WlSeat>::GetPointer { id } => {
|
Request::<WlSeat>::GetPointer { id } => {
|
||||||
let new_entity = state.world.reserve_entity();
|
|
||||||
let client = {
|
let client = {
|
||||||
state
|
state
|
||||||
.world
|
.world
|
||||||
.get::<&client::wl_seat::WlSeat>(*entity)
|
.get::<&client::wl_seat::WlSeat>(*entity)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get_pointer(&state.qh, new_entity)
|
.get_pointer(&state.qh, *entity)
|
||||||
};
|
};
|
||||||
let server = data_init.init(id, new_entity);
|
let server = data_init.init(id, *entity);
|
||||||
state.world.spawn_at(new_entity, (client, server));
|
state.world.insert(*entity, (client, server)).unwrap();
|
||||||
}
|
}
|
||||||
Request::<WlSeat>::GetKeyboard { id } => {
|
Request::<WlSeat>::GetKeyboard { id } => {
|
||||||
let client = {
|
let client = {
|
||||||
|
|
|
||||||
|
|
@ -435,6 +435,8 @@ impl Event for client::wl_seat::Event {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PendingEnter(client::wl_pointer::Event);
|
struct PendingEnter(client::wl_pointer::Event);
|
||||||
|
struct CurrentSurface(Entity);
|
||||||
|
pub struct LastClickSerial(pub client::wl_seat::WlSeat, pub u32);
|
||||||
|
|
||||||
impl Event for client::wl_pointer::Event {
|
impl Event for client::wl_pointer::Event {
|
||||||
fn handle<C: XConnection>(self, target: Entity, state: &mut ServerState<C>) {
|
fn handle<C: XConnection>(self, target: Entity, state: &mut ServerState<C>) {
|
||||||
|
|
@ -459,7 +461,8 @@ impl Event for client::wl_pointer::Event {
|
||||||
let mut cmd = CommandBuffer::new();
|
let mut cmd = CommandBuffer::new();
|
||||||
let pending_enter = state.world.remove_one::<PendingEnter>(target).ok();
|
let pending_enter = state.world.remove_one::<PendingEnter>(target).ok();
|
||||||
let server = state.world.get::<&WlPointer>(target).unwrap();
|
let server = state.world.get::<&WlPointer>(target).unwrap();
|
||||||
let mut query = surface.data().copied().and_then(|e| {
|
let surface_entity = surface.data().copied();
|
||||||
|
let mut query = surface_entity.and_then(|e| {
|
||||||
state
|
state
|
||||||
.world
|
.world
|
||||||
.query_one::<(&WlSurface, &SurfaceRole, &SurfaceScaleFactor, &x::Window)>(e)
|
.query_one::<(&WlSurface, &SurfaceRole, &SurfaceScaleFactor, &x::Window)>(e)
|
||||||
|
|
@ -481,6 +484,7 @@ impl Event for client::wl_pointer::Event {
|
||||||
if !surface_is_popup {
|
if !surface_is_popup {
|
||||||
state.last_hovered = Some(*window);
|
state.last_hovered = Some(*window);
|
||||||
}
|
}
|
||||||
|
cmd.insert(target, (CurrentSurface(surface_entity.unwrap()),));
|
||||||
};
|
};
|
||||||
|
|
||||||
if !surface_is_popup {
|
if !surface_is_popup {
|
||||||
|
|
@ -512,11 +516,11 @@ impl Event for client::wl_pointer::Event {
|
||||||
}
|
}
|
||||||
client::wl_pointer::Event::Leave { serial, surface } => {
|
client::wl_pointer::Event::Leave { serial, surface } => {
|
||||||
let _ = state.world.remove_one::<PendingEnter>(target);
|
let _ = state.world.remove_one::<PendingEnter>(target);
|
||||||
|
let _ = state.world.remove_one::<CurrentSurface>(target);
|
||||||
if !surface.is_alive() {
|
if !surface.is_alive() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debug!("leaving surface ({serial})");
|
debug!("leaving surface ({serial})");
|
||||||
let _ = state.world.remove_one::<PendingEnter>(target);
|
|
||||||
if let Some(surface) = surface
|
if let Some(surface) = surface
|
||||||
.data()
|
.data()
|
||||||
.copied()
|
.copied()
|
||||||
|
|
@ -584,17 +588,39 @@ impl Event for client::wl_pointer::Event {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
client::wl_pointer::Event::Button {
|
||||||
|
serial,
|
||||||
|
time,
|
||||||
|
button,
|
||||||
|
state: button_state,
|
||||||
|
} => {
|
||||||
|
let mut cmd = CommandBuffer::new();
|
||||||
|
let (server, seat, CurrentSurface(surface)) = state
|
||||||
|
.world
|
||||||
|
.query_one_mut::<(&WlPointer, &client::wl_seat::WlSeat, &CurrentSurface)>(
|
||||||
|
target,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// from linux/input-event-codes.h
|
||||||
|
mod button_codes {
|
||||||
|
pub const LEFT: u32 = 0x110;
|
||||||
|
}
|
||||||
|
|
||||||
|
if button_state == WEnum::Value(client::wl_pointer::ButtonState::Pressed)
|
||||||
|
&& button == button_codes::LEFT
|
||||||
|
{
|
||||||
|
cmd.insert(*surface, (LastClickSerial(seat.clone(), serial),));
|
||||||
|
}
|
||||||
|
|
||||||
|
server.button(serial, time, button, convert_wenum(button_state));
|
||||||
|
cmd.run_on(&mut state.world);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let server = state.world.get::<&WlPointer>(target).unwrap();
|
let server = state.world.get::<&WlPointer>(target).unwrap();
|
||||||
simple_event_shunt! {
|
simple_event_shunt! {
|
||||||
server, self => [
|
server, self => [
|
||||||
Frame,
|
Frame,
|
||||||
Button {
|
|
||||||
serial,
|
|
||||||
time,
|
|
||||||
button,
|
|
||||||
|state| convert_wenum(state)
|
|
||||||
},
|
|
||||||
Axis {
|
Axis {
|
||||||
time,
|
time,
|
||||||
|axis| convert_wenum(axis),
|
|axis| convert_wenum(axis),
|
||||||
|
|
|
||||||
|
|
@ -1091,6 +1091,31 @@ impl<S: X11Selection + 'static> InnerServerState<S> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn move_window(&mut self, window: x::Window) {
|
||||||
|
let Some(data) = self
|
||||||
|
.windows
|
||||||
|
.get(&window)
|
||||||
|
.copied()
|
||||||
|
.and_then(|e| self.world.entity(e).ok())
|
||||||
|
else {
|
||||||
|
warn!("Requested move of unknown window {window:?}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(last_click_data) = data.get::<&LastClickSerial>() else {
|
||||||
|
warn!("Requested move of window {window:?} but we don't have a click serial for it");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let role = data.get::<&SurfaceRole>();
|
||||||
|
let Some(SurfaceRole::Toplevel(Some(data))) = role.as_deref() else {
|
||||||
|
warn!("Requested move of non toplevel {window:?} ({role:?})");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
data.toplevel._move(&last_click_data.0, last_click_data.1);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn destroy_window(&mut self, window: x::Window) {
|
pub fn destroy_window(&mut self, window: x::Window) {
|
||||||
if let Some(id) = self.windows.remove(&window) {
|
if let Some(id) = self.windows.remove(&window) {
|
||||||
self.world.remove::<(x::Window, WindowData)>(id).unwrap();
|
self.world.remove::<(x::Window, WindowData)>(id).unwrap();
|
||||||
|
|
|
||||||
|
|
@ -509,6 +509,28 @@ impl XState {
|
||||||
x if x == self.atoms.active_win => {
|
x if x == self.atoms.active_win => {
|
||||||
server_state.activate_window(e.window());
|
server_state.activate_window(e.window());
|
||||||
}
|
}
|
||||||
|
x if x == self.atoms.moveresize => {
|
||||||
|
let x::ClientMessageData::Data32(data) = e.data() else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_x_root, _y_root) = (data[0], data[1]);
|
||||||
|
let Ok(direction) = MoveResizeDirection::try_from(data[2]) else {
|
||||||
|
warn!("unknown direction for _NET_WM_MOVERESIZE: {}", data[2]);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let button = data[3];
|
||||||
|
// XXX: This can technically be driven by keyboard events and other mouse buttons as well,
|
||||||
|
// but I haven't found an application that does this yet. We'll cross that bridge when we get to it.
|
||||||
|
if button != 1 {
|
||||||
|
warn!("Attempted move/resize of {:?} with non left click button ({button})", e.window());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(direction, MoveResizeDirection::Move) {
|
||||||
|
server_state.move_window(e.window());
|
||||||
|
}
|
||||||
|
}
|
||||||
t => warn!("unrecognized message: {t:?}"),
|
t => warn!("unrecognized message: {t:?}"),
|
||||||
},
|
},
|
||||||
xcb::Event::X(x::Event::MappingNotify(_)) => {}
|
xcb::Event::X(x::Event::MappingNotify(_)) => {}
|
||||||
|
|
@ -933,6 +955,7 @@ xcb::atoms_struct! {
|
||||||
xsettings => b"_XSETTINGS_S0" only_if_exists = false,
|
xsettings => b"_XSETTINGS_S0" only_if_exists = false,
|
||||||
xsettings_settings => b"_XSETTINGS_SETTINGS" only_if_exists = false,
|
xsettings_settings => b"_XSETTINGS_SETTINGS" only_if_exists = false,
|
||||||
primary => b"PRIMARY" only_if_exists = false,
|
primary => b"PRIMARY" only_if_exists = false,
|
||||||
|
moveresize => b"_NET_WM_MOVERESIZE" only_if_exists = false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1076,24 +1099,13 @@ mod motif {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, num_enum::TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
pub enum Decorations {
|
pub enum Decorations {
|
||||||
Client = 0,
|
Client = 0,
|
||||||
Server = 1,
|
Server = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u32> for Decorations {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: u32) -> Result<Self, ()> {
|
|
||||||
match value {
|
|
||||||
0 => Ok(Self::Client),
|
|
||||||
1 => Ok(Self::Server),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Decorations> for zxdg_toplevel_decoration_v1::Mode {
|
impl From<Decorations> for zxdg_toplevel_decoration_v1::Mode {
|
||||||
fn from(value: Decorations) -> Self {
|
fn from(value: Decorations) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
|
@ -1104,42 +1116,37 @@ mod motif {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, num_enum::TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
pub enum SetState {
|
pub enum SetState {
|
||||||
Remove,
|
Remove,
|
||||||
Add,
|
Add,
|
||||||
Toggle,
|
Toggle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u32> for SetState {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::TryFromPrimitive)]
|
||||||
type Error = ();
|
#[repr(u32)]
|
||||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
0 => Ok(Self::Remove),
|
|
||||||
1 => Ok(Self::Add),
|
|
||||||
2 => Ok(Self::Toggle),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum WmState {
|
pub enum WmState {
|
||||||
Withdrawn = 0,
|
Withdrawn = 0,
|
||||||
Normal = 1,
|
Normal = 1,
|
||||||
Iconic = 3,
|
Iconic = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u32> for WmState {
|
#[derive(Debug, Copy, Clone, num_enum::TryFromPrimitive, num_enum::IntoPrimitive)]
|
||||||
type Error = ();
|
#[repr(u32)]
|
||||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
pub enum MoveResizeDirection {
|
||||||
match value {
|
SizeTopLeft,
|
||||||
0 => Ok(Self::Withdrawn),
|
SizeTop,
|
||||||
1 => Ok(Self::Normal),
|
SizeTopRight,
|
||||||
3 => Ok(Self::Iconic),
|
SizeRight,
|
||||||
_ => Err(()),
|
SizeBottomRight,
|
||||||
}
|
SizeBottom,
|
||||||
}
|
SizeBottomLeft,
|
||||||
|
SizeLeft,
|
||||||
|
Move,
|
||||||
|
SizeKeyboard,
|
||||||
|
MoveKeyboard,
|
||||||
|
Cancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RealConnection {
|
pub struct RealConnection {
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ use std::time::{Duration, Instant};
|
||||||
use wayland_protocols::xdg::{
|
use wayland_protocols::xdg::{
|
||||||
decoration::zv1::server::zxdg_toplevel_decoration_v1, shell::server::xdg_toplevel,
|
decoration::zv1::server::zxdg_toplevel_decoration_v1, shell::server::xdg_toplevel,
|
||||||
};
|
};
|
||||||
use wayland_server::protocol::wl_output;
|
use wayland_server::protocol::{wl_output, wl_pointer};
|
||||||
use wayland_server::Resource;
|
use wayland_server::Resource;
|
||||||
use xcb::{x, Xid};
|
use xcb::{x, Xid};
|
||||||
use xwayland_satellite as xwls;
|
use xwayland_satellite as xwls;
|
||||||
use xwayland_satellite::xstate::{WmSizeHintsFlags, WmState};
|
use xwayland_satellite::xstate::{MoveResizeDirection, WmSizeHintsFlags, WmState};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct TestDataInner {
|
struct TestDataInner {
|
||||||
|
|
@ -329,6 +329,7 @@ xcb::atoms_struct! {
|
||||||
incr => b"INCR",
|
incr => b"INCR",
|
||||||
xsettings => b"_XSETTINGS_S0",
|
xsettings => b"_XSETTINGS_S0",
|
||||||
xsettings_setting => b"_XSETTINGS_SETTINGS",
|
xsettings_setting => b"_XSETTINGS_SETTINGS",
|
||||||
|
moveresize => b"_NET_WM_MOVERESIZE",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2024,3 +2025,31 @@ fn rotated_output() {
|
||||||
other => panic!("unexpected event {other:?}"),
|
other => panic!("unexpected event {other:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BTN_LEFT: u32 = 0x110;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn client_init_move() {
|
||||||
|
let mut f = Fixture::new();
|
||||||
|
let mut connection = Connection::new(&f.display);
|
||||||
|
|
||||||
|
let win_toplevel = connection.new_window(connection.root, 0, 0, 20, 20, false);
|
||||||
|
let surface = f.map_as_toplevel(&mut connection, win_toplevel);
|
||||||
|
f.testwl.move_pointer_to(surface, 10., 10.);
|
||||||
|
let ptr = f.testwl.pointer();
|
||||||
|
ptr.motion(10, 10.0, 10.0);
|
||||||
|
ptr.frame();
|
||||||
|
ptr.button(10, 20, BTN_LEFT, wl_pointer::ButtonState::Pressed);
|
||||||
|
ptr.frame();
|
||||||
|
f.testwl.dispatch();
|
||||||
|
|
||||||
|
connection.send_client_message(&x::ClientMessageEvent::new(
|
||||||
|
win_toplevel,
|
||||||
|
connection.atoms.moveresize,
|
||||||
|
x::ClientMessageData::Data32([0, 0, MoveResizeDirection::Move.into(), 1, 0]),
|
||||||
|
));
|
||||||
|
|
||||||
|
f.wait_and_dispatch();
|
||||||
|
let data = f.testwl.get_surface_data(surface).unwrap();
|
||||||
|
assert!(data.moving);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,7 @@ pub struct SurfaceData {
|
||||||
pub last_enter_serial: Option<u32>,
|
pub last_enter_serial: Option<u32>,
|
||||||
pub fractional: Option<WpFractionalScaleV1>,
|
pub fractional: Option<WpFractionalScaleV1>,
|
||||||
pub viewport: Option<Viewport>,
|
pub viewport: Option<Viewport>,
|
||||||
|
pub moving: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SurfaceData {
|
impl SurfaceData {
|
||||||
|
|
@ -1541,6 +1542,10 @@ impl Dispatch<XdgToplevel, SurfaceId> for State {
|
||||||
};
|
};
|
||||||
toplevel.parent = parent;
|
toplevel.parent = parent;
|
||||||
}
|
}
|
||||||
|
xdg_toplevel::Request::Move { seat: _, serial: _ } => {
|
||||||
|
let data = state.surfaces.get_mut(surface_id).unwrap();
|
||||||
|
data.moving = true;
|
||||||
|
}
|
||||||
other => todo!("unhandled request {other:?}"),
|
other => todo!("unhandled request {other:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1862,6 +1867,7 @@ impl Dispatch<WlCompositor, ()> for State {
|
||||||
last_enter_serial: None,
|
last_enter_serial: None,
|
||||||
fractional: None,
|
fractional: None,
|
||||||
viewport: None,
|
viewport: None,
|
||||||
|
moving: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
state.last_surface_id = Some(SurfaceId(id));
|
state.last_surface_id = Some(SurfaceId(id));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue